Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember.js ArrayProxy Array of Models

Tags:

model

ember.js

Goal

To create an array of Model’s, managed by an ArrayController (ArrayProxy). Requirements

Use ArrayController (ArrayProxy) abstraction to encapsulate the array of Model’s Convert objects input to ArrayProxy automagically upon insertion into the ArrayProxy Do not lazy convert at access time

Example Data Structures

App.AddressModel = Ember.Object.extend({
    address_name: null,
    address: null,
    printme: function() {
        console.log("Just making sure the array turned into an AddressModel");
    },
});

App.addressArray = Ember.ArrayProxy.create({
    transformFrom: function(item) {
    },
    transformTo: function(item) {
    },
    arrayContentWillChange: function(startIdx, removeAmt, addAmt) {
    },
});

Failures by Trial

dynamic property

Someone in the IRC channel mentioned trying dynamic properties. This resulted in a, what appeared to be by logic and empirical evidence, a recursive result. No doubt from making content both the dynamically generated variable and the ‘trigger/exported’ variable.

arrayContentWillChange

Again, a seemingly recursive result. Upon receiving a arrayContentWillChange notification I generate an AddressModel from the given array index item(s). I then set the old indexed item to the created Model and an arrayContentWillChange event is triggered again, repeat … recurse.

transformFrom / transformTo

https://github.com/emberjs/ember.js/pull/554#issuecomment-5401112 tomdale mentions in the above post to try using transformFrom and transformTo to cast the incoming and/or outgoing data. These function don’t seem to exist [http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.8.js].

ArrayProxy model patch

https://github.com/emberjs/ember.js/pull/554 tomdale’s suggestion to the original solution/post to this problem seems to generalize better than the model implementation introduced by jedwood, however, in Backbone.js handles this problem by using the special model variable and I found it to work well.

The Question

How do I extend ArrayProxy to convert all incoming object, to be managed, into an AddressModel?

like image 204
chrism Avatar asked May 24 '12 18:05

chrism


1 Answers

I took the approach mentioned by Tom Dale which overwrites the replace method. It also registers an observer for the content property to assure that the content is a typed version of the array, see http://jsfiddle.net/pangratz666/XCLmE/:

App.ModelArrayProxy = Ember.ArrayProxy.extend({
    modelType: Ember.required,

    _typedArray: function(objects) {
        var modelType = this.get('modelType');
        var array = objects.map(function(item) {
            // check if item is an instance of type
            if (!(item instanceof modelType)) {
                // create a new instance of type and pass the hash
                return modelType.create(item);
            }

            // just return the item, since it's already of type modelType             
            return item;
        });
        return array;
    },

    contentChanged: function() {
        if (!this.get('setTypedContent')) {
            var content = this.get('content');

            this.set('setTypedContent', true);
            this.set('content', this._typedArray(content));
            this.set('setTypedContent', false);
        }
    }.observes('content'),

    replace: function(idx, amt, objects) {
        this._super(idx, amt, this._typedArray(objects));
    }
});

This ModelArrayProxy can then be used as a "normal" ArrayProxy:

// model declaration
App.AddressModel = Ember.Object.extend({
    ...
});

// create a proxy for given modelType
App.proxy = App.ModelArrayProxy.create({
    content: [],
    modelType: App.AddressModel
});

var myArray = [{
    address_name: 'address_name 1',
    address: 'address 1'},
{
    address_name: 'address_name 2',
    address: 'address 2'}];

// set content with "normal" objects
App.proxy.set('content', myArray);

// invoke 'log' on all items in array
App.proxy.forEach(function(item) {
    item.log();
});

// push a normal object
App.proxy.pushObject({
    address_name: 'address_name 3',
    address: 'address 3'
});

// push an instance of App.AddressModel
App.proxy.pushObject(App.AddressModel.create({
    address_name: 'address_name 4',
    address: 'address 4'

}));

// invoke 'log' on all items in array
App.proxy.forEach(function(item) {
    item.log();
});​
like image 189
pangratz Avatar answered Sep 20 '22 21:09

pangratz