简体   繁体   中英

How can I bind from a callback in Angular 4

I am trying to bind my template to the value which was returned from subscription via callback. But there is no change detection invoked.

  //authorisation service public login(data,callbackFromLogin : (msg) => void): void { this.http.httpPost(ApiRoutes.LoginRoute,data).subscribe(result => { callbackFromLogin(msg); }); } //and then in login component onSubmit(request) { this.authService.login(request,(message) => { alert(NgZone.isInAngularZone()); if(message) { this.ngZone.run( () => { this.message = message; alert(NgZone.isInAngularZone()); }); } }); } 
 <div>{{message}}</div> 

The message does not change, though It gets a value from a service. I guess this issue is related to Zone.

I don't know exactly why this not happen.

But some question below.

Who is set msg to the callback?

Maybe you want to do callbackFromLogin(result); ?

But I think you could try to implement another solution.

Why you are pass a callback to service and not do the function inside the subscription on the component?

Something like this:

 // authorisation service
 public login(data) {
  return this.http.httpPost(ApiRoutes.LoginRoute, data);
  // if you want manipulate the message you could use `pipe` method here
 }

 // and then in login component 
 onSubmit(request) {
   this.authService.login().subscribe((message) => {
     if(message){
       this.message = message;
     }   
   });
 }

I used your advise and change my code to this:

 //Service public login(data): Observable<any> { return this.http.httpPost(ApiRoutes.LoginRoute,data).pipe(map(result=>{ if (result && result.status == 200) { /// }else{ let msg = `my message`; return {msg:msg}; } })); } //login component onSubmit(request){ this.authService.login(request).subscribe((result) => { if(result.msg){ this.message = result.msg; alert( this.message ) } }); } 
 <div>{{message}}</div> 

Same issue , the message is passed to result ,when I call to alert( this.message ) function , the message is shown above..but there is no change in a template Interpolation however.

Try this and let me know.

  1. Make a property loginRequest: Observable<any>;
  2. Inside onSubmit() :

onSubmit(request){ this.loginRequest = this.authService.login(request); }

In your template,

<div> 
  {{ (loginRequest | async)?.msg }}
</div>

Here's a simple Http call.

import {HttpClient} from '@angular/common/http';

class SomeService {
  constructor(private _http: HttpClient){}

  postSomething() {
    return this._http.post('url', toPost);
  }
}

Yes..here is an AuthService:

 import { SessionService } from '../session/session.service'; import { environment } from './../../../environments/environment'; import { IUser, UserType } from '../../../../../contracts/IUser'; import { IEmployee } from '../../../../../contracts/IEmployee'; import { Injectable,Inject } from '@angular/core'; import { Router } from '@angular/router'; import 'rxjs/add/operator/filter'; import * as auth0 from 'auth0-js'; import { Http } from '@angular/http'; import {ReplaySubject, Observable} from 'rxjs/Rx'; import { ApiService } from '../api/api.service'; import { ActivityService } from '../activity/activity.service'; import { UserActivityAction } from '../../../../../contracts/Library'; import { ApiRoutes } from '../../../../../contracts/ApiRoutes'; import { AlertService} from '../alert/alert.service'; import { MessageService} from '../message/message.service'; import {Events} from '../../../../../contracts/Library'; import { map } from 'rxjs/operators'; @Injectable() export class AuthService { private userIsLoggedIn : boolean; constructor(public http: ApiService,private rHttp: Http, private session:SessionService, @Inject('BASE_URL') public baseUrl: string, public router: Router, private alertService: AlertService, private activity: ActivityService, private messageService : MessageService) { this.session = session; } // logIn attempts to log in a user handleAuthentication() : void { let auth = this.http.httpGetAll(ApiRoutes.AuthRoute); auth.subscribe( data => { let results = data.json(); switch(results.status) { case 401: { this.routeAfterLogin(false); this.messageService.sendMessage('',Events.UserLogout); break; } case 402: { break; } case 200: { //when success break; } default: { this.routeAfterLogin(false); break; } } }, error => { console.log(error); }); this.router.navigate(['/home']); } public login(data): Observable<any> { return this.http.httpPost(ApiRoutes.LoginRoute,data).pipe(map(result=>{ if (result && result.status == 200) { //when success }else{ let msg = "some error message"; this.routeAfterLogin(false); return {msg:msg}; } })); } private routeAfterLogin(state:boolean) { this.userIsLoggedIn = state; if(this.userIsLoggedIn){ this.router.navigate(['/home']); console.log("Login"); } else { this.router.navigate(['/login']); console.log("Failure Login"); } } public logout(): void { let auth = this.http.httpGetAll(ApiRoutes.LogoutRoute); auth.subscribe( data => { console.log("Logout"); this.messageService.sendMessage('',Events.UserLogout); this.router.navigate(['/login']); }, error => { console.log(error); }); } public isAuthenticated(): boolean { return this.userIsLoggedIn; } public fillFileDownloadPass(){ let getUploadPath$ = this.http.httpGetAll(ApiRoutes.DownloaPathdRout); getUploadPath$.subscribe( data => { let results = data.json(); switch(results.status) { case 200: { this.session.download101AtchFilePath = results.filehDownloadPat; this.session.approvedFormsPath = results.approvedFormsPath; break; } case 400: { console.log("didn't find an upload path"); break; } default: { break; } } }, error => { console.log(error); }); } } 

The handleAuthentication() function is called from the app.component..

  ngOnInit() { this.authService.handleAuthentication(); this.subscription = this.messageService.getMessage().subscribe(message => { switch (message.type) { case Events.UserLogin: this.showNav = true; break; case Events.UserLogout: this.showNav = false; break; case Events.FirstEntrance : //some message break; } }); } 

The ApiService's httpPost function wraps the http:

  httpPost(url:string,data:any):Observable<Response | any> { return this._http.post(`${this.baseUrl}${url}`, data ) .map(this.parseData) .catch(this.handleError.bind(this)); } private parseData(res: Response) { return res.json() || []; } 

I think it is a Zone related issue as you suggested..in that case:

  onSubmit(request){ this.message="outer message" this.loginRequest = this.authService.login(request); this.loginRequest.subscribe((result) => { if(result.msg){ this.message ="inner message" // alert(this.message) } }); } 

When onSubmit() fires , I see the "outer message" binding , but after entrance to subscription it disappears and the "inner message" is not shown at all.

I found the problem. In my AuthService, in login() function, which returns an Observable, I am making a call for a this.routeAfterLogin(false) . This function redirects my route to login page in case the error occurs. That redirection inside an observable messes the change detection. Actually I don't need it ,after removing it everything started to work fine. Thanks all for the help.

  public login(data): Observable<any> { return this.rHttp.post(ApiRoutes.LoginRoute,data).pipe(map(result=>{ if(...){ // not relevant }else{ let msg = "error message"; this.alertService.error(msg); **this.routeAfterLogin(false);** this.messageService.sendMessage('',Events.UserLogout); return {msg:msg}; } })); } **private routeAfterLogin(state:boolean)** { this.userIsLoggedIn = state; if(this.userIsLoggedIn){ this.router.navigate(['/home']); console.log("Login"); } else { this.router.navigate(['/login']); console.log("Failure Login"); } } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM