angular - 在 app.component.ts 中将 Observable 添加到 ngOnInit 后,Reactive Form 停止工作
问题描述
好的,这是我奇怪的情况。我创建了一个带有侧边导航的 Ionic 项目。然后我创建了一个简单的登录组件来与 Firebase 一起使用。该表单就像一个魅力,它在字段更改时触发并显示验证错误。现在,我的下一步是在用户登录时禁用侧边菜单。出于某种原因,当我到达处理侧边菜单逻辑的 app.component.ts 并在 ngOnInit 函数中添加一个 observable 时,禁用的菜单有效,但反应形式停止工作。
这是我的登录组件:
login.module.ts:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { LoginPageRoutingModule } from './login-routing.module';
import { LoginPage } from './login.page';
import { ComponentsModule } from '../../../components/components.module'
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
IonicModule,
LoginPageRoutingModule,
ComponentsModule
],
declarations: [LoginPage]
})
export class LoginPageModule {}
login.page.html
<form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)" novalidate>
<ion-list>
<ion-item>
<ion-label position="floating" color="primary">Email</ion-label>
<ion-input type="email" formControlName="email"></ion-input>
</ion-item>
<div class="validation-errors">
<ng-container *ngFor="let v of validation_messages.email">
<ion-text color="danger" *ngIf="email.hasError(v.type) && (email.dirty || email.touched)">
<p class="ion-padding-start">
{{ v.message }}
</p>
</ion-text>
</ng-container>
</div>
<ion-item>
<ion-label position="floating" color="primary">Password</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
<div class="validation-errors">
<ng-container *ngFor="let v of validation_messages.password">
<ion-text color="danger" *ngIf="password.hasError(v.type) && (password.dirty || password.touched)">
<p class="ion-padding-start">
{{ v.message }}
</p>
</ion-text>
</ng-container>
</div>
</ion-list>
<ion-button class="submit-btn" type="submit" expand="block" [disabled]="!loginForm.valid">Login</ion-button>
<ion-text color="danger">
<p class="ion-padding-start">
{{ errorMessage }}
</p>
</ion-text>
<ion-text color="success">
<p class="ion-padding-start">
{{ successMessage }}
</p>
</ion-text>
<p class="go-to-register">No account yet? <a [routerLink]="['/register']">Create an account.</a></p>
{{loginForm.value | json}}
</form>
login.page.ts:
import { Component, OnInit, NgZone } from '@angular/core';
import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { AuthService } from '../../../services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
loginForm: FormGroup;
testForm: FormGroup;
errorMessage: string = "";
successMessage: string = "";
errorMessageFB: string = "";
validation_messages = {
'email': [
{ type: 'required', message: 'Email is required.' },
{ type: 'email', message: 'Enter a valid email.' }
],
'password': [
{ type: 'required', message: 'Password is required.' },
{ type: 'minlength', message: 'Password need to be at least 6 characters long.' }
]
}
constructor(
private formBuilder: FormBuilder,
private authService: AuthService,
private router: Router,
private ngZone: NgZone
) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
email: new FormControl('', Validators.compose([
Validators.required,
Validators.email
])),
password: new FormControl('', Validators.compose([
Validators.required,
Validators.minLength(6)
]))
});
}
login(value: any){
}
loginWithFB(){
}
get email() { return this.loginForm.get('email'); }
get password() { return this.loginForm.get('password'); }
}
到目前为止一切顺利,这有效:
现在我有一个服务来登录和注册用户,到目前为止它正在工作:
auth.service.ts
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Facebook, FacebookLoginResponse } from '@ionic-native/facebook/ngx';
import { Platform } from '@ionic/angular';
import { auth } from 'firebase/app';
import { resolve } from 'url';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
user$: Observable<firebase.User>;
isLoggedIn$: Observable<boolean>;
isLoggedOut$: Observable<boolean>;
constructor(
private afAuth: AngularFireAuth,
private fb: Facebook,
private platform: Platform
) { }
init(){
this.user$ = this.afAuth.user;
this.isLoggedIn$ = this.afAuth.authState.pipe(map(user => !!user));
this.isLoggedOut$ = this.isLoggedIn$.pipe(map(loggedIn => !loggedIn));
}
loginWithFB(): Promise<auth.UserCredential>{
if (this.platform.is("capacitor")) {
return this.loginNativeFB();
} else {
return this.loginBrowserFB();
}
}
loginBrowserFB(){
return new Promise<auth.UserCredential>((resolve, reject) => {
const provider = new auth.FacebookAuthProvider();
this.afAuth.signInWithPopup(provider).then((res) => {
resolve(res);
}, (err) => {
reject(err);
})
});
}
loginNativeFB(){
return new Promise<auth.UserCredential>((resolve, reject) => {
this.fb.login(['email', 'public_profile']).then((response: FacebookLoginResponse) => {
if (response.authResponse) {
const credential = auth.FacebookAuthProvider.credential(
response.authResponse.accessToken
);
this.afAuth.signInWithCredential(credential).then((res) => {
resolve(res);
}, (rej) => {
reject(rej);
})
}
}, (err) => {
console.log(err);
reject(err);
})
});
}
doRegister(value: any){
return new Promise<any>((resolve, reject) => {
this.afAuth.createUserWithEmailAndPassword(value.email, value.password).then(
res => resolve(res),
err => reject(err)
)
})
}
doLogin(value: any){
return new Promise<any>((resolve, reject) => {
this.afAuth.signInWithEmailAndPassword(value.email, value.password).then(
res => resolve(res),
err => reject(err)
)
})
}
doLogout(){
return new Promise<any>((resolve, reject) => {
this.afAuth.signOut().then(() => {
resolve();
}).catch((error) => {
console.log(error);
reject();
})
})
}
}
现在问题来了:我想使用 observable 隐藏侧边菜单。所以我去: app.component.ts 并这样做:
import { Component, OnInit } from '@angular/core';
import { Platform } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { AuthService } from './services/auth.service'
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {
public selectedIndex = 0;
public appPages = [
{
title: 'Inbox',
url: '/folder/Inbox',
icon: 'mail'
},
{
title: 'Outbox',
url: '/folder/Outbox',
icon: 'paper-plane'
},
{
title: 'Favorites',
url: '/folder/Favorites',
icon: 'heart'
},
{
title: 'Archived',
url: '/folder/Archived',
icon: 'archive'
},
{
title: 'Trash',
url: '/folder/Trash',
icon: 'trash'
},
{
title: 'Spam',
url: '/folder/Spam',
icon: 'warning'
}
];
public labels = ['Family', 'Friends', 'Notes', 'Work', 'Travel', 'Reminders'];
disableMenu: boolean;
user: firebase.User;
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private authService: AuthService
) {
this.initializeApp();
}
initializeApp() {
this.authService.init();
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
ngOnInit() {
!!!!!!!!!! HERE I ADD THIS SUBSBRIBE AND THIs BREAK THE FORM !!!!!!!!!!
this.authService.isLoggedOut$.subscribe((loggedOut) => {
this.disableMenu = loggedOut;
});
this.authService.user$.subscribe((user) => {
this.user = user;
});
!!!!!!!!!! IF I REMOVE THIS IT START WORKING AGAIN !!!!!!!!!!!!!!!!!!!!
const path = window.location.pathname.split('folder/')[1];
if (path !== undefined) {
this.selectedIndex = this.appPages.findIndex(page => page.title.toLowerCase() === path.toLowerCase());
}
}
}
通过将这些 observables 添加到 ngOnInit 的代码中,我的表单将停止工作。控制台中没有显示错误。
我错过了什么吗?谢谢
我可以通过在表单中添加一个 ngIf 来修复它,如下所示:
<form *ngIf="loginForm" [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)" novalidate>
但我仍然想解释为什么会发生这种情况。
应用模块:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireAuthGuard } from '@angular/fire/auth-guard';
import { environment } from '../environments/environment';
import { Facebook } from "@ionic-native/facebook/ngx";
import { Geolocation } from '@ionic-native/geolocation/ngx';
import { GoogleMaps } from '@ionic-native/google-maps/ngx'
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
AngularFirestoreModule
],
providers: [
StatusBar,
SplashScreen,
Facebook,
Geolocation,
GoogleMaps,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
AngularFireAuthGuard
],
bootstrap: [AppComponent]
})
export class AppModule {}
解决方案
推荐阅读
- php - 更改产品 Prestashop 的每个变体的包装尺寸
- xpath - xpath 无法识别标记的谓词
- python - 使用动态 ID 从 Python 代码访问 kivy 小部件
- sql - SQL Server - 在 WHERE 子句中使用组合列
- json - 在 Scala 中解析对 jsonarray 的请求
- spring-boot - spring boot jar 缺少项目依赖项
- javascript - 如何在页面部分中嵌套 nightwatch.js 命令?
- java - 当我按下按钮android时如何动态地将图像添加到按钮
- c# - 检测具有重复主键的插入
- web - 跟踪网站上的所有事件