Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ember computed property not being updated

I'm not too entirely sure why my computed property isn't returning updated values.

I have a list of options that a user can click through, and the action updates a property, which is an Ember Object, for the controller. I have a computed property that loops through the object, looks for keys that have non-null values for that Ember Object property, and if it does find one, returns false, otherwise true.

Here's the stuff:

App.SimpleSearch = Ember.Object.extend({
    init: function() {
        this._super();

        this.selectedOptions = Ember.Object.create({
            "Application" : null,
            "Installation" : null,
            "Certification" : null,
            "Recessed Mount" : null,
            "Width" : null,
            "Height" : null,
            "Heating" : null,
            "Power" : null
        });
    },

    selectedOptions: {},

    numOfOptions: 0,

    allOptionsSelected: function() {
        var selectedOptions = this.get('selectedOptions');
        for (var option in selectedOptions) {
            console.log(selectedOptions.hasOwnProperty(option));
            console.log(selectedOptions[option] === null);
            if (selectedOptions.hasOwnProperty(option)
                && selectedOptions[option] === null) return false;
        }
        return true;
    }.property('selectedOptions')
});


App.SimpleSearchRoute = Ember.Route.extend({
    model: function() {
        return App.SimpleSearch.create({
            'SimpleSearchOptions': App.SimpleSearchOptions,
            'numOfOptions': App.SimpleSearchOptions.length
        });
    },
    setupController: function(controller, model) {
        controller.set('model', model);
    }
});


App.SimpleSearchController = Ember.ObjectController.extend({
    getProductsResult: function() {
        var productsFromQuery;
        return productsFromQuery;
    },

    setSelection: function (option, selectionValue) {
        this.get('selectedOptions').set(option, selectionValue);
        this.notifyPropertyChange('allOptionsSelected');
    },

    actions: {
        registerSelection: function(option) {
            console.log('registering selection');
            console.log(this.get('allOptionsSelected'));
            console.log(this.get('selectedOptions'));
            this.setSelection(option.qname, option.value);
        },

The action in the controller, registerSelection is firing just fine, but I only see the console.log from the SimpleSearch model once. Once the property is computed that first time, it isn't paid attention to after that, which means that the computed property isn't observing the changes to selectedOptions whenever this is called:

setSelection: function (option, selectionValue) {
    this.get('selectedOptions').set(option, selectionValue);
    this.notifyPropertyChange('allOptionsSelected');
},

Edit:

I actually solved my issue without changing anything.

I've changed the following line:

this.notifyPropertyChange('allOptionsSelected');

to:

this.get('model').notifyPropertyChange('selectedOptions');

notifyPropertyChange needs to be called within the context of the model (or the Ember Object that has observers of a specific property), and the string sent as an argument is the name of the property that was updated.

After I made that change, it worked as intended.

like image 353
Nathan Lutterman Avatar asked Feb 04 '14 20:02

Nathan Lutterman


2 Answers

Ember doesn't observe objects for any change on the object, it observes a single property.

How is this affecting you? Well in this method you are watching selectedOptions, but that object itself is still the same object, you might be changing properties on it, but not the object itself. And then you are telling Ember in the scope of the controller that allOptionsSelected has changed, so it regrabs it, but it doesn't recalculate it because it's not dirty, it just changed. You'd really want to say selectedOptions has changed to get allOptionsSelected to recalculate its value. Unfortunately you're doing this in the scope of the controller, so telling the controller that property has changed doesn't matter to it.

allOptionsSelected: function() {
  var selectedOptions = this.get('selectedOptions');
  for (var option in selectedOptions) {
     console.log(selectedOptions.hasOwnProperty(option));
     console.log(selectedOptions[option] === null);
     if (selectedOptions.hasOwnProperty(option)
        && selectedOptions[option] === null) return false;
     }
  return true;
}.property('selectedOptions')

Here's a dummy example showing what things cause it to actually update.

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

Honestly since you're not watching particular properties I'd probably do an array, or create a method on the object that handles adding/removing/modifying the properties so you could fire from within it's scope a property change updating all parent listeners with the changes.

like image 138
Kingpin2k Avatar answered Sep 18 '22 21:09

Kingpin2k


Ember computed property dependent keys can be a

  1. property key. in example: 'jobTitle'
  2. computed property key. in example: 'companyName'
  3. property key of an object. in example: 'salesAccount.name and salesAccount.website'

Example extracted from Ember.model definition:

...
   jobTitle : DS.attr('string'),
   salesAccount: belongsTo('salesAccount'),

   companyName: Ember.computed('jobTitle', 'salesAccount.name', {
    get: function () {
      return this.get('salesAccount.name');
    }
  }),

  companyWebsite: Ember.computed('salesAccount.website', 'companyName',  {
    get: function () {
      return this.get('salesAccount.website');
    }
  })
...
like image 25
Ganesh Arulanantham Avatar answered Sep 16 '22 21:09

Ganesh Arulanantham