Angular & RXJS - [ts] Property 'map' does not exist on type 'Observable<User>' - rxjs

Since creating a new angular 6 project, some previous code that I've copied over doesn't seem to be working. This primarily seems to be rxjs syntax
On the .map, it displays the error:
[ts] Property 'map' does not exist on type 'Observable'<User>'.
I seem to be getting a similar error on another file with .take
Is anyone able to point me in the right direction to resolve this please?
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '#angular/router';
import { Observable } from 'rxjs';
import { AngularFireAuth } from 'angularfire2/auth';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/do';
#Injectable()
export class LoginGuardService implements CanActivate {
constructor(
private router: Router,
private auth: AngularFireAuth
) { }
canActivate(): Observable<boolean> {
return this.auth.authState.map(authState => {
if (authState) this.router.navigate(['/folders']);
return !authState;
}).take(1);
}
}
Second Guard
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):
Observable<boolean> {
this.authGuardStateURL = state.url;
return this.auth.authState.pipe(
take(1)
.map(authState => !!authState)
.do(auth => !auth ? this.router.navigate(['/login']) : true)
)
}

I reckon you used Angular CLI to create your app. Angular 6 comes with RxJS 6 and since v5, RxJS has been using pipeable operators.
So your code should look like this:
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '#angular/router';
import { Observable } from 'rxjs';
import { AngularFireAuth } from 'angularfire2/auth';
import { map, take, tap } from 'rxjs/operators';
#Injectable()
export class LoginGuardService implements CanActivate {
constructor(
private router: Router,
private auth: AngularFireAuth
) { }
canActivate(): Observable<boolean> {
return this.auth.authState.pipe(
map(authState => {
if (authState) this.router.navigate(['/folders']);
return !authState;
}),
take(1)
)
}
//Second Guard
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot): Observable<boolean> {
this.authGuardStateURL = state.url;
return this.auth.authState.pipe(
take(1),
map(authState => !!authState),
tap(auth => !auth ? this.router.navigate(['/login']) : true)
)
}
}
Notice how you import the operators now and how you put map and take inside pipe method.

Related

Angular 6: Property 'of' does not exist on type 'typeof Observable'

I am using Angular 6
using "rxjs": "^6.0.0",
ERROR : Property 'of' does not exist on type 'typeof Observable'.
import { Injectable } from '#angular/core';
import { TranslateLoader } from '#ngx-translate/core';
import { Observable, Subject, pipe, of } from 'rxjs';
#Injectable()
export class MiTranslateLoaderService implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return Observable.of({
lbl_select: 'Select',
});
}
}
Since RxJS 6 the correct and recommended way of using of() (RxJS 5 in Observable.of()) is this:
import { of } from 'rxjs';
I think this import { of } from 'rxjs/observable/of'; will work only while you have rxjs-compat package installed.
There are some updates in rxjs: ( Its rxjs6)
import { of } from 'rxjs';
It will work only when your app has rxjs-compat package installed
You can import of from rxjs:
import { Observable,of } from 'rxjs';
And simply return of()
return of({
lbl_select: 'Select',
});
So your code will be:
import { Injectable } from '#angular/core';
import { TranslateLoader } from '#ngx-translate/core';
import { Observable, of } from 'rxjs';
#Injectable()
export class MiTranslateLoaderService implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return of({
lbl_select: 'Select',
});
}
}
This is working for me.
Angular CLI 6.0.8
RxJS 6.2.2
import {of} from 'rxjs/index';
this.dataService.currentData
.pipe(takeUntil(this.destroy$))
.pipe(switchMap((myData:MyDataType) =>
of(this.anotherService.get(myData._id))))
.pipe(map((response) => {
if(response instanceof Error) {
console.log('error:');
console.dir(response);
}
return response;
}))
.subscribe((data:any) => {
doStuff(data);
},
response => {
console.log('response error');
console.log(response)
},
() => {
console.log('response complete.');
});
With the release of version 6, RxJS changed its internal package structure
https://www.academind.com/learn/javascript/rxjs-6-what-changed/#import-statement-update-path
import 'rxjs/add/observable/of';
// or
import { of } from 'rxjs/observable/of';
You need to import of from rxjs/observable/of
import { of } from "rxjs/observable/of";
Usage:
return of({
lbl_select: 'Select',
});
Update: for rxjs version 6 without rxjs-compat, you need to import of from rxjs itself as mentioned by #martin.
import { of } from 'rxjs';
Migration guide to rxjs6
The solution is to return of(..) directly :
getTranslation(lang: string): Observable<any> {
return of({
lbl_select: 'Select',
});

Passing data between more than one component using single model and presist it?

I have this service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DataService<T> {
private subject: BehaviorSubject<Partial<T>> = new BehaviorSubject<Partial<T>>(null);
changeData(message: Partial<T>) {
this.subject.next(message);
}
clearData() {
this.subject.next(null);
}
getData(): Observable<Partial<T>> {
return this.subject.asObservable();
}
}
I'm using it like this.
Datepicker:
this.ds.changeData({
dateFrom: this.dateRange.start,
dateTo: this.dateRange.end
});
Main Component:
this.ds.getData().subscribe((data: FilterQuery) => {
console.log('Update data', data);
this.filterModel = data;
});
I would like to add few more components what is the best way to presist the data and build full query object? If you have any other suggestions, I will gladly appreciate it.

Response isn't working in Angular 4

I have a service to connect to my backend. But I have this problem:
The error:
ERROR in src/app/login/sevices/login.service.ts(18,14): error TS2345: Argument of type '(res: Response) => Promise' is not assignable to parameter of type '(value: Response, index: number) => Promise'.
Types of parameters 'res' and 'value' are incompatible.
Type 'Response' is not assignable to type 'Response'. Two different types with this name exist, but they are unrelated.
Property 'body' is missing in type 'Response'.
login.service.ts:
import {Injectable} from "#angular/core";
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Login } from'../data-login/models/login.model';
import 'rxjs/add/operator/catch';
#Injectable()
export class LoginService{
private url = 'http://localhost:8080/login';
constructor(private http: Http){}
loginQuery(login: Login){
return this.http.post(this.url,JSON.stringify(login))
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
My Component:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { LoginService } from '../sevices/login.service';
import { Login } from './models/login.model';
import {NgForm} from "#angular/forms";
import {AuthService} from "../../auth.service";
#Component({
selector: 'data-login-component',
templateUrl: './data-login.component.html',
styleUrls: ['./data-login.component.css']
})
export class DataLoginComponent implements OnInit {
cssClass: string;
login: Boolean = false;
constructor(private loginService: LoginService, private router: Router, private authService: AuthService) { }
ngOnInit() {
}
verifyLogin(change: Boolean){
if(change){
console.log('OK');
this.authService.login();
this.router.navigate(['home-aluno']);
}else{
console.log('ERROR');
}
}
onSingin(form: NgForm){
if( (form.value.code !== '') && (form.value.password !== '')){
this.loginService.loginQuery(new Login(form.value.code, form.value.password))
.subscribe(
result => this.verifyLogin(result)
);
}
}
}
My backend working fine. Where is my problem?

componentDidUpdate does not fire

So I've been struggling to figure out the react-redux ecosystem for a while now. I'm almost there but there is still something that keep giving is me issues, and that's the componentDidUpdate method. When I dispatch an async action, the store is reducer is called correctly and the component's state does update.
But for some reason, the componentDidUpdate method does not fire, there is no re-render, and I cannot access the updated props. I can see it change in devtools, if I console.log(this.props.blogStore). At first it shows as an empty object but when on click it opens and shows the updated state.
I've tried as many life cycle methods as I can but nothing seems to work, including componentWillReceiveProps.
Any idea what I'm doing wrong?
Here is the code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import Datastore from 'Datastore';
const store = Datastore()
store.subscribe(() => console.log("state changed", store.getState()))
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
Datastore.js
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import Mainmenu from 'reducers/Mainmenu';
import Blogstore from 'reducers/Blogstore';
const reducer = combineReducers({
Mainmenu,
Blogstore,
})
export default function Datastore() {
const store = createStore(
reducer,
applyMiddleware(thunk)
)
return store
}
reducer
import Article from 'lib/Article';
import { ARTICLE_LOAD, ARTICLE_UPDATE, SAVE_ARTICLE_LIST } from 'actionTypes';
const initialBlogState = {
}
const Blogstore = (state=initialBlogState, action) => {
switch(action.type) {
case SAVE_ARTICLE_LIST:
state.init = true
state.articles = action.payload
return state
case ARTICLE_LOAD:
return state
case ARTICLE_UPDATE:
return state
}
return state
}
export default Blogstore;
blog-actions.js
import { ARTICLE_LOAD, ARTICLE_UPDATE, SAVE_ARTICLE_LIST } from 'actionTypes';
import APIFetch from '../lib/Fetch';
export function getArticlePids() {
return dispatch => {
APIFetch().get("/blog/list").then(response => {
dispatch({
type: SAVE_ARTICLE_LIST,
payload: response.data
})
})
}
}
component
import React from 'react';
import { connect } from 'react-redux';
import * as blogActions from '../actions/blog-actions';
#connect(state => ({
blogStore: state.Blogstore
}))
export default class Blog extends React.Component {
constructor() {
super()
}
componentDidMount() {
this.props.dispatch(blogActions.getArticlePids())
}
componentDidUpdate(prevProps) {
console.log("update", prevProps)
}
render() {
console.log("render", this.props.blogStore)
return (
<div><h1>Blog</h1></div>
)
}
}
That is pretty much it. I won't bother pasting the App and Router that are between index.js and the component because there is nothing of interest there. Just a basic react router and components that have nothing to do with this.
You need to return a new object from your reducer, like this:
import Article from 'lib/Article';
import { ARTICLE_LOAD, ARTICLE_UPDATE, SAVE_ARTICLE_LIST } from 'actionTypes';
const initialBlogState = {
}
const Blogstore = (state=initialBlogState, action) => {
switch(action.type) {
case SAVE_ARTICLE_LIST:
return Object.assign({}, state, {
init: true,
articles: action.payload,
})
case ARTICLE_LOAD:
return state
case ARTICLE_UPDATE:
return state
}
return state
}
export default Blogstore;
Otherwise, if you try to update your state directly (as you are doing currently) it will only mutate the internal reference of the state and react components won't be able to detect the change and wont re-render. Read more here.

NativeScript handling back button event

I am trying to handle the hardware back button in a NativeScript app. I am using NativeScript version 2.3.0 with Angular.
Here is what I have in main.ts file
// this import should be first in order to load some required settings (like globals and reflect-metadata)
import { platformNativeScriptDynamic, NativeScriptModule } from "nativescript-angular/platform";
import { NgModule,Component,enableProdMode } from "#angular/core";
import { AppComponent } from "./app.component";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { routes, navigatableComponents } from "./app.routing";
import { secondComponent } from "./second.component";
import {AndroidApplication} from "application";
#Component({
selector: 'page-navigation-test',
template: `<page-router-outlet></page-router-outlet>`
})
export class PageNavigationApp {
}
#NgModule({
declarations: [AppComponent,PageNavigationApp,secondComponent
// ...navigatableComponents
],
bootstrap: [PageNavigationApp],
providers:[AndroidApplication],
imports: [NativeScriptModule,
NativeScriptRouterModule,
NativeScriptRouterModule.forRoot(routes)
],
})
class AppComponentModule {
constructor(private androidapplication:AndroidApplication){
this.androidapplication.on("activityBackPressed",()=>{
console.log("back pressed");
})
}
}
enableProdMode();
platformNativeScriptDynamic().bootstrapModule(AppComponentModule);
I am importing application with
import {AndroidApplication} from "application";
Then in the constrouctor of appComponentModule I am registering the event for activityBackPressed and just doing a console.log.
This does not work.
What am I missing here?
I'm using NativeScript with Angular as well and this seems to work quite nicely for me:
import { RouterExtensions } from "nativescript-angular";
import * as application from "tns-core-modules/application";
import { AndroidApplication, AndroidActivityBackPressedEventData } from "tns-core-modules/application";
export class HomeComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {
if (application.android) {
application.android.on(AndroidApplication.activityBackPressedEvent, (data: AndroidActivityBackPressedEventData) => {
if (this.router.isActive("/articles", false)) {
data.cancel = true; // prevents default back button behavior
this.logout();
}
});
}
}
}
Note that hooking into the backPressedEvent is a global thingy so you'll need to check the page you're on and act accordingly, per the example above.
import { Component, OnInit } from "#angular/core";
import * as Toast from 'nativescript-toast';
import { Router } from "#angular/router";
import * as application from 'application';
#Component({
moduleId: module.id,
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent {
tries: number = 0;
constructor(
private router: Router
) {
if (application.android) {
application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
if (this.router.url == '/main') {
args.cancel = (this.tries++ > 0) ? false : true;
if (args.cancel) Toast.makeText("Press again to exit", "long").show();
setTimeout(() => {
this.tries = 0;
}, 2000);
}
});
}
}
}
Normally you should have an android activity and declare the backpress function on that activity. Using AndroidApplication only is not enough. Try this code:
import {topmost} from "ui/frame";
import {AndroidApplication} from "application";
let activity = AndroidApplication.startActivity ||
AndroidApplication.foregroundActivity ||
topmost().android.currentActivity ||
topmost().android.activity;
activity.onBackPressed = function() {
// Your implementation
}
You can also take a look at this snippet for example
As far as I know, NativeScript has a built-in support for this but it's not documented at all.
Using onBackPressed callback, you can handle back button behaviour for View components (e.g. Frame, Page, BottomNavigation).
Example:
function pageLoaded(args) {
var page = args.object;
page.onBackPressed = function () {
console.log("Returning true will block back button default behaviour.");
return true;
};
page.bindingContext = homeViewModel;
}
exports.pageLoaded = pageLoaded;
What's tricky here is to find out which view handles back button press in your app. In my case, I used a TabView that contained pages but the TabView itself handled the event instead of current page.

Resources