Konu geniş. Bir öğretici gibi olacak. Bir deneyeceğim. Normal durumda, eylem, redüktör ve deponuz olacak. Eylemler, indirgeyici tarafından abone olunan mağaza tarafından gönderilir ve daha sonra redüktör harekete geçerek yeni bir durum oluşturur. Öğretici için, tüm eyaletler ön plandadır, ancak gerçek uygulamada, DB veya MQ vb. Arka uçları çağırmak zorundadır, bu çağrıların, bu etkileri ortak bir yere, yani kullanılan çerçeveye dönüştürmek için yan etkileri vardır.
Kişi Kaydı'nı veritabanına kaydetmek için action: Action = {type: SAVE_PERSON, payload: person}
diyelim. Normalde bileşen, HTTP hizmetini indirgemeye izin vermek için doğrudan this.store.dispatch({type: SAVE_PERSON, payload: person})
'u arayamaz, bunun yerine this.personService.save(person).subscribe(res => this.store.dispatch({type: SAVE_PERSON_OK, payload: res.json}))
'u arayacaktır. Gerçek hayatta hata saptamayı düşünürseniz, bileşen mantığı daha karmaşık hale gelecektir. Bunu önlemek için, bileşenden this.store.dispatch({type: SAVE_PERSON, payload: person})
numaralı telefonu arayarak sorun olmaz.
Bu etki kitaplığı için geliyor. Redüktörün önünde JEE servlet filtresi gibi davranır. ACTION türüyle eşleşir (filtre, java dünyasındaki URL'lerle eşleşebilir) ve daha sonra bunun üzerinde hareket eder, ve sonunda farklı bir eylem, ya da eylem ya da çoklu eylemler döndürür ve ardından efektlerin çıkış eylemlerine yanıtları azaltır.
etkileri kütüphane şekilde, önceki örnekte Devam:
@Effects() savePerson$ = this.stateUpdates$.whenAction(SAVE_PERSON)
.map<Person>(toPayload)
.switchMap(person => this.personService.save(person))
.map(res => {type: SAVE_PERSON_OK, payload: res.json})
.catch(e => {type: SAVE_PERSON_ERR, payload: err})
örgü mantığı tüm Etkileri ve İndirgenler sınıflara merkezileştirilir. Daha karmaşık hale gelebilir ve aynı zamanda bu tasarım diğer parçaları daha basit ve daha tekrar kullanılabilir hale getirir.
Örneğin, UI'nin otomatik kaydetme ve elle kaydetme özelliği varsa, gereksiz kaydetme işlemlerinden kaçınmak için, UI otomatik kaydetme parçası yalnızca zamanlayıcı tarafından tetiklenebilir ve kullanıcı tarafından elle bölümün tetiklenmesi, her ikisi de SAVE_CLIENT eylemini, etki engelleyicide gönderir, bu olabilir: bir hata varsa
...switchMap(person => this.personService.save(person))
.map(res => {type: SAVE_PERSON_OK, payload: res.json})
.catch(e => Observable.of({type: SAVE_PERSON_ERR, payload: err}))
sadece bir kez çalışır
@Effects() savePerson$ = this.stateUpdates$.whenAction(SAVE_PERSON)
.debounce(300).map<Person>(toPayload)
.distinctUntilChanged(...)
.switchMap(see above)
// at least 300 milliseconds and changed to make a save, otherwise no save
çağrı. Yakalama, hata yakalandıktan sonra yayılır, çünkü yakalama dış akış üzerinde çalışır. Çağrı
...switchMap(person => this.personService.save(person).map(res => {type: SAVE_PERSON_OK, payload: res.json})
.catch(e => Observable.of({type: SAVE_PERSON_ERR, payload: err})))
olmalıdır; Ya da başka bir şekilde: sunucu tarafında hata kodunu, hata mesajını ve sarılmış yanıt nesnesini içeren ServiceResponse'yi döndürmek için tüm ServiceClass hizmetleri yöntemini değiştirin;Benim önceki açıklamaya
export class ServiceResult {
error: string;
data: any;
hasError(): boolean {
return error != undefined && error != null; }
static ok(data: any): ServiceResult {
let ret = new ServiceResult();
ret.data = data;
return ret;
}
static err(info: any): ServiceResult {
let ret = new ServiceResult();
ret.error = JSON.stringify(info);
return ret;
}
}
@Injectable()
export class PersonService {
constructor(private http: Http) {}
savePerson(p: Person): Observable<ServiceResult> {
return http.post(url, JSON.stringify(p)).map(ServiceResult.ok);
.catch(ServiceResult.err);
}
}
@Injectable()
export class PersonEffects {
constructor(
private update$: StateUpdates<AppState>,
private personActions: PersonActions,
private svc: PersonService
){
}
@Effects() savePerson$ = this.stateUpdates$.whenAction(PersonActions.SAVE_PERSON)
.map<Person>(toPayload)
.switchMap(person => this.personService.save(person))
.map(res => {
if (res.hasError()) {
return personActions.saveErrAction(res.error);
} else {
return personActions.saveOkAction(res.data);
}
});
@Injectable()
export class PersonActions {
static SAVE_OK_ACTION = "Save OK";
saveOkAction(p: Person): Action {
return {type: PersonActions.SAVE_OK_ACTION,
payload: p};
}
... ...
}
Bir düzeltme: Aynı eylem türüne tepki Etkisi-sınıf ve Redüktör-sınıfı hem varsa Etkisi-Class ve Redüktör-Class, Redüktör sınıf sonra Effect ilk tepki ve edecek -sınıf. İşte bir örnek: Bir bileşenin, bir kez tıklandığında, effectChainReducer
ve ClientEffects.chainEffects$
tarafından işlenecek olan this.store.dispatch(this.clientActions.effectChain(1));
adı verilen bir düğmesi vardır; bu, yükü 1'den 2'ye çıkarır; this.clientActions.effectChain(2)
sonra yük 2'den 3'e kadar artar 2 ve daha sonra ClientEffects.chainEffects$
, this.clientActions.effectChain(3)
, ..., bu 10 daha büyük olduğu kadar, ClientEffects.chainEffects$
this.clientActions.endEffectChain()
yayar değişikliği yayarlar = hangi effectChainReducer
tarafından ele: başka bir işlem yayan 500 ms beklemek Mağaza durumu effectChainReducer
aracılığıyla 1000'e, nihayet burada durur. seviyesinde 51 redüktör zinciri:
istemci reducer.ts: çıkış gibi olmalıdır
:
export interface AppState {
... ...
chainLevel: number;
}
// In NgModule decorator
@NgModule({
imports: [...,
StoreModule.provideStore({
... ...
chainLevel: effectChainReducer
}, ...],
...
providers: [... runEffects(ClientEffects) ],
...
})
export class AppModule {}
export class ClientActions {
... ...
static EFFECT_CHAIN = "Chain Effect";
effectChain(idx: number): Action {
return {
type: ClientActions.EFFECT_CHAIN,
payload: idx
};
}
static END_EFFECT_CHAIN = "End Chain Effect";
endEffectChain(): Action {
return {
type: ClientActions.END_EFFECT_CHAIN,
};
}
static RESET_EFFECT_CHAIN = "Reset Chain Effect";
resetEffectChain(idx: number = 0): Action {
return {
type: ClientActions.RESET_EFFECT_CHAIN,
payload: idx
};
}
export class ClientEffects {
... ...
@Effect()
chainEffects$ = this.update$.whenAction(ClientActions.EFFECT_CHAIN)
.map<number>(toPayload)
.map(l => {
console.log(`effect chain are at level: ${l}`)
return l + 1;
})
.delay(500)
.map(l => {
if (l > 10) {
return this.clientActions.endEffectChain();
} else {
return this.clientActions.effectChain(l);
}
});
}
// client-reducer.ts file
export const effectChainReducer = (state: any = 0, {type, payload}) => {
switch (type) {
case ClientActions.EFFECT_CHAIN:
console.log("reducer chain are at level: " + payload);
return payload;
case ClientActions.RESET_EFFECT_CHAIN:
console.log("reset chain level to: " + payload);
return payload;
case ClientActions.END_EFFECT_CHAIN:
return 1000;
default:
return state;
}
}
çalıştırmak için yukarıdaki kodu yapmak, çıkış gibi olmalıdır 1
istemci effects.ts: seviyesinde 72 etkisi zinciri: 2
istemci effects.ts: seviyesinde 51 redüktör zincir: 1
istemci reducer.ts: 72 etki zinciri düzeyde 2
istemci reducer.ts: ... ... 3
istemci reducer.ts: seviyesinde 72 etkisi zincir: 3
istemci effects.ts: 51 redüktör zincir seviyesinde 51 azaltıcı zincir düzeyde: 10
istemci effects.ts: 72 etki zinciri düzeyde: 10
Bu azaltıcı etkileri önce ilk olarak çalışır gösterir etkisi sınıfı bir post-önleme, pre-önleme olduğunu. Akış şemasına bakınız:
Ngrx mağazasının ve efektlerinin bu yaratıcılarından https://www.youtube.com/watch?v=cyaAhXHhxgk adresindeki videoyu izledim. Ngrx efektlerinin doğru bir şekilde açıklandığı benim için ilk kez. Öyleyse lütfen kontrol edin Eğer hala kafanızı sarmak gerekiyorsa. – trungk18