【问题标题】:How can I bind from a callback in Angular 4如何从 Angular 4 中的回调绑定
【发布时间】:2019-04-25 13:34:39
【问题描述】:

我正在尝试将我的模板绑定到通过回调从订阅返回的值。但是没有调用变化检测。

 //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>

消息没有改变,尽管它从服务中获取了一个值。我猜这个问题与Zone有关。

【问题讨论】:

  • 你能为此创建一个 stackblitz sn-p 吗?

标签: angular typescript asynchronous zone


【解决方案1】:

我不知道为什么没有发生这种情况。

但下面有一些问题。

谁将msg 设置为回调?

也许你想做callbackFromLogin(result);

但我认为您可以尝试实施另一种解决方案。

为什么你将回调传递给服务,而不是在组件的订阅中执行函数?

类似这样的:

 // 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;
     }   
   });
 }

【讨论】:

  • 对不起,为了简单起见,我从 sn-p 中删除了一些代码。在我收到错误消息后设置该消息。让味精=“....”; callbackFromLogin(msg);问题是我在服务中有更多逻辑(在我的订阅中)我不想将它移动到登录组件,我只想发送消息(msg)
  • 您不需要将逻辑移动到组件中,您可以使用 RxJS 运算符,如maptap,无论您需要什么,并使用它们在服务中应用您的逻辑。然后,在订阅中只更新组件中的消息。我认为这是最佳做法。
  • 谢谢我更改了我的代码..但我仍然得到相同的结果..消息已传递,但无法在绑定中更改 ({{}}) 我将发布一个新的现在sn-p。
【解决方案2】:

我使用了您的建议并将我的代码更改为:

//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 )
      }   
    });
  }
&lt;div&gt;{{message}}&lt;/div&gt;

同样的问题,消息被传递给结果,当我调用 alert(this.message) 函数时,消息如上所示..但是模板插值没有变化。

【讨论】:

  • 我想这可能是我的应用程序中的一些问题..因为我知道这样的代码应该触发更改...这种奇怪行为的原因可能是什么。
  • 你从哪里得到 httpPost() 方法? Angular 的 HttpClient 没有 httpPost() 方法。
【解决方案3】:

是的..这是一个 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);
  });
 }
}

从app.component调用handleAuthentication()函数..

 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;
      } 
    }); 
 }

ApiService 的 httpPost 函数包装了 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() || [];
}

【讨论】:

  • 你的代码中有很多抽象,很混乱。在构造函数中,http 属性是 ApiService 的实例,而 rHttp 是旧的 Angular Http。我将使用 Angular 的 HttpClient 以任何简单直接的方式进行 http 调用来编辑我的答案,希望对您有所帮助。
  • 我删除了 ApiService,现在使用来自 '@angular/http' 的 http,对于这个 post 方法..没有帮助..不幸的是。
  • 您是否在控制台中看到任何错误?并且不要使用@angular/http。这已被 '@angular/common/http' 中的 HttpClient 取代
  • 不,没有任何错误。关于 HttpClient ,我知道,但谢谢。
  • 我没有看到任何超出 Angular 区域的代码。如果在构造函数中初始化消息属性,是否可以在 UI 中看到消息? UI 和组件类之间可能存在脱节。
【解决方案4】:

试试这个,然后告诉我。

  1. 创建一个属性loginRequest: Observable&lt;any&gt;;
  2. onSubmit():

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

在您的模板中,

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

这是一个简单的 Http 调用。

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

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

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

【讨论】:

  • 你从哪里得到 httpPost() 函数?那不是来自 Angular HttpClient。您的代码可能会超出该方法的角度区域。
  • 你能把你的 authService 类和注入一起发布吗?
  • 这篇文章转到 NodeJs Api 方面..使用 ngZone.run .... 也没有帮助..
  • 这没有用,维克多。我想在 AuthService 类中查看您的依赖注入。你能发布你的 AuthService 构造函数吗?
【解决方案5】:

我认为这是您建议的区域相关问题..在这种情况下:

 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)
      }   
    });
  }

当 onSubmit() 触发时,我看到“外部消息”绑定,但进入订阅后它消失了,“内部消息”根本不显示。

【讨论】:

    【解决方案6】:

    我发现了问题。 在我的 AuthService 中,在 login() 函数中,它返回一个 Observable,我正在调用 this.routeAfterLogin(false) 。如果发生错误,此功能会将我的路由重定向到登录页面。可观察对象内的重定向会扰乱更改检测。 实际上我不需要它,删除它后一切都开始正常工作。 谢谢大家的帮助。

     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");
      }
    }

    【讨论】:

      猜你喜欢
      • 2018-07-30
      • 2019-02-14
      • 1970-01-01
      • 2018-05-26
      • 2018-06-07
      • 2018-01-05
      • 1970-01-01
      • 2018-11-11
      • 2017-10-28
      相关资源
      最近更新 更多