2017-04-20 11 views
8

Wewnątrz mojego projektu Angular (4 + Dokument) użyłem już przygotowanego komponentu (nie ma to znaczenia, ponieważ sposób rozszerzenia ControlValueAccessor jest taki sam, ale jeśli to ma znaczenie, użyłem ng2-select), które chciałem ze względu na lepszy UX i ładniejszy wygląd. Po włączeniu go do mojego komponentu formularza, który używa formularzy reaktywnych, zobaczyłem, że po przesłaniu wracam (dla formantu) obiekt oparty na klasie SelectItem, która jest używana wewnątrz przygotowanego komponentu, do zarządzania wybranymi przedmiotami - takimi jak:Angular 4 ControlValueAccessor wartości przy zgłaszaniu i zmianach wartości zdarzeń

// console.log(this.myForm.values) 
Object { 
    txtInput: 'value', 
    // ... 
    preparedComponent: SelectItem { // <-- 
     id: '1', 
     text: 'Chosen item' 
    } 
} 

Ponieważ składnik jest w zasadzie tylko zamiennik standardowego wyboru sterowania, spodziewałem się, że będę wracać identyfikator wybranej pozycji utworzone jako ciąg - tak:

// console.log(this.myForm.values) 
Object { 
    txtInput: 'value', 
    // ... 
    preparedComponent: '1' // <-- 
} 

Po I dowiedziałem się, czym właściwie jest ControlValueAccessor i jak to działa, zmieniłem sposób, który powraca po przesłaniu (lub lepiej powiedzieć o zmianie pozycji - onChange) do identyfikatora przedmiotu. Problem został rozwiązany przez pewien czas, dopóki nie rozpocząłem ustawiania wstępnie zdefiniowanych wartości (przy edycji instancji), aby utworzyć formanty zamiast pustych wartości (na dodawanie instancji). Uznałem, że tym razem writeValue przypisuje wartości do formowania w oparciu o to, co jest wysyłane do niego jako predefiniowana wartość. Tak więc w przypadku, gdy wysłałem wcześniej zdefiniowaną wartość jako ...

Object { 
    id: '1', 
    text: 'Chosen item' 
} 

... była to również wartość w Object przy przesyłaniu, tak jak w pierwszym przykładzie kodu powyżej. Zrobiłem obejście tego problemu, aby przypisać poprawną wartość do kontroli z onChange zaraz po wywołaniu writeValue. To był naprawdę niewłaściwy sposób to zrobić, ale nie mogę zorientowali się, że coś innego niż:

public writeValue(val: any): void { 
    this.selectedItems = val; // val is for example Object { id: '1', text: 'Chosen item' } 

    setTimeout(() => { 
     this.onChange(val.id); // just an example of emitted value, there should be some checks val is object and id exists in real code 
    }, 0); 
} 

A że pracował również w miarę nie zacząć subskrybować tworzą kontrola na valueChanges. Problem polega na tym, że przy każdorazowym przypisywaniu wstępnie zdefiniowanej wartości do przygotowanego komponentu istnieje również emisja wartości użytej po przesłaniu, z powrotem do subskrybentów, nawet przy użyciu { emitEvent: false }.

To są rzeczy, które chcę w jakiś sposób obsłużyć przygotowanym komponentem, ponieważ chcę uzyskać odpowiednią wartość z kontroli przy przesyłaniu i nie chcę zmiany emisji dla abonentów, dopóki wartość nie zostanie naprawdę zmieniona, a nie tylko zastąpione jako wartość predefiniowana. Każdy pomysł, jak sobie z tym poradzić, zostanie doceniony.

+0

Przeszedłem dokładnie ten proces. Nie jestem do końca pewien, czy rozumiem dokładnie ten problem. – Amit

+0

Problem dotyczy głównie 'onChanges' wewnątrz' writeValue'. Po pierwsze dlatego, że 'setTimeout', który jest naprawdę ostatnią rzeczą, którą robiłbym jako obejście, a po drugie dlatego, że emituje zdarzenie change po zainicjowaniu z predefiniowaną wartością. Idealnie wartości wejściowe i wyjściowe byłyby rozdzielone - Input zaakceptowałby obiekt, a Output zwrócił identyfikator wybranego obiektu. W związku z tym nie ma potrzeby wprowadzania zmian na wartość init, ponieważ nie jest to oczekiwane zachowanie domyślne (w przypadku '{emitEvent: false}' na 'setValue' kontroli lub formularza' reset' Angular nie emituje zdarzenia przy wartości przełączania). – user1257255

+1

Jestem przekonany, że to bardziej problem, ponieważ jesteś (i ja też) "walczę" z ng2-select (który jest ledwo utrzymany) – Amit

Odpowiedz

2

Nie jestem pewien, czy zrozumiałem to poprawnie, ale chciałbym spróbować.

chcesz komponentu wejście się z następujących kategorii:

{ 
    id: string, 
    text: string 
} 

Ale chcesz komponentu wyjście być tylko identyfikator ciąg wybranego elementu.

Twierdzę, że ten przepływ danych jest nieco niezręczny. Podczas inicjowania numer preparedComponent to object, ale po wybraniu elementu należy go zamienić na string. Oprócz kwestii bezpieczeństwa typu, po prostu wydaje się to nieintuicyjne.

Dla porównania, wyobraź sobie, robi to z ngModel i wprowadzania tekstu:

<input type="text" [(ngModel)]="preparedComponent" /> 

można oczekiwać, aby przejść w obiekcie i wrócić ciąg, gdy użytkownik wpisze? Prawdopodobnie nie.


Ale załóżmy, że tak musi być z jakiegoś powodu. Kontynuowałeś modyfikację ng2-select implementacji ng2-select implementacji ControlValueAccessor lub tworzenie własnego komponentu, który implementuje ControlValueAccessor i owija ng2-select. (Nie wiem, który z nich robiłeś w swoim pytaniu). Teraz zadzwoń pod numer this.onChange(val.id) po wybraniu elementu, który aktualizuje komponent macierzysty za pomocą identyfikatora ciągu.

Następnie zauważyłeś oddzielny problem, który powoduje, że ng2-select wywołuje zdarzenie zmiany podczas inicjalizacji, jeśli wstępnie zdefiniujesz wybraną wartość. Jest to dziwne, ponieważ użytkownik nie wchodził jeszcze w interakcję z kontrolą wyboru. Więc spróbowałeś zainicjować preparedComponent dzwoniąc pod numer setValue na formularzu kontrolnym z emitEvent: false, ale domyślam się, że to nie działało, ponieważ chociaż mogło to uniemożliwić wywołanie zdarzenia zmiany podczas inicjowania danych, ng2-select dokonało drugiej zmiany w Twoich danych natychmiast.

Więc zamiast tego obejść ten czekając, aż pierwsza aktualizacja przez ng2-select aktualizuje składnik nadrzędny do nowego obiektu, a następnie (przy użyciu setTimeout) zastąpisz go z identyfikatorem ciągu rzeczywiście chcesz. Dzięki temu preparedComponent jest na bieżąco z ciągiem znaków, ale nie rozwiązuje problemu zdarzenia zmiany, nawet jeśli użytkownik nie wchodził w interakcję z formantem. (Nawiasem mówiąc, jeśli pakujesz ng2-select z własnym komponentem, nie jestem pewien, dlaczego musiałbyś wykonać tę część, ponieważ nie miałbyś już innej funkcji, która zadzwoni pod numer this.onChange(val.id), gdy ng2-select poinformuje Cię o nowym wybrany element, jak już wspomniałem w poprzednim akapicie)


Ponieważ nie mogę powiedzieć, czy modyfikowania ng2-select bezpośrednio lub owijając go z własnego komponentu:

przypadku modyfikowania ng2-select, dlaczego nie znaleźć metody writeValue i usunąć kod emitujący zdarzenie ? W ten sposób aktualizuje DOM podczas predefiniowania wartości, ale nie zastępuje wartości.

Jeśli owijania ng2-select z własnego komponentu, dlaczego nie wystarczy zmodyfikować metodę writeValue przechowywać flagę jeśli ng2-select ma zamiar wystrzelić imprezę, więc można je zignorować:

private: ignoreSelectedEvent = false; 

public writeValue(val: any): void { 
    this.ignoreSelectedEvent = true; 

    // Update ng2-select, which will cause an event to fire 
} 

, a następnie w Twój funkcja, która reaguje na nowego wybranego elementu:

if (this.ignoreSelectedEvent) { 
    this.ignoreSelectedEvent = false; 

    return; 
} 

// Update the model with the string ID 

this.onChange(val.id); 
0

Zasadniczo pytanie pytaniem, czy jest możliwe, aby wysłać różne typy wartości do elementu sterującego jako wartości początkowej, która jest obsługiwana przez write Metoda wartości. Ten powinien następnie przekonwertować wartość na identyfikator ciągu lub tablicę identyfikatorów ciągów, w zależności od tego, który typ zostanie wybrany - pojedyncza wartość lub wiele wartości. Jak powiedział Frank w swojej odpowiedzi, jest to trochę niezręczne, aby zadzwonić na Change z opcją setTimeout.

Rozwiązaniem jest ustawienie wartości początkowej na to, co składnik kontrolny zwróci w procesie onChange po przekonwertowaniu obiektu na ID/tablicę identyfikatorów. Sam komponent musi wybrać odpowiedni obiekt ze wszystkich dostępnych elementów w zależności od ID/ID, które są do niego wysyłane.

Powiązane problemy