2017-02-19 19 views
6

Мой план состоит в том, чтобы сохранить значения формы в моем магазине ngrx, чтобы мои пользователи могли перемещаться по сайту и вернуться к форме, если они того пожелают. Идея заключалась бы в том, что значения формы будут заселяться из магазина с помощью наблюдаемого.Как инициализировать форму Reactive Angular2 с помощью Observable?

вот как я это делаю в данный момент:

constructor(private store: Store<AppState>, private fb: FormBuilder) { 
    this.images = images; 
    this.recipe$ = store.select(recipeBuilderSelector); 
    this.recipe$.subscribe(recipe => this.recipe = recipe); // console.log() => undefined 
    this.recipeForm = fb.group({ 
     foodName: [this.recipe.name], // also tried with an OR: (this.recipe.name || '') 
     description: [this.recipe.description] 
    }) 
    } 

Магазин присваивается начальное значение, которое я видел, проходит через мою функцию селектора правильно, но к тому времени моя форма создана, я дон Не думаю, что ценность вернулась. Поэтому this.recipe по-прежнему не определено.

Это неправильный подход, или я могу каким-то образом гарантировать, что наблюдаемое возвращается до создания формы?

ответ

3

Я могу думать о двух вариантах ...

Вариант 1:

Используйте *ngIf на HTML, который отображает вид что-то вроде

<form *ngIf="this.recipe">...</form> 

Вариант 2: Используйте шаблон async в свой шаблон и создайте свою модель:

компонент

model: Observable<FormGroup>;  
... 
this.model = store.select(recipeBuilderSelector) 
    .startWith(someDefaultValue) 
    .map((recipe: Recipe) => { 
     return fb.group({ 
      foodName: [recipe.name], 
      description: [recipe.description] 
     }) 
    }) 

шаблон

<app-my-form [model]="(model | async)"></app-my-form> 

Вы должны рассмотреть, как обрабатывать обновления в хранилище и к текущей модели.

+1

Мне не удалось заставить его работать таким образом. 'startsWith() не существует в типе Observable'. эта функция, по-видимому, существует только для строк. –

+0

Извините, метод должен быть 'startWith'. https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/startwith.md – shusson

+0

'.startWith()' работал на первой итерации, но когда второй (режим dev) запускался, он снова не был определен. Я получил его для работы, удалив 'startWith()' и изменив свой селектор на: 'return _.cloneDeep (state.recipebuilder) || someDefaultValue; ' –

5

Хотя добавление еще один слой может показаться более сложным, гораздо легче иметь дело с наблюдаемыми путем разделения одного компонента на две части: в контейнер компонент и презентационный компонент.

Контейнерный компонент имеет дело только с наблюдаемыми, а не с презентацией. Данные из любых наблюдаемых передаются компоненте презентации с помощью @Input свойств и async трубы используются:

@Component({ 
    selector: "recipe-container", 
    template: `<recipe-component [recipe]="recipe$ | async"></recipe-component>` 
}) 
export class RecipeContainer { 

    public recipe$: Observable<any>; 

    constructor(private store: Store<AppState>) { 
    this.recipe$ = store.select(recipeBuilderSelector); 
    } 
} 

Презентационный компонент принимает простые свойства и не приходится иметь дело с наблюдаемыми:

@Component({ 
    changeDetection: ChangeDetectionStrategy.OnPush, 
    selector: "recipe-component", 
    template: `...` 
}) 
export class RecipeComponent { 

    public recipeForm: FormGroup; 

    constructor(private formBuilder: FormBuilder) { 
    this.recipeForm = this.formBuilder.group({ 
     foodName: [""], 
     description: [""] 
    }); 
    } 

    @Input() set recipe(value: any) { 
    this.recipeForm.patchValue({ 
     foodName: value.name, 
     description: value.description 
    }); 
    } 
} 

Понятие использования контейнерных и презентационных компонентов является общей концепцией Redux и объясняется в Presentational and Container Components.

+0

Я не мог так работать для меня. Каким-то образом форма строилась до того, как селектор был вызван –

+0

Да, я вижу вашу мысль. Я должен был создать форму в конструкторе и применить изменения только при изменении '@ Input'. Я обновил ответ. Каким бы способом вы решили это сделать, я бы посоветовал вам разобраться в разделении вещей на контейнерные и презентационные компоненты, поскольку это облегчает жизнь. – cartant

+0

любая критика или вариант shusson 2 ответ? Я смог сделать это без контейнера, используя '[formGroup] =« recipe $ | async »' –