如何从 Angular 4 中的回调绑定
Posted
技术标签:
【中文标题】如何从 Angular 4 中的回调绑定【英文标题】:How can I bind from a callback in 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 吗? 【参考方案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 运算符,如map
、tap
,无论您需要什么,并使用它们在服务中应用您的逻辑。然后,在订阅中只更新组件中的消息。我认为这是最佳做法。
谢谢我更改了我的代码..但我仍然得到相同的结果..消息已传递,但无法在绑定中更改 () 我将发布一个新的现在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 )
);
<div>message</div>
同样的问题,消息被传递给结果,当我调用 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】:试试这个,然后告诉我。
-
创建一个属性
loginRequest: Observable<any>;
内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");
【讨论】:
以上是关于如何从 Angular 4 中的回调绑定的主要内容,如果未能解决你的问题,请参考以下文章
如何解决 NullInjectorError: No provider for HttpClient! Ionic 4(Angular 8)中的问题
如何将组件中的 Angular2 方法作为第三方库的回调方法传递?