0
votes

I have a problem with a setter on a computed property which is an array. It seems like setter is not fired for arrays.

Here is a minimized code just for demonstrating my problem. There is also a JSFiddle: http://jsfiddle.net/NQKvy/970/

App.People = DS.Model.extend({
    array1: DS.attr('array', {defaultValue: [{name: 'Hans Maulwurf'},
                                             {name: 'Karl Käfer'}]}),

    array2: function(key, value){
        var array1 = this.get('array1'),
            array2 = [];

        if (arguments.length === 2) {
            this.set('array1', value);
        }

        if (array1 === undefined || typeof array1 === null) {
            return null;
        }

        $.each(array1, function(index, value){
            array2.pushObject( {name: value.name} );
        });

        return Ember.isNone(array2) ? null : array2;
    }.property('array1.@each')
});

App.IndexRoute = Ember.Route.extend({
    model: function(){
      return this.store.createRecord('people', {
      })
  }
});

App.IndexController = Ember.ObjectController.extend({
    actions: {
        newPerson1: function() {
            this.get('model.array1').pushObject({name: 'Klaus Kobold'});
        },
        newPerson2: function() {
            this.get('model.array2').pushObject({name: 'Klaus Mann'});
        }
    }
});

Everything is fine when I push a person to array1. Computed property is updated properly. But when I push a person to array2 (the computed property) array1 is not updated even so a setter is implemented.

Normally it is no problem to update an property through a computed property (http://emberjs.com/guides/object-model/computed-properties/#toc_setting-computed-properties). But in this case EmberJS doesn't set value when changing the array.

Did I make a mistake or is it really not possible in EmberJS? If so do you have an idea how to achieve this?

I have to change the array through a computed property. There is no other way for my use case.


Update:

As kingpin2k pointed out it's only a problem when not setting a new value for whole array but pushing a new element to an existing array. I updated the JSFiddle to demonstrate the difference: http://jsfiddle.net/NQKvy/972/ As you can see array1 got updated when setting a new value to array2 by changePerson2 action but is not updated when pushing a new element to the array by newPerson2 action.

I figured out that there is a dirty workaround. I could set the computed property again after pushing an element to the array:

this.get('model.array2').pushObject({name: 'Detlef Workaround'});
this.set('model.array2', this.get('model.array2'));

Action dirtyWorkaround in fiddle is demonstrating this. But isn't there another way?

A few word about my use case: I am using computed properties for encryption / decryption of data. The normal property which is send to server is encrypted with Stanford Javascript Crypto Library (SJCL). Decryption / encryption is handled by a computed property. So application access all data through computed properties. This works totally fine for data type String, Date etc. I only run into problems with arrays.

1
You aren't setting array2, so it wouldn't call the computed property's setter. Please explain why you think you have to change the array through the computed property. - Kingpin2k
@kingpin2k I updated the question with a few word about my use case and pointed out that the problem only occurs when not setting an array but pushing an element to an existing array. - jelhan
ehhhhhhhhhhhhhhh, quick rant, client side encryption has no actual benefit, if you're encrypting/decrypting it client side then that means the private keys are available client side and doing no more good than obfuscation. I'd feel remiss if I didn't mention it's a waste of time. Next, my recommendation.... - Kingpin2k
Okay, I would recommend completely tossing away the idea of using a computed property to encrypt/decrypt your models. It would make more sense to do this in the serialization/deserialization of the record when you fetch/save it from/to the server. Then you wouldn't have to do all kinds of hacks with computed properties and worry about how you access your models in your application. - Kingpin2k
The encryption should protect the private data against the server. So it's be design that the private key must be only available on client side. The server should not have any knowledge about the data stored. I know that a malicious server could send bad javascript and thereby get the private key. - jelhan

1 Answers

0
votes

For completeness sake, you would want to add an array observer on the array, and update the other array whenever that array changed.

array.addArrayObserver(contextToBindTo,{
  willChange: function(observedObj, start, removeCount, addCount){  }, 
  didChange: function(observedObj, start, removeCount, addCount){ }
});

Here's an example

http://emberjs.jsbin.com/wifovebu/1/edit