在开始提问之前,我想告诉您我已经做了大量研究,但我找不到解决方法(解释)为什么会出现此错误。
请注意,我是Angular的新手,我刚开始学习它的工作原理。
所以,我遇到的问题就是我在这个问题的标题中输入的内容。
我尝试做的是根据我在Udemy上购买的课程,使用Firebase构建登录系统。
我使用的代码如下:
auth.service.ts
import {Injectable} from '@angular/core';
import * as firebase from 'firebase';
@Injectable ()
export class AuthService {
token: string;
// ...
singInUser ( email: string, password: string ) {
// login process here ...
}
// Responsible to retrieve the authenticated user token
getToken () {
return firebase
.auth ()
.currentUser
.getIdToken ();
}
}
数据-storage.service.ts
// ... Dependencies here
@Injectable ()
export class DataStorageService {
private recipeEndPoint: string = 'https://my-unique-id.firebaseio.com/recipes.json';
private recipeSubscription: Observable<any> = new Observable();
constructor ( private http: Http,
private recipes: RecipeService,
private authService: AuthService ) {}
// other functionality ...
getRecipes () {
const token = this.authService.getToken ();
token.then (
( token: string ) => {
this.recipeSubscription = this.http.get ( this.recipeEndPoint + '?auth=' + token ).map (
( data: Response ) => {
return data.json ();
}
);
// THIS PARTICULAR CODE WORKS AS EXPECTED
// WITH NO ISSUES
this.recipeSubscription.subscribe (
( data: Response ) => {
console.log ( 'Data response: ', data );
},
( error ) => {
console.log ( 'Error: ' + error );
}
)
}
);
// This is supposed to return an Observable to the caller
return this.recipeSubscription;
}
}
header.component.ts
// Dependencies here ...
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
constructor(private dataStorage: DataStorageService, private recipeService: RecipeService) { }
// Other Code Here ...
onFetchData() {
let recipeSubscription = this.dataStorage.getRecipes();
// THIS RETURNS TRUE
console.log(recipeSubscription instanceof Observable);
// THIS LINE THEN RETURNS THE MESSAGE:
// ERROR TypeError: Cannot read property 'subscribe' of undefined
recipeSubscription.subscribe();
// IF I COMMENT OUT THE PREVIOUS LINE
setTimeout(
() => {
// THIS RETURNS TRUE
console.log(recipeSubscription instanceof Observable);
},
500
);
setTimeout(
() => {
// AS WELL THIS ONE RETURNS TRUE
console.log(recipeSubscription instanceof Observable);
},
1000
);
setTimeout(
() => {
// AS WELL THIS ONE RETURNS TRUE
console.log(recipeSubscription instanceof Observable);
},
1500
);
}
}
所以,不幸的是,我看不出这段代码会出现什么问题。谁能发现我做错了什么?
注意: 我删除了部分代码,只是为了让代码片段更具可读性。如果您需要任何其他部分,请随时问我,我会在这里提供。
更新#1
这就是header.component.html
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">Logo Here</div>
<div class="navbar-default">
<ul class="nav navbar-nav">
<!-- Left Navigation Options -->
</ul>
<ul class="nav navbar-nav navbar-right">
<!-- Right Navigation Options -->
<li class="dropdown" appDropdown>
<a routerLink="/" class="dropdown-toggle" role="button">Manage <span class="caret"></span></a>
<ul class="dropdown-menu">
<li>
<a style="cursor: pointer;" (click)="onSaveData()">Save Data</a>
</li>
<li>
<!-- Here is where I call the onFetchData method -->
<a style="cursor: pointer;" (click)="onFetchData()">Fetch Data</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
答案 0 :(得分:23)
我使用统一的EventEmitter遇到了相同的错误:
@Output() change: EventEmitter<any>;
代替:
@Output() change: EventEmitter<any> = new EventEmitter<any>();
尝试订阅更改事件的更高级别组件中发生了错误。
答案 1 :(得分:15)
问题似乎是代码执行的顺序,更具体地说是getRecipes()
方法:
// Numbers indicate the execution order
getRecipes () {
const token = this.authService.getToken ();
// 1. You call a promise, which will take a while to execute...
token.then (
( token: string ) => {
// 3. Finally, this bit gets executed, but only when the promise resolves.
this.recipeSubscription = ...
}
);
// 2. Then, you return a variable that hasn't been assigned yet,
// due to the async nature of the promise.
return this.recipeSubscription;
}
对此的解决方案是您的getRecipes ()
方法不应该订阅。它应返回Promise或Observable 。
这样的事情:
getRecipes() {
// Convert the initial promise into an observable
// so can you use operators like map(), mergeMap()... to transform it.
const tokenObs = Observable.fromPromise(this.authService.getToken());
// Merge the token observable into an HTTP observable
// and return the JSON data from the response.
return tokenObs
.mergeMap(token => this.http.get('XXX?auth=' + token))
.map(resp => resp.json());
}
然后,HeaderComponent
中的调用代码变为:
const recipeObs = this.dataStorage.getRecipes();
recipesObs.subcribe(jsonData => {
// Use the JSON data from the HTTP response
});
几条评论:
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
getRecipes()
不是一个好主意,因为它包含recipeSubscription
,而不是Observable
。订阅是Subscription
返回的内容。换句话说:subscribe()
。答案 2 :(得分:4)
问题
我偶然发现了相同的错误,原因是我正在ngOnInit()内部初始化@Output
事件发射器。
export class MyClass implements OnInit {
@Output()
onChange : EventEmitter<void>;
ngOnInit() {
// DO NOT initialize @Output event here
this.onChange = new EventEmitter<void>();
}
}
解决方案
当我将初始化更改为声明的相同位置时。
export class MyClass implements OnInit {
@Output()
onChange : EventEmitter<void> = new EventEmitter<void>();
ngOnInit() {
}
}
我认为这是因为父组件尝试过早订阅事件(在触发ngOnInit()
之前)。
答案 3 :(得分:3)
问题是,你正在返回一个observable并在Token()的响应中重新分配它。
尝试制作你现在拥有的Observable的主题,我发现这些更容易使用。
<div id="content">
Confetti World
<br /> I confetti!
<br />
<div class="buttonContainer">
<button id="stopButton">Stop Confetti</button>
<button id="startButton">Drop Confetti</button>
</div>
</div>
<canvas id="canvas"></canvas>
从
更改作业public recipeSubscription: Subject<any> = new Subject();
要
this.recipeSubscription = this.http.get....
在函数中订阅此函数:
let response = this.http.get....
现在您可以直接在物业上订阅
response.subscribe((res) => {this.recipeSubscription.next(res)})
我希望这足以帮助你:)。
答案 4 :(得分:0)
离子4。
我有一个订阅可观察对象的方法,在ngOnInit
中调用该方法,引发了此错误。将方法调用移到构造函数上解决了我的问题。
答案 5 :(得分:0)
正如已经提到的其他答案一样,您应该返回observable以便进行订阅。 就我而言,尽管如此,它仍会引发错误。我刚刚用
重新启动了我的角度应用程序ng serve
然后正常工作。
答案 6 :(得分:0)
我正在使用angular8 , 面临类似的挑战。 我应该打一个端点,将一些有效的凭据附加到我的get / post上,从而在检查凭据之前调用方法会导致问题。
在执行任何操作之前,请确保已通过身份验证,以解决问题:
ngOnInit() {
this.authService.isAuthenticated().then(status => {
if (status === true) {
this.getClients();
this.getCities();
} else {
this.routerCtrl.navigate(["login"]);
}
});
}