2016-11-30 2 views
5

Я использую @ ngrx/store для приложения Angular 2.Обновление объекта в ngrx/store

В моем магазине хранится список, скажем, Book объектов. Я хочу обновить поле в одном из этих объектов. У меня также есть Наблюдаемый экземпляр книги, который я ищу для обновления (скажем, selectedBook).

Чтобы сделать обновление, я намерен вызвать редуктор с помощью UpdateBookAction и полезной нагрузки новой книги. Поэтому я делаю глубокую копию существующего объекта Book, подписываясь на selectedBook, а затем вызывая Object.assign().

Но когда я пытаюсь записать в одно из полей копии, я получаю следующую ошибку. (Это случается та же ошибка, я получаю, если я стараюсь писать непосредственно на объект книги в магазине.)

Ошибка

Cannot assign to read only property 'name' of object '#<Object>' at ViewWrappedError.BaseError [as constructor] 

Код

ngOnInit() { 
    this.book$ = this.store.let(fromRoot.getSelectedBook); 
    //... 
} 

someFunction() { 
    //... 
    this.book$.subscribe(book => { 

     let updatedBook = Object.assign({}, book); 
     updatedBook.name = 'something else';   // <--- THIS IS WHAT THROWS 

     let action = new BookUpdateAction(updatedBook); 
     this.store.dispatch(action); 

    } 
} 

Уточнение после Комментариев

Я был в предположении, что у меня может быть действие с полезной нагрузкой, которая была не всем состоянием магазина. (На самом деле это кажется необходимым, нет?) Я уверен, что это так, учитывая документацию.

Действие Я ищу взять что-то вроде этого:

Action = UPDATE, payload = {'id': 1234, 'name': 'something new'} 

Как уже упоминалось, я намерен сделать на этот вызов, как это:

this.store.dispatch(action); 

Предположительно под капотом, ngrx является передавая мое действие редуктору вместе с (неизменяемым) текущим состоянием.

Итак, оттуда все должно работать нормально. Моя логика внутри редуктора не мутирует существующее состояние, оно просто создает новый из существующего состояния и полезной нагрузки, в которой я прошел.

Настоящий вопрос заключается в том, как я могу разумно построить новый " objectToUpdate ", чтобы я мог передать это как полезную нагрузку.

я мог бы сделать что-то вроде этого:

this.book$.subscribe(book => { 

    let updatedBook = new Book(); 
    updatedBook.id = book.id; 
    //set all other fields manually... 
    updatedBook.name = 'something else'; 

    let action = new BookUpdateAction(updatedBook); 
    this.store.dispatch(action); 

} 

Но мы не просто говорим о двух полей здесь ... что, если моя книга имеет несколько полей? Должен ли я вручную создавать с нуля новую книгу каждый раз только для обновления одного поля?

Мое решение состояло в том, чтобы сделать глубокую копию, используя Object.assign({}, book) (и не мутировать старый!), А затем внести обновление только в поле, которое я искал, чтобы коснуться.

+0

Можете ли вы опубликовать определение объекта книги? Похоже, это может быть проблема с свойством write only. –

+0

Да, видя определение книги, было бы полезно. У вас такая же проблема, когда вы делаете все это за один раз? например 'Object.assign ({}, book, {name: 'something else'})'? – rob3c

ответ

5

Идея магазина ngrx состоит в том, чтобы иметь одно и только одно место истины, что означает, что все объекты неизменяемы, и единственный способ изменить что-либо - это воссоздать все как единое целое.Кроме того, вы, вероятно, используете ngrx freeze (https://github.com/codewareio/ngrx-store-freeze), что означает, что все объекты будут созданы только для чтения, поэтому вы не сможете их изменить (это хорошо для разработки, если вы хотите полностью следовать шаблону сокращения). Если вы удалите часть, в которой магазин заморозит объект, вы сможете изменить его, но это не лучшая практика.

Что я предлагаю вам: Используйте наблюдаемый ngrx с асинхронным каналом, чтобы поместить данные (в ваших книгах) в немой компонент, который может только вводить и выводить какое-то событие. Затем внутри немого компонента вы можете «отредактировать» этот объект, сделав его копию, и после того, как вы закончите, вы можете отменить изменения в интеллектуальном компоненте, который подписан на магазин, и позволить ему изменять состояние через магазин (фиксация). Этот способ лучше всего, потому что не очень часто менять целое состояние для действительно небольшого изменения (например, двусторонняя привязка, когда пользователь вводит ...).

Если следовать Redux шаблону, чем вы сможете добавить историю, что означает, что магазин будет держать копии государственных воссозданные в прошлом X, так что вы можете получить UNDO функциональность, проще отлаживать, сроки и т.д.

Ваша проблема заключается в том, что вы непосредственно редактируете свойство вместо воссоздания всего состояния.

+1

Привет, Денко, спасибо, что нашли время ответить. Я добавил разъяснения выше. Я знаю цель сохранения состояния неизменной. Я не пытаюсь мутировать само государство, просто пытаясь сделать его копию, чтобы я мог настроить копию, прежде чем передавать ее в качестве полезной нагрузки. Я думаю, вы на что-то, но с ngrx-store-freeze. Я подозреваю, что глубокая копия приводит к чистому объекту, доступному только для чтения. Это заставляет меня отключить ngrx-store-freeze, но я верю, что продолжу следовать «лучшей практике». Это может быть скорее недостатком ngrx-store-freeze, чем чем-либо. –

4

Мне нужно сделать предположение о реальном сценарии, который испытывает ОП.

Проблема

Это невозможно изменить элемент замороженного объекта. Это ошибка.

Причина

ngrx-store-freeze используется в качестве мета-редуктор, чтобы заморозить любой объект, который входит в магазин. В другом месте, когда объект нужно изменить, делается мелкая копия. Object.assign() не делает глубокая копия. Изменяется член другого объекта, достигнутого от исходного объекта. Этот вторичный объект также заморожен, не дублируется.

Решение

Используйте глубокую копию как cloneDeep() от lodash. Или отправил мешок свойств, который нужно изменить с помощью правильного действия. Обработать изменения на редукторе.

1

Как уже упоминалось - причина вы получаете

Невозможно назначить для чтения «имя» только свойство объекта

потому, что «ngrx-магазин замораживания» замораживает состояние и предотвращает мутируя его.

Object.assign обеспечит новый объект, как вы ожидаете, но он будет копировать свойства государства наряду с собственным определением каждого свойства - такие, как «записываемый» определение (которое «ngrx-магазин заморозки» вероятными наборы на false).

Описывается другой подход in this answer и объясняется, как клонирование объектов с помощью JSON.parse (JSON.stringify (yourObject)) является самым быстрым, но этот подход имеет недостатки, если вы сохраняете даты или методы и т. Д. В вашем состоянии.

Использование lodash's cloneDeep является, вероятно, лучшим выбором для глубокого клонирования состояния.

Смежные вопросы