简体   繁体   中英

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)

The content I have bound has the following custom find method defined

App.Foo = DS.Model.extend({
    name: DS.attr('string')
}).reopenClass({
    records: [],
    all: function() {
        return this.records;
    },
    find: function() {                                                              
        var self = this;
        $.getJSON('/api/foo/', function(response) {
            response.forEach(function(data) {
              //say I want to kill everything in the array here for some strange reason...
              self.records = [];
              //the template still shows the record ... not an empty list ?
            }, this);
        });
        return this.records;
    }
});

My other model uses this directly

App.Related = DS.Model.extend({
  listings: function() {
    return App.Foo.find();
  }.property()
});

Now inside my template

{{#each foo in related.listings}}
  {{foo.name}}<br />
{{/each}}

The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)

add: function(record) {
    this.records.addObject(App.Foo.createRecord(record));
},

and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.

Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?

self.records.splice(i, 1);

Even when I query the client manually after the splice or empty work it returns 0

console.log(App.Foo.all().get('length'));

Initially I see records, but then I see they are gone (yet the html doesn't change)

I understood your question this way, that the following remark is the point your are struggling with:

  response.forEach(function(data) {
          //say I want to kill everything in the array here for some strange reason...
          self.records = [];
          //the template still shows the record ... not an empty list ?
        }, this);

You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:

App.Related = DS.Model.extend({
  listings: function() {
    return App.Foo.find();
  }.property("App.Foo.records.@each")
});

Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.

One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:

var a = []; // is the same as -> var a = Ember.A();

Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.

You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.

In your case, there is an additional problem, which is that in find() , you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.

Your model should look something like this:

App.Foo = DS.Model.extend({
    name: DS.attr('string')
}).reopenClass({
    records: [],
    all: function() {
        // can't use 'this.get(...)' within a class method
        return Ember.get(this, 'records');
    },
    findAll: function() {
        var records = Ember.get(this, 'records');
        $.getJSON('/api/foo/', function(response) {
            records.clear();

            // in this case my json has a 'foos' root 
            response.foos.forEach(function(json) {
                this.add(json);
            }, this);
        }, this);

        // this gets updated asynchronously
        return records;
    },
    add: function(json) {
        // in order to access the store within a 
        // class method, I cached it at App.store
        var store = App.get('store');
        store.load(App.Foo, json);

        var records = Ember.get(this, 'records');
        records.addObject(App.Foo.find(json.id));
    }
});

Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.

Here's a working jsfiddle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM