![](/img/trans.png)
[英]Angular: Share data dynamically between components using RxJs/BehaviorSubject
[英]Using BehaviorSubject to share data between sibling components
我仍在學習Angular,並嘗試查看BehaviorSubject
工作原理,但被困在何處。 這是我要做的事:
創建一個名為HeroDetailComponent
的搜索框組件,在其中輸入一些文本,並根據此文本搜索英雄列表,並使用另一個HeroesComponent
組件將其顯示給用戶。 現在,應該在BehaviorSubject
幫助下使用服務組件完成此通信。
這是我的主要組成部分:
app.component.html
<app-hero-detail></app-hero-detail>
<app-heroes></app-heroes>
這是我的hero-detail.component.html,我將其用作搜索框:
<div>
<label>name:
<input [(ngModel)]="hero.name" placeholder="name"/>
</label>
</div>
和相應的hero-detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
這是我的heroes.component.html ,在這里顯示搜索結果:
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
及其heroes.component.ts :
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes = HEROES;
constructor(private heroService: HeroService) { }
getHeroes(): void {
// Logic to access heroes list matching search text and display to user
}
ngOnInit() {
this.getHeroes();
}
}
這是我的服務組件:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable({
providedIn: 'root'
})
export class HeroService {
private searchSource = new BehaviorSubject<string>("");
searchText = this.searchSource.asObservable();
constructor() { }
searchByName(name: string) {
this.searchSource.next(name);
}
}
這是我的英雄榜模擬數據:
import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
您能幫我實現這個問題嗎?
更新:
我按照trichetriche答案中給出的步驟進行操作,但出現以下錯誤:
ERROR in src/app/hero-detail/hero-detail.component.ts(17,16): error TS2339: Property 'formValue$' does not exist on type 'HeroService'.
src/app/hero-detail/hero-detail.component.ts(17,61): error TS2304: Cannot find name 'startWith'.
src/app/heroes/heroes.component.ts(17,13): error TS2304: Cannot find name 'combineLatest'.
src/app/heroes/heroes.component.ts(19,22): error TS2339: Property 'formValue$' does not exist on type 'HeroService'.
src/app/heroes/heroes.component.ts(21,5): error TS2552: Cannot find name 'map'. Did you mean 'Map'?
如何解決這些錯誤?
更新:上面的錯誤現在已解決。 現在我開始面對可觀察的問題:
Observable.js:54 TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at subscribeTo (subscribeTo.js:41)
at subscribeToResult (subscribeToResult.js:11)
at CombineLatestSubscriber.push../node_modules/rxjs/_esm5/internal/observable/combineLatest.js.CombineLatestSubscriber._complete (combineLatest.js:62)
at CombineLatestSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.complete (Subscriber.js:66)
at Observable._subscribe (subscribeToArray.js:8)
at Observable.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe (Observable.js:43)
at Observable.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe (Observable.js:29)
at CombineLatestOperator.push../node_modules/rxjs/_esm5/internal/observable/combineLatest.js.CombineLatestOperator.call (combineLatest.js:32)
at Observable.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe (Observable.js:24)
at MapOperator.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapOperator.call (map.js:18)
您能幫我解決這個問題嗎?
主題和行為主題是代理:它們既充當可觀察者,又充當觀察者。
您可以在您可以使subject.next subject.next(value)
時使代理的觀察者做出反應。next subject.next(value)
:主題與行為主題之間的區別在於,主題的觀察者將收到代理的當前值(而主題將僅獲取下next
的值)。
所以基本上
BehaviorSubject === Subject.pipe(startWith(someValue))
現在,您實際上不需要行為主體即可完成您想要的事情。 您可以使用一種更簡潔的方法,使用反應形式及其valueChanges
屬性。
在您的表單中,使用以下命令:
<div>
<label>name:
<input [formControl]="heroName" placeholder="name"/>
</label>
</div>
export class HeroDetailComponent implements OnInit {
heroName = new FormControl('');
constructor(
private service: HeroService
) {
this.service.formValue$ = this.heroName.valueChanges.pipe(startWith(''));
}
}
您將創建一個表單控件,然后將服務注入到組件中,最后,您將使用該表單控件的valueChanges
觀察值對同級中的英雄進行排序。
在服務中,只需添加一個formValue$: Observable<string>
即可聲明變量。 ( xx$
只是可觀察對象的命名約定,以便快速發現它們)。
您需要startWith
運算符來觸發控件上的第一個值更改(否則,除非您鍵入內容,否則該值不會更改)
最后,在同級中:
export class HeroesComponent implements OnInit {
private _heroesList$ = new BehaviorSubject(HEROES);
heroes$;
constructor(private heroService: HeroService) { }
getHeroes(): void {
this.heroes$ = combineLatest(
this._heroesList$,
this.heroService.formValue$
).pipe(
map(([list, name]) => list.filter(hero => hero.name.includes(name)))
);
}
ngOnInit() {
this.getHeroes();
}
}
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes$ | async"
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
這樣,您就可以創建一個由表單值和列表組成的新流,並映射結果以返回過濾后的英雄列表。 您可以使用async
管道讓angular處理訂閱,這是一個好習慣,可以防止您忘記取消訂閱。
getHeroes(): void {
this.heroService.searchText
.pipe(
map((name) => HEROES.filter((hero) => hero.name.indexOf(name) !== -1))
)
.subscribe((results) => this.heroes = results);
}
然后像這樣循環:
<ul class="heroes">
<li *ngFor="let hero of heroes"
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.