0
votes

I'm not sure what I'm missing here but after sending a save() request to the API, I'm finding that the list within the template is not updating.

I'm using the JSONAPI adapter, and after the create has been completed, it returns a corresponding post-create payload response containing all the new information, which I can see is in fact updating the note model in the Ember Inspector correctly with the information from the payload.

I've also tried to add in lines like:

get(this, 'notes').pushObject(note);

and alternatively:

get(this, 'notes').addObject(note._internalModel);

which I found from SO and Ember Forums, but they both return a warning:

The result of a server query (on testapp@model:note:) is immutable.

From what I've read I thought the JSONAPI adapter should be handling this (which it looks like it is from within Ember Inspector) but I just can't see any changes reflected in the template.

I have the following:

route.js

import Ember from 'ember';

export default Ember.Route.extend({
    model(params) {

        return Ember.RSVP.hash({
            contact:    this.store.findRecord('contact', params.contact_id),
            notes:      this.store.query('note', {filter: {contact_id: params.contact_id}}),
            note:       this.store.createRecord('note'),
        });
    },

    setupController(controller, model) {
        controller.set('contact', model.contact);
        controller.set('notes', model.notes);
        controller.set('note', model.note);

    }
});

controller.js

import Ember from 'ember';

const {Controller, set, get} = Ember;

export default Controller.extend({

  actions: {
    // Note CRUD.. 
    store() {
        let note = get(this, 'note');
        set(note, 'contact', get(this, 'contact'));
        note.save();
        this.send('reset');
    },
    reset() {
        set(this, 'note', this.store.createRecord('note'));
    }

  },

});

template.hbs

{{#em-form model=note action="store"}}
  {{em-text rows="3" property="content" label="Note" placeholder="Enter a note..."}}
  <button type="button" class="btn btn-default" {{action "reset"}}>Reset</button>
{{/em-form}}

<ul>
  {{#each notes as |note|}}
    <li>{{note.content}}</li>
  {{/each}}
</ul>

Also, not sure if this is relevant but in Ember Inspector, even though the model has populated with the new ID and timestamps etc from the server, I noticed this difference between the new item and the existing ones?

New: <testapp@model:note::ember1376:null> (note the null)

Existing: <testapp@model:note::ember876:48> ..etc

3

3 Answers

1
votes

The problem is that you are making both the post request and the query request at the same time. The notes array on your model hash will grab the note records at the time of querying, which will not include your newly created note.

Furthermore, the notes array will not update itself to include your new note once it's created. If you want this to happen, the functionality you're looking for is found within ember data's filter function http://emberjs.com/api/data/classes/DS.Store.html#method_filter

return Ember.RSVP.hash({
        contact:    this.store.findRecord('contact', params.contact_id),
        notes:      this.store.filter('note', {contact_id: params.contact_id}, function(note) {

                       var belongsToContact = note.get('contact_id', params.contact_id);
                       var hasAnId = !note.get('isNew');

                       return isNotNew && belongsToContact;
                    }),
        note:       this.store.createRecord('note'),
    });

The filter function will make both a query request for fresh data and will consult the local ember data store for records that it already has loaded. Because of this, you must provide a client side filter function to filter your local records.

0
votes

I'm thinking rename notes to notesQuery (in the route, leaves as notes in the template) and then have a computed that watches notesQuery and something on note like note.isNew, so maybe:

notes: Ember.computed('notesQuery', 'note.isNew', function() {
  let result = [];
  result.pushObjects(this.get('notesQuery'));
  if (!this.get('note.isNew')) {
    result.addObject(this.get('note'));
  }
  return result;
})

I'm not sure on isNew, you may want to use one of your own properties, or something else listed in http://emberjs.com/api/data/classes/DS.Model.html.

0
votes

Accepted answer led me to the solution:

This answer led me to the solution, although the this.store.filter() method is depreciated, I was able to replicate it with:

route.js

model(params) {

    return RSVP.hash({
        contact:    this.store.findRecord('contact', params.contact_id),
        notes:      this.store.query('note', {filter: {contact_id: params.contact_id}}),

    });
},
setupController(controller, model) {

    controller.set('contact', model.contact);
    controller.set('note', this.store.createRecord('note'));

},

controller.js

allNotes: computed( function(){
    return this.store.peekAll('note');
}),

notes:  function() {
    return get(this, 'allNotes').filterBy('isNew', false);
}.property('[email protected]'),

so when the new item is created it is no longer 'isNew'. I'm not sure if this is foolproof (ie if a note somehow gets pushed to the store with a relationship to a different contact the peekAll() function will pick it up, but I figure this will do for now. Thanks.