Подтвердить что ты не робот

Ember.js & Ember Data: как создать запись с hasMany отношениями, когда родитель и ребенок еще не существуют

Как представляется, с большинством всего, что я делаю с данными ember-data, простой тестовый пример очень прост, но что-то "реальное" быстро разваливается из-за моего недостатка понимания (и, я думаю, документации). В простейшем случае у меня нет проблем с созданием новой записи и ее сохранением в моем REST API. Я воспользуюсь примером корзины с преобладанием фруктов и начну с простого сохранения нового куска fruit.

// Here is our fruit model
module.exports = App.Fruit= DS.Model.extend({
    name: attr('string'),   
    color: attr('string')
});

// This is easy to create and save
var fruit = this.store.createRecord('fruit', {
    name: 'apple',
    color: 'red'
});

fruit.save().then(successCallback, errorCallback);

Хорошо, здесь у меня нет проблем. Концептуально имеет смысл, код чист, и все выглядит великолепно! Предположим теперь, что у нас есть модель basket и что модель basket может содержать много fruit. Кроме того, пользователь может создать новый fruit и добавить его в новый basket, который еще не был создан за один шаг от пользовательского интерфейса.

Итак, вот новые модели:

module.exports = App.Fruit= DS.Model.extend({
    name: attr('string'),   
    color: attr('string')
    basket: DS.belongsTo('basket')
});

module.exports = App.Basket = DS.Model.extend({
    fruits: DS.hasMany('fruit', {async:true})
});

Учитывая вышеизложенное, допустим, что пользовательские входы в двух новых фруктах, которые в настоящее время не существуют в магазине или в backend REST API, добавляет их в свою новую корзину (которая также еще не существует), а затем попадает 'сохранить'.

Как создать эту запись с помощью Ember Data? Я надеялся, что вы сможете сделать что-то вроде этого:

var newFruits = Ember.A();

newFruits.pushObject(this.store.createRecord('fruit', {
    name: 'orange',
    color: 'orange'
}));

newFruits.pushObject(this.store.createRecord('fruit', {
    name: 'banana',
    color: 'yellow'
}));

var basket = this.store.createRecord('basket', {
    fruits: newFruits
});

basket.save().then(successCallback, errorCallback);

К сожалению, это не работает. Когда я смотрю данные запроса POST в инспекторе Chrome, свойство корзины fruits всегда пусто. В основном, запрос заканчивается следующим образом:

{
    basket: {
       fruits: []
    }
}

Использую ли я правильный шаблон для создания записи "корзины"? Я надеюсь, что мне не нужно в конечном итоге экономить каждый кусочек фруктов, прежде чем сохранить корзину, поскольку кажется бесполезным отправлять 3 запроса POST, когда 1 будет достаточно. Кроме того, что происходит, когда пользователь хочет создать 10 кусочков фруктов? Это будет 11 запросов POST.

Существует ли стандартная практика для выполнения вышеуказанного? По существу, как вы создаете новую запись с hasMany отношениями (корзины), где записи в отношениях hasMany также являются новыми (плод).

Спасибо!

Вот версии, которые я использую:

------------------------------- 
Ember      : 1.3.2+pre.25108e91 
Ember Data : 1.0.0-beta.7+canary.238bb5ce 
Handlebars : 1.3.0 
jQuery     : 2.0.3 
------------------------------- 
4b9b3361

Ответ 1

Я просто понимаю это сам, но я просто сделал это так, и он работает...

Для начала, когда вы this.store.createRecord('basket', {}), он должен иметь пустой массив для своего свойства fruits. Итак, сделайте следующее:

var basket = this.store.createRecord('basket', {});

basket.get('fruits').addObject(this.store.createRecord('fruit', {
    name: 'orange',
    color: 'orange'
}));

basket.get('fruits').addObject(this.store.createRecord('fruit', {
    name: 'banana',
    color: 'yellow'
}));

Кстати, пустой массив fruits на самом деле является DS.PromiseArray (который может мгновенно разрешить DS.ManyArray?), более конкретный подкласс Ember.Array, так что это может иметь значение.

Однако я обнаружил, что свойство fruits не будет сериализоваться вообще - я использую JsonApiAdapter, хотя это может быть причиной. Или может быть, это ожидаемое поведение, когда дочерние объекты hasMany еще не были сохранены. Во всяком случае, я просто сделал что-то вроде этого в моем действии action.save контроллера:

basket.save().then(function(){
    var promises = Ember.A();
    basket.get('fruits').forEach(function(item){
        promises.push(item.save());
    });
    Ember.RSVP.Promise.all(promises).then(function(resolvedPromises){
        alert('All saved!');
    });
});

Как-то волшебно, когда все это было сделано, я закончил с сохраненной "корзиной" с сгенерированным id и сохраненным "плодом" с сгенерированным id s. И все свойства фруктов belongsTo указывали назад на корзину, а корзина hasMany содержала все фрукты... несмотря на то, что, когда я save() d в корзине, ответ от моего backend возвращал пустой массив для моего свойства fruits, которое, я уверен, испортило бы все, но почему-то этого не делало.

Конечно, это были не мои фактические модели, но параллель была очень близка. Я на Ember Data 1.0.0-beta5 и Ember 1.3.1. Я не использую запасной адаптер REST, но JsonApiAdapter - это довольно тонкая оболочка, поэтому попробуйте этот подход и посмотрите, работает ли он!

Изменить

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

Кроме того, возникла другая потенциальная проблема, с которой я столкнулся: когда (в этом примере) создается basket, свойство fruits (которое является DS.PromiseArray) может быть еще не полностью готово. Поэтому вам может понадобиться сделать это следующим образом:

var basket = this.store.createRecord('basket', {});

basket.get('fruits').then(function(){
    // Now the fruits array is available
    basket.get('fruits').addObject(this.store.createRecord('fruit', { /* ... */ }));
});

Ответ 2

В случае, если кто-либо еще дожидается до этого, текущая документация указывает, что вы можете просто вызвать createRecord() следующим образом:

post.get('comments').createRecord({})

который работает, даже если post - пустой объект.