Angular - 在组件的控制器中检测到可观察到的变化,但在其模板中未检测到
Angular - Observable change detected in component's controller but not in its' template
第二次编辑:
所以!我已经弄清楚了问题所在,但要了解问题,您必须先了解目标:
我目前正在开发一个 Angular Chrome-Extension
,它有一个持久的后台脚本(不像我发现的所有示例和教程只解释非持久的后台脚本 - 或事件页面 Example) .
后台脚本实际上是一个 Angular 组件,其中包含 logic service
应该可以在不同的 chrome 组件(弹出窗口、选项等)中访问
- 当使用简单的 Angular DI 方法注入逻辑服务时,事情开始变得混乱,因为从 chrome 的角度来看,后台脚本页面和弹出脚本页面有不同的
window
对象导致有 2 个应用程序实例,1 个在 'background window' 中,另一个在 'popup window'. 中
- 当使用 chrome 的 API
chrome.extension.getBackgroundPage()
在弹出窗口中获取服务时,我得到了正确的实例,但现在出现另一个问题,后台服务的更改没有反映,因为该服务在 Angular 的应用上下文之外。
所以我有一个新问题是:
是否可以告诉 Angular 给 'add/watch' 一个未被实例化的 Angular 服务并将其添加到应用程序上下文中?
已编辑:
我已经添加了 2 个服务,似乎我的项目中有更深层次的问题,因为这个演示代码按预期工作。解决后将继续更新。
原题
我正在尝试将异步数据从父组件传递到子组件,我 运行 遇到了一些问题。
该服务持有一个可观察对象,父级订阅它 + 通过 async
管道将其传递给子级的输入。
问题是父级选择更改(仅来自控制器)并且 UI 的 none 已更新。
我可以在订阅函数中触发 ChangeDetectorRef.detectChanges()
,但在我看来这是一件非常奇怪的事情,因为 async
管道应该接受这些更改,但它没有。
有什么建议吗?
考虑以下代码(这作为一个新项目按预期工作):
父组件:
@Component({
selector: 'parent',
template: '<div>
<button (click)="logic.doAction()">Get data</button>
<child [someInput]="logic.data$ | async"></child>
<div>data: {{logic.data$ | async | json}}</div>
</div>'
})
export class ParentComponent {
constructor(public logic: LogicService) {}
}
子组件:
@Component({
selector: 'child',
template: '<span>{{someInput}}</child>'
})
export class ChildComponent {
@Input() someInput: string;
constructor() {}
}
逻辑服务:
@Injectable()
export class LogicService {
private readonly dataSubject: BehaviorSubject<Array<string>>;
private readonly _data$: Observable<Array<string>>;
constructor(private api: ApiService) {
this.dataSubject = new BehaviorSubject([]);
this._data$ = this.dataSubject.asObservable();
}
doAction(): void {
this.api.fetchFromAPI().then((data) => this.dataSubject.next(data));
}
get data$(): Observable<Array<string>> {
return this._data$;
}
}
API 服务:
@Injectable()
export class ApiService {
fetchFromAPI(): Promise<Array<string>> {
return new Promise((res, rej) => {
setInterval(() => {
res(['some', 'demo', 'data']);
}, 4000);
});
}
}
IMO,如果服务方法 returns 是一个可观察对象,它应该在需要数据的地方解包(订阅)。我会这样做:
<child [someInput]="service.observable"></child>
并且在 child 中:
export class ChildComponent {
@Input() someInput: Observable;
// subscribe to someInput in the child
}
所以我创建了一个指令,在更新可观察对象时强制更新视图。
@Directive({
selector: '[detectChanges]'
})
export class DetectChangesDirective implements OnInit, OnDestroy {
private subscription: Subscription;
@Input('detectChanges') observable$: Observable<any>;
constructor(private cd: ChangeDetectorRef) {
}
ngOnInit() {
this.subscription = this.observable$.subscribe((() => {
this.cd.detectChanges();
}));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
使用它非常简单,只需将它应用于您遇到更新问题的视图,如下所示:
<div [detectChanges]="logic.data$">
<button (click)="logic.doAction()">Get data</button>
<child [someInput]="logic.dataValue"></child>
<div>data: {{logic.dataValue | json}}</div>
</div>
重要提示:
- 请注意,我不再使用
async
管道,而是将实际值传递到绑定它的位置。
- 我将
Observable
传递给在值更改时跟踪的指令
第二次编辑:
所以!我已经弄清楚了问题所在,但要了解问题,您必须先了解目标:
我目前正在开发一个 Angular Chrome-Extension
,它有一个持久的后台脚本(不像我发现的所有示例和教程只解释非持久的后台脚本 - 或事件页面 Example) .
后台脚本实际上是一个 Angular 组件,其中包含 logic service
应该可以在不同的 chrome 组件(弹出窗口、选项等)中访问
- 当使用简单的 Angular DI 方法注入逻辑服务时,事情开始变得混乱,因为从 chrome 的角度来看,后台脚本页面和弹出脚本页面有不同的
window
对象导致有 2 个应用程序实例,1 个在 'background window' 中,另一个在 'popup window'. 中
- 当使用 chrome 的 API
chrome.extension.getBackgroundPage()
在弹出窗口中获取服务时,我得到了正确的实例,但现在出现另一个问题,后台服务的更改没有反映,因为该服务在 Angular 的应用上下文之外。
所以我有一个新问题是:
是否可以告诉 Angular 给 'add/watch' 一个未被实例化的 Angular 服务并将其添加到应用程序上下文中?
已编辑: 我已经添加了 2 个服务,似乎我的项目中有更深层次的问题,因为这个演示代码按预期工作。解决后将继续更新。
原题
我正在尝试将异步数据从父组件传递到子组件,我 运行 遇到了一些问题。
该服务持有一个可观察对象,父级订阅它 + 通过 async
管道将其传递给子级的输入。
问题是父级选择更改(仅来自控制器)并且 UI 的 none 已更新。
我可以在订阅函数中触发 ChangeDetectorRef.detectChanges()
,但在我看来这是一件非常奇怪的事情,因为 async
管道应该接受这些更改,但它没有。
有什么建议吗?
考虑以下代码(这作为一个新项目按预期工作):
父组件:
@Component({
selector: 'parent',
template: '<div>
<button (click)="logic.doAction()">Get data</button>
<child [someInput]="logic.data$ | async"></child>
<div>data: {{logic.data$ | async | json}}</div>
</div>'
})
export class ParentComponent {
constructor(public logic: LogicService) {}
}
子组件:
@Component({
selector: 'child',
template: '<span>{{someInput}}</child>'
})
export class ChildComponent {
@Input() someInput: string;
constructor() {}
}
逻辑服务:
@Injectable()
export class LogicService {
private readonly dataSubject: BehaviorSubject<Array<string>>;
private readonly _data$: Observable<Array<string>>;
constructor(private api: ApiService) {
this.dataSubject = new BehaviorSubject([]);
this._data$ = this.dataSubject.asObservable();
}
doAction(): void {
this.api.fetchFromAPI().then((data) => this.dataSubject.next(data));
}
get data$(): Observable<Array<string>> {
return this._data$;
}
}
API 服务:
@Injectable()
export class ApiService {
fetchFromAPI(): Promise<Array<string>> {
return new Promise((res, rej) => {
setInterval(() => {
res(['some', 'demo', 'data']);
}, 4000);
});
}
}
IMO,如果服务方法 returns 是一个可观察对象,它应该在需要数据的地方解包(订阅)。我会这样做:
<child [someInput]="service.observable"></child>
并且在 child 中:
export class ChildComponent {
@Input() someInput: Observable;
// subscribe to someInput in the child
}
所以我创建了一个指令,在更新可观察对象时强制更新视图。
@Directive({
selector: '[detectChanges]'
})
export class DetectChangesDirective implements OnInit, OnDestroy {
private subscription: Subscription;
@Input('detectChanges') observable$: Observable<any>;
constructor(private cd: ChangeDetectorRef) {
}
ngOnInit() {
this.subscription = this.observable$.subscribe((() => {
this.cd.detectChanges();
}));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
使用它非常简单,只需将它应用于您遇到更新问题的视图,如下所示:
<div [detectChanges]="logic.data$">
<button (click)="logic.doAction()">Get data</button>
<child [someInput]="logic.dataValue"></child>
<div>data: {{logic.dataValue | json}}</div>
</div>
重要提示:
- 请注意,我不再使用
async
管道,而是将实际值传递到绑定它的位置。 - 我将
Observable
传递给在值更改时跟踪的指令