2017-06-29 49 views
3

我有一些動態生成的窗體並向它們傳遞值。我覺得有人必須解決這個問題,否則我錯過了一些顯而易見的事情,但我找不到任何提及。角動態窗體可觀察屬性綁定

因此,例如,我有三個組成部分,父母,孩子,然後這個孩子的孩子。對於名稱,我會去,formComponent,questionComponent,textBoxComponent。這兩個孩子都在使用changeDetection.OnPush。

所以形式部件通過輸入經過一些值下降到questionComponent,有的使用異步管訂閱在商店它們各自的值。

QuestionComponent動態創建不同的組件,然後將它們放置在頁面上,如果他們匹配(這麼多類型的組件,但每個questionComponent只處理一個組件上

一些代碼:

@Input() normalValue 
@Input() asyncPipedValue 
@ViewChild('questionRef', {read: ViewContainerRef}) public questionRef: any; 
private textBoxComponent: ComponentFactory<TextBoxComponent>; 

ngOnInit() { 
let component = 
this.questionRef.createComponent(this.checkboxComponent); 
component.instance.normalValue = this.normalValue; 
component.instance. asyncPipedValue = this. asyncPipedValue; 
} 

這適用於normalValues的所有實例,但不適用於asyncValues。我可以在questionComponent的ngOnChanges中確認值正在更新,但該值不會傳遞給textBoxComponent。

什麼我基本上需要的是異步管道,但不適用於模板。我已經嘗試了多種解決方案來傳遞asyncValues,我試着檢測asyncPipeValue何時發生變化,並在textBoxComponent上觸發changeDetectionRef.markForChanges(),但只有當我將changeDetectionStrategy更改爲正常時,纔會起作用,這會影響性能我從使用ngrx獲得收益。

這似乎是太大的監督,尚未有解決的辦法的,所以我假定這只是我沒有想到的東西。有什麼想法嗎?

+0

你可以提供重擊嗎? – yurzui

+1

你看過BehaviorSubject嗎?您可以讓服務運行邏輯並/或將您的asyncValue修改爲BehaviorSubject,並讓每個組件訂閱該BehaviorSubject的Observable。 –

+0

^爲什麼我們在使用ngrx時需要'BehaviorSubject',我們可以直接存儲在'store'中? – Skeptor

回答

0

這是我對這個問題的看法,考慮到了有限的代碼片段。

首先,提供的示例似乎並不具有任何與ngrx。在這種情況下,預計ngOnInit只運行一次,並且當時this.asyncPipedValue的值爲undefined。因此,如果this.checkboxComponentchangeDetectionChangeDetection.OnPush,則該值不會更新。我建議閱讀one excellent article關於變化檢測和傳遞異步輸入。那篇文章還包含了其他更多關於變化檢測的優秀資源。另外,似乎相同的輸入在組件樹中傳遞兩次,從我的角度來看,這不是一個好的解決方案。

二,另一種方法是使用ngrx,然後你並不需要在所有通過任何異步輸入。特別是,如果兩個組件在組件樹中沒有父子關係,則這種方法很好。在這種情況下,一個組件調度將數據放入Store的操作,另一個組件從Store中訂閱該數據。

export class DataDispatcherCmp { 

    constructor(private store: Store<ApplicationState>) { 
    } 

    onNewData(data: SomeData) { 
     this.store.dispatch(new SetNewDataAction(data)); 
    } 
} 

export class DataConsumerCmp implements OnInit { 

    newData$: Observable<SomeData>; 

    constructor(private store: Store<ApplicationState>) { 
    } 

    ngOnInit() { 
     this.newData$ = this.store.select('someData'); 
    } 
} 

希望這可以幫助或至少提供一些線索。

+0

因此,相同的輸入不會傳遞兩次,而是從父項傳遞給子項,然後必須將子項傳遞給另一個子項。第一個孩子的輸入是用異步管道訂閱的,所以他們會隨着時間的推移而變化(實際上它是第一個孩子的孩子發出一個動作並更改存儲,並傳遞了新的數據)。 –

+0

好的,不知道那裏發生了什麼。需要看到涉及的所有3個組件。 – mikedanylov

0

我相信我已經找到了一個解決方案(從gitter.com/angular通道一些幫助)。

由於值的問題組件可以改變,並觸發它的ngOnChanges觸發,每當ngOnChanges中有事件時,它需要解析事件,並綁定並更改爲動態子組件。

ngOnChanges(event) { 
if (this.component) { 
    _.forEach(event, (value, key) => { 
    if (value && value.currentValue) { 
     this.component.instance[key] = value.currentValue; 
    } 
    }); 
} 
} 

這是所有問題組件,它會重置組件實例變量(如果它們已更改)。迄今爲止最大的問題是,孩子的ngOnChanges不會觸發,所以這不是一個完整的解決方案。我會繼續深入研究它。

+0

您的TextBoxComponent是否暴露了任何角度AbstractControl組件?或者它擴展AbstractControl? –

+0

@MattSugden它不擴展一個abstractControl,我向下傳遞一個formControl實例,該實例綁定到模板上的[formControl] –

1

我做了類似的事情,據此我填寫了來自我的Ngrx商店的數據。我的表格不是動態的,所以我不能100%確定這是否也適用於您。

只需使用setter定義輸入,然後在窗體/表單控件上調用patchValue()或setValue()。根組件保持不變,通過異步管道將數據傳遞到下一個組件。

@Input() set asyncPipedValue(data) { 
    if (data) { 
    this.textBoxComponent.patchValue(data); 
    } 
} 

patchValue()在AbstractControl類上。如果您無法從您的問題組件訪問該文件,那麼您的TextBoxComponent可能會公開一個類似的方法,該方法可以從您的QuestionComponent中調用,執行該控件的更新。

但有一點需要注意,如果您還在窗體/控件中訂閱valueChanges,則可能需要設置第二個參數,以免valueChanges事件立即觸發。

this.textBoxComponent.patchValue(data, { emitEvent: false }); 
在TextBoxComponent

this.myTextBox.valueChanges 
    .debounceTime(a couple of seconds maybe) 
    .distinctUntilChanged() 
    .map(changes => { 
    this.store.dispatch(changes); 
    }) 
    .subscribe(); 

this.textBoxComponent.setValue(...same as above); 

那麼這種做法是相當不錯的了,而且無需有保存/更新按鈕隨處可見。