为什么在订阅回调中更改组件的属性时,角色4组件的视图不会重新渲染?
问题描述:
我已经使用RxJS Observer功能在角度4中编写了认证系统。查询Oracle数据库是基于查询流在服务中转换为observer/observable,然后在登录组件中使用登录形式,我订阅服务中的obervable以获得身份验证结果。服务的方法返回的可观察对象在用户进行身份验证时发布“true”或“false”。通过单击登录表单组件中的登录按钮,将显示加载动画并隐藏登录表单。当用户通过身份验证时,路由器会将用户重定向到功能页面。当用户未通过身份验证时,加载动画将被隐藏,并再次显示登录表单。但这并不如预期的那样。在控制台I看到输出该用户没有登录,并在加载动画仍显示的同时,形式是隐藏的,尽管=假和showLoginForm =真为什么在订阅回调中更改组件的属性时,角色4组件的视图不会重新渲染?
login.service.ts组件显示showLoading的性质
import { Injectable } from '@angular/core';
import { DbSelectService } from '../db/db-select.service';
import * as md5 from 'js-md5';
import { AuthModel, CurrentUserModel } from '../../data-models/data-models.interfaces';
import { DbSelectQueryParams } from '../../data-models/data-models.interfaces';
import { CurrentUserDbRow } from '../../data-models/db-responses.interfaces';
import { SessionService } from '../core/session.service';
import { CacheService } from '../feature/cache.service';
import { LoggerService } from '../core/logger.service';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
@Injectable()
export class LoginService {
private currentAuthData: AuthModel;
private currentUser: CurrentUserModel;
constructor (
private dbSelect: DbSelectService,
private sessionService: SessionService,
private cacheService: CacheService,
private logger: LoggerService
) {
this.currentAuthData = this.cacheService.getCurrentAuthDataCache();
this.currentUser = this.cacheService.getCurrentUser();
}
checkAccessRights(): Observable<boolean> {
return new Observable((observer: Observer<boolean>) => {
const queryParams: DbSelectQueryParams = {
fields: [
`NAME`,
`ROLE`
],
tables: [
`SINS.P_USER`
],
conditions: [
`UPPER(P_USER.NAME) = UPPER(:username)`,
`AND`,
`PSWD = :password`,
`AND`,
`HASH = :hash`,
`AND`,
`LOCKED <> '1'`,
`AND`,
`ROWNUM = 1`
]
};
const bindParams = {
username: this.currentAuthData.username,
password: this.currentAuthData.password,
hash: md5(this.currentAuthData.password)
};
let userIsAuthenticated = false;
const AUTHENTICATED = true;
const NOT_AUTHENTICATED = false;
this.dbSelect.select<CurrentUserDbRow>(queryParams, bindParams).subscribe(
user => {
console.warn(user);
this.currentUser.username = user.NAME.toUpperCase();
this.currentUser.role = user.ROLE.toLowerCase();
this.sessionService.getAccessToUser();
userIsAuthenticated = true;
observer.next(AUTHENTICATED);
observer.complete();
},
err => {
observer.error(err);
this.logger.writeError(err);
this.sessionService.closeSession();
},
() => {
observer.next(NOT_AUTHENTICATED);
observer.complete();
if (userIsAuthenticated === NOT_AUTHENTICATED) {
this.logger.writeNewError('logon denied');
this.sessionService.closeSession();
}
}
);
});
}
}
login.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { AppConfig } from '../services/core/app-config.service';
import { remote } from 'electron';
import { LoginService } from '../services/auth/login.service';
import { AuthModel } from '../data-models/data-models.interfaces';
import { ToastService } from '../services/core/toast.service';
import { CacheService } from '../services/feature/cache.service';
import { AppConfigParamsModel, MaxLengthAndValueModel } from '../data-models/data-models.interfaces';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'login',
styleUrls: ['app/login/login.component.css'],
templateUrl: 'app/login/login.component.html'
})
export class LoginComponent implements OnInit, OnDestroy {
private pagename = 'Логин';
private showLoading = false;
private showLoginForm = true;
private loginSubscription: Subscription;
private authmodel: AuthModel;
private maxlength: MaxLengthAndValueModel = {
username: 32,
password: 32
};
constructor (
private titleService: Title,
private config: AppConfig,
private loginService: LoginService,
private toastService: ToastService,
private cacheService: CacheService
) {
this.authmodel = this.cacheService.getCurrentAuthDataCache();
}
ngOnInit() {
this.config.getParams().then((params: AppConfigParamsModel) => {
this.titleService.setTitle(`${params.appname} - ${this.pagename}`);
});
}
ngOnDestroy() {
if (this.loginSubscription) {
this.loginSubscription.unsubscribe();
}
}
private onLogin(): void {
this.showLoadingAnimation();
this.loginSubscription = this.loginService.checkAccessRights().subscribe(
accessAllowed => {
if (accessAllowed) {
this.toastService.showAdviceToast('Добро пожаловать!');
} else {
this.hideLoadingAnimation();
this.toastService.showErrorToastWithAdvice('ВХОД ЗАПРЕЩЁН', 'Обратитесь к администратору');
}
},
err => {
this.hideLoadingAnimation();
this.toastService.showErrorToastWithAdvice('Не удалось войти', 'Попробуйте ещё раз');
}
);
}
private closeWindow(): void {
remote.getCurrentWindow().close();
}
private showLoadingAnimation(): void {
this.showLoading = true;
this.showLoginForm = false;
console.warn(this);
}
private hideLoadingAnimation(): void {
this.showLoading = false;
this.showLoginForm = true;
console.warn(this);
}
}
但是,如果我改变服务方法只是为了实验俨然以下所示的(没有订阅查询结果)组件的视图被重新呈现良好。
checkAccessRights(): Observable<boolean> {
return new Observable((observer: Observer<boolean>) => {
const AUTHENTICATED = true;
const NOT_AUTHENTICATED = false;
this.currentUser.username = 'ADMIN';
this.currentUser.role = 'admin';
this.sessionService.getAccessToUser();
observer.next(AUTHENTICATED);
observer.complete();
});
}
在服务
checkAccessRights(): Promise<boolean> {
return new Promise((resolve, reject) => {
const queryParams: DbSelectQueryParams = {
fields: [
`NAME`,
`ROLE`
],
tables: [
`SINS.P_USER`
],
conditions: [
`UPPER(P_USER.NAME) = UPPER(:username)`,
`AND`,
`PSWD = :password`,
`AND`,
`HASH = :hash`,
`AND`,
`LOCKED <> '1'`,
`AND`,
`ROWNUM = 1`
]
};
const bindParams = {
username: this.currentAuthData.username,
password: this.currentAuthData.password,
hash: md5(this.currentAuthData.password)
};
let userIsAuthenticated = false;
const AUTHENTICATED = true;
const NOT_AUTHENTICATED = false;
this.dbSelect.select<CurrentUserDbRow>(queryParams, bindParams).subscribe(
user => {
console.warn(user);
this.currentUser.username = user.NAME.toUpperCase();
this.currentUser.role = user.ROLE.toLowerCase();
this.sessionService.getAccessToUser();
userIsAuthenticated = true;
resolve(AUTHENTICATED);
},
err => {
reject(err);
this.logger.writeError(err);
},
() => {
resolve(NOT_AUTHENTICATED);
if (userIsAuthenticated === NOT_AUTHENTICATED) {
this.logger.writeNewError('logon denied');
}
}
);
});
}
在组分
this.loginService.checkAccessRights().then(
accessAllowed => {
if (accessAllowed) {
this.toastService.showAdviceToast('Добро пожаловать!');
} else {
this.hideLoadingAnimation();
this.toastService.showErrorToastWithAdvice('ВХОД ЗАПРЕЩЁН', 'Обратитесь к администратору');
}
}
).catch(
err => {
this.hideLoadingAnimation();
this.toastService.showErrorToastWithAdvice('Не удалось войти', 'Попробуйте ещё раз');
}
);
请帮助我解决此问题并按预期进行组件重新渲染。
答
试图去改变一些事情:
- 注释你的组件领域正确的输入 - 只是
@Input
前面加上他们; - 通过明确指定或仅删除关键字
private
来公开这些字段。
@Input() showLoading = false;
@Input() showLoginForm = true;
+0
这也未解决问题。我想说明的是,如果我使用Promise而不是Observer,那么一切正常。 –
有时,问题来自于退订方法。 – Wandrille
在此代码的以前版本中,取消订阅方法不存在,并且问题相同 –