错误TypeError:“ this.profileService.getUserProfile(...)未定义”

时间:2019-06-18 08:18:26

标签: angular typescript ionic-framework

我用用户名,密码和电子邮件更新功能创建一个配置文件页面。我可以使用“更改电子邮件”和“更改密码”,但是以某种方式将相同的方法应用于“更改名称”却无法正常工作

我确实尝试了一些安全的导航操作符(?),但不起作用。请帮我指出我错了,非常感谢。

profile.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';

import { IonicModule } from '@ionic/angular';

import { ProfilePage } from './profile.page';

const routes: Routes = [
  {
    path: '',
    component: ProfilePage
  }
];

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule.forChild(routes)
  ],
  declarations: [ProfilePage]
})
export class ProfilePageModule {}

profile.page.html

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/home"></ion-back-button>
    </ion-buttons>
    <ion-title>Profile Page</ion-title>
    <ion-buttons slot="end">
      <ion-button (click)="logOut()">
        <ion-icon slot="icon-only" name="log-out"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list> <ion-list-header> Personal Information </ion-list-header> </ion-list>
  <ion-item (click)="updateName()">
    <ion-label>
      <ion-grid>
        <ion-row>
          <ion-col class="text-left" size="5"> Name </ion-col>
          <ion-col
            class="text-center"
            size="7"
            *ngIf="userProfile?.firstName || userProfile?.lastName"
          >
            {{userProfile?.firstName}} {{userProfile?.lastName}}
          </ion-col>
          <ion-col
            size="7"
            class="placeholder-profile text-center"
            *ngIf="!userProfile?.firstName"
          >
            <span> Tap here to edit. </span>
          </ion-col>
        </ion-row>
      </ion-grid>
    </ion-label>
  </ion-item>
  <ion-item>
    <ion-label class="dob-label">Date of Birth</ion-label>
    <ion-datetime
      displayFormat="MMM D, YYYY"
      pickerFormat="D MMM YYYY"
      [(ngModel)]="birthDate"
      (ionChange)="updateDOB(birthDate)"
    >
    </ion-datetime>
  </ion-item>
  <ion-item (click)="updateEmail()">
    <ion-label>
      <ion-grid>
        <ion-row>
          <ion-col class="text-left" size="5"> Email </ion-col>
          <ion-col class="text-center" size="7" *ngIf="userProfile?.email">
            {{userProfile?.email}}
          </ion-col>
          <ion-col
            size="7"
            class="placeholder-profile text-center"
            *ngIf="!userProfile?.email"
          >
            <span> Tap here to edit. </span>
          </ion-col>
        </ion-row>
      </ion-grid>
    </ion-label>
  </ion-item>

  <ion-item (click)="updatePassword()">
    <ion-label>
      <ion-grid>
        <ion-row>
          <ion-col class="text-left" size="5"> Password </ion-col>
          <ion-col class="text-center" size="7" class="placeholder-profile">
            <span> Tap here to edit. </span>
          </ion-col>
        </ion-row>
      </ion-grid>
    </ion-label>
  </ion-item>
</ion-content>

profile.page.ts

import { Component, OnInit } from '@angular/core';
import { AlertController } from '@ionic/angular';
import { AuthService } from '../../services/user/auth.service';
import { ProfileService } from '../../services/user/profile.service';
import { Router } from '@angular/router';
@Component({
  selector: 'app-profile',
  templateUrl: './profile.page.html',
  styleUrls: ['./profile.page.scss'],
})
export class ProfilePage implements OnInit {
  public userProfile: any;
  public birthDate: Date;
  constructor(
    private alertCtrl: AlertController,
    private authService: AuthService,
    private profileService: ProfileService,
    private router: Router
  ) { }

  ngOnInit() {
    this.profileService
      .getUserProfile()
      .get()
      .then( userProfileSnapshot => {
        this.userProfile = userProfileSnapshot.data();
        this.birthDate = userProfileSnapshot.data().birthDate;
      });
  }
  async updateName(): Promise<void> {
    const alert = await this.alertCtrl.create({
      subHeader: 'Your first name & last name',
      inputs: [
        {
          type: 'text',
          name: 'firstName',
          placeholder: 'Your first name',
          value: this.userProfile.firstName,
        },
        {
          type: 'text',
          name: 'lastName',
          placeholder: 'Your last name',
          value: this.userProfile.lastName,
        },
      ],
      buttons: [
        { text: 'Cancel' },
        {
          text: 'Save',
          handler: data => {
            this.profileService.updateName(data.firstName, data.lastName);
          },
        },
      ],
    });
    await alert.present();
  }
  logOut(): void {
    this.authService.logoutUser().then( () => {
      this.router.navigateByUrl('login');
    });
  }

  updateDOB(birthDate: string): void {
    if (birthDate === undefined) {
      return;
    }
    this.profileService.updateDOB(birthDate);
  }
  async updateEmail(): Promise<void> {
    const alert = await this.alertCtrl.create({
      inputs: [
        { type: 'text', name: 'newEmail', placeholder: 'Your new email' },
        { name: 'password', placeholder: 'Your password', type: 'password' },
      ],
      buttons: [
        { text: 'Cancel' },
        {
          text: 'Save',
          handler: data => {
            this.profileService
              .updateEmail(data.newEmail, data.password)
              .then(() => {
                console.log('Email Changed Successfully');
              })
              .catch(error => {
                console.log('ERROR: ' + error.message);
              });
          },
        },
      ],
    });
    await alert.present();
  }

  async updatePassword(): Promise<void> {
    const alert = await this.alertCtrl.create({
      inputs: [
        { name: 'newPassword', placeholder: 'New password', type: 'password' },
        { name: 'oldPassword', placeholder: 'Old password', type: 'password' },
      ],
      buttons: [
        { text: 'Cancel' },
        {
          text: 'Save',
          handler: data => {
            this.profileService.updatePassword(
              data.newPassword,
              data.oldPassword
            );
          },
        },
      ],
    });
    await alert.present();
  }
}

profile.service.ts

import { Injectable } from '@angular/core';
import * as firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
@Injectable({
  providedIn: 'root'
})
export class ProfileService {
  public userProfile: firebase.firestore.DocumentReference;
  public currentUser: firebase.User;
  constructor() {
    firebase.auth().onAuthStateChanged(user => {
    if (user) {
      this.currentUser = user;
      this.userProfile = firebase.firestore().doc(`/userProfile/${user.uid}`);
    }
  });
}
getUserProfile(): firebase.firestore.DocumentReference {
  return this.userProfile;
}
updateName(firstName: string, lastName: string): Promise<any> {
  return this.userProfile.update({ firstName, lastName });
}
updateDOB(birthDate: string): Promise<any> {
  return this.userProfile.update({ birthDate });
}
  async updateEmail(newEmail: string, password: string): Promise<any> {
  const credential: firebase.auth.AuthCredential = firebase.auth.EmailAuthProvider.credential(
    this.currentUser.email,
    password
  );

  try {
    await this.currentUser
      .reauthenticateAndRetrieveDataWithCredential(credential);
    this.currentUser.updateEmail(newEmail).then(() => {
      this.userProfile.update({ email: newEmail });
    });
  }
  catch (error) {
    console.error(error);
  }
}
  async updatePassword(newPassword: string, oldPassword: string): Promise<any> {
  const credential: firebase.auth.AuthCredential = firebase.auth.EmailAuthProvider.credential(
    this.currentUser.email,
    oldPassword
  );

  try {
    await this.currentUser
      .reauthenticateAndRetrieveDataWithCredential(credential);
    this.currentUser.updatePassword(newPassword).then(() => {
      console.log('Password Changed');
    });
  }
  catch (error) {
    console.error(error);
  }
}
}
ERROR TypeError: "this.profileService.getUserProfile(...) is undefined"

ERROR Error: "Uncaught (in promise): TypeError: this.userProfile is undefined

1 个答案:

答案 0 :(得分:0)

在配置文件组件初始化期间会触发

ngOnInit生命周期挂钩:

ngOnInit() {
    this.profileService
      .getUserProfile()
      .get()
      .then( userProfileSnapshot => {
        this.userProfile = userProfileSnapshot.data();
        this.birthDate = userProfileSnapshot.data().birthDate;
      });
  }

同时,在您的个人资料服务中,方法“ getUserProfile”仍未定义,因为您只返回定义了类型但没有值的var:

getUserProfile(): firebase.firestore.DocumentReference {
  return this.userProfile;
}

这是因为向this.userProfile分配值的方法是异步的:

constructor() {
    firebase.auth().onAuthStateChanged(user => {
    if (user) {
      this.currentUser = user;
      this.userProfile = firebase.firestore().doc(`/userProfile/${user.uid}`);
    }
  });

因此,当您的生命周期挂钩已尝试利用userProfile时,甚至不会返回用户值。

已更新

要解决此问题,您需要确保分配了诺言并进行了适当的“等待”:

在您的服务中,将userProfile和currentUser都定义为Promises:

public userProfile: Promise<any>;
public currentUser: Promise<any>;

在构造函数中执行

this.currentUser = new Promise(async (resolve,reject)=>{
  let user = await firebase.auth().onAuthStateChanged();
  resolve(user)
})
this.userProfile = new Promise(async (resolve,reject)=>{
  let user = await this.currentUser;
  let userProfile = firebase.firestore().doc(`/userProfile/${user.uid}`);
})

现在,您的方法应该返回promise而不是未定义的var。请检查一下。我无法验证此代码,但我构建了一个类似于它的测试用例,它应该可以工作,但也请参见修复返回的类型:

getUserProfile(): Promise<any> {
  return this.userProfile;
}