0

I have two models in Ember where one is a collection and the other is book. Collection model has a "hasMany" relationship with "book" and "book" "belongsTo" "collection". So what i want to do is have a form with both models in and store them at the same time.

Collection Model

// collection Model
export default DS.Model.extend({
    name: DS.attr('string'),
    city: DS.attr('string'),
    books: DS.hasMany('book', { async: true })
});

The book model

//book Model
export default DS.Model.extend({
    title: DS.attr('string'),
    description: DS.attr('string'),
    collection: DS.belongsTo('collection', {async: true})
});

The form

//Collection View
<div class="row">
    <div class="col-sm-12">
        <div class='panel panel-default'>
            <div class='panel-body'>
                <form>
                    <fieldset>
                        <legend>
                            Collection
                        </legend>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="name" class="col-sm-2">Name</label>
                                <div class="col-sm-10">
                                    {{input id="name" type="text" class="form-control" value=collection.name}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="city" class="col-sm-2">city</label>
                                <div class="col-sm-10">
                                    {{input id="city" type="text" class="form-control" value=collection.city}}
                                </div>
                            </div>
                        </div>
                    </fieldset>
                    <fieldset>
                        <legend>
                            books
                        </legend>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="title1" class="col-sm-2">title</label>
                                <div class="col-sm-10">
                                    {{input id="title1" type="text" class="form-control" value=book.title1}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="description1" class="col-sm-2">description</label>
                                <div class="col-sm-10">
                                    {{input id="description1" type="text" class="form-control" value=book.description1}}
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="title2" class="col-sm-2">title</label>
                                <div class="col-sm-10">
                                    {{input id="title2" type="text" class="form-control" value=book.title2}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="description2" class="col-sm-2">description</label>
                                <div class="col-sm-10">
                                    {{input id="description2" type="text" class="form-control" value=book.description2}}
                                </div>
                            </div>
                        </div>
                    </fieldset>
                    <div class="row">
                        <div class="col-sm-12">
                            <button {{action  'addCollection'}} class="btn btn-primary">New Collection</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

The controller:

actions:{
    addCollection: function(){
        var name = this.get('collection.name');
        var city = this.get('collection.city');


        var title1 = this.get('book.title1');
        var description1 = this.get('book.description1');
        var title2 = this.get('book.title2');
        var description2 = this.get('book.description2');


        var newCollection = this.store.createRecord('collection', {
            name: name,
            city: city,
        });
    },

As you can see I'm trying to obtain 2 sets of books for this collection, but I'm having trouble on finding the correct procedure to store items to these models. I guess I may have to store the collection model first and then push the individual books to the collection. Is this correct? How would I go about it?

jsg
  • 1,224
  • 7
  • 22
  • 44

1 Answers1

1

If the book is a record already you can simply push it onto the new collection. You don't don't need to assign anything to variables since your template is already bound to the record.

As an improvement I'd suggest creating the collection before the add as well. Either within your model, or on controller initiation.

So the action could be as simple as this.

actions:{

  addCollection: function(){
    // newCollection and Book need to be records
    this.get("newCollection").pushObject(this.get('book'));
  },
}

Note: the createRecord will not persist until you call .save() but you do not need to call .save() to create a relationship.

Forgive me for not noticing the multiple new books, here is a more pertinent example.

// template.hbs

<div class="row">
    <div class="col-sm-12">
        <div class='panel panel-default'>
            <div class='panel-body'>
                <form>
                    <fieldset>
                        <legend>
                            Collection
                        </legend>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="name" class="col-sm-2">Name</label>
                                <div class="col-sm-10">
                                    {{input id="name" type="text" class="form-control" value=model.newCollection.name}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="city" class="col-sm-2">city</label>
                                <div class="col-sm-10">
                                    {{input id="city" type="text" class="form-control" value=model.newCollection.city}}
                                </div>
                            </div>
                        </div>
                    </fieldset>
                    <fieldset>
                        <legend>
                            books
                        </legend>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="title1" class="col-sm-2">title</label>
                                <div class="col-sm-10">
                                    {{input id="title1" type="text" class="form-control" value=model.book1.title}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="description1" class="col-sm-2">description</label>
                                <div class="col-sm-10">
                                    {{input id="description1" type="text" class="form-control" value=model.book1.description}}
                                </div>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-sm-6">
                                <label for="title2" class="col-sm-2">title</label>
                                <div class="col-sm-10">
                                    {{input id="title2" type="text" class="form-control" value=model.book2.title}}
                                </div>
                            </div>
                            <div class="col-sm-6">
                                <label for="description2" class="col-sm-2">description</label>
                                <div class="col-sm-10">
                                    {{input id="description2" type="text" class="form-control" value=model.book2.description}}
                                </div>
                            </div>
                        </div>
                    </fieldset>
                    <div class="row">
                        <div class="col-sm-12">
                            <button {{action  'addCollection' model}} class="btn btn-primary">New Collection</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

// route.js

model () {
  return Ember.RSVP.hash({
    newCollection: this.get('store').createRecord('collection'),
    book1: this.get('store').createRecord('book'),
    book2: this.get('store').createRecord('book')
  })
}

// controller.js

actions:{
  addCollection(model) {
    model.newCollection.pushObject(model.book1);
    model.newCollection.pushObject(model.book2);
  },
}

Notice I've used the model to create the records before hand and it's being passed into the action. You still may need to save for these changes to persist.

BrandonW
  • 268
  • 3
  • 11
  • How about if the book is created in the same instance, would you suggest to store the book first and then create it as a variable to then push to the collection? As there multiple books being saved at once would it be `this.get("book")` – jsg Sep 20 '17 at 15:30
  • You can create both simultaneously, then either set the collection to the book, or push the book onto the collection. Documentation can be found here. https://guides.emberjs.com/v2.15.0/models/relationships/#toc_creating-records @jsg – BrandonW Sep 20 '17 at 15:35
  • If each book is saved separately, then you should create the collection before hand and push both books to it. You could also set the collection to each book as it's being saved. It may be easier to pass in the book to the action via the template if it's adding a book each time. I'll update my answer with a scenario for multiple books at the same time. – BrandonW Sep 20 '17 at 15:38
  • I get this error when I attempt to type data in each field `Cannot call set with 'name' on an undefined object.` – jsg Sep 21 '17 at 12:44
  • I can't seem to find the reason for this issue. – jsg Sep 21 '17 at 13:37
  • Did you update your template to use model.book1 etc...? Where are you calling set? – BrandonW Sep 21 '17 at 13:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155002/discussion-between-brandonw-and-jsg). – BrandonW Sep 21 '17 at 13:41
  • Note, the fix to `Cannot call set with 'name' on an undefined object.` was adding `return` before the RSVP. That was my fault for not testing the code I wrote. – BrandonW Sep 21 '17 at 16:15
  • Would the return be implemented after, I've got little knowledge on how to get this to work – jsg Sep 22 '17 at 07:55
  • I've added the change to the model in the answer. @jsg – BrandonW Sep 22 '17 at 13:20