简体   繁体   中英

Ember approach for setting controller computed property from action

I'm new to Ember and trying to follow the Data Down Action Up approach with my controller acting as the main parent 'component' with a number of components sending actions up to the controller to set values on models that will eventually trickle back down new value to the components. I know controllers are going away but I need this controller to hold all the state if you will...

I'm struggling with my approach in that I want to set a number of computed properties on my controller that watch model properties. Most difficult seemingly is setting the value of one of these computed properties that needs to set some other property when an action fires. Here is some of my controller (see updated exampled below, this left for reference):

//controllers/groupPlanning.js

import Ember from 'ember';

export default Ember.Controller.extend({

    groups: Ember.computed('model.@each.groups', function(){
        return this.get('model').get('groups');
    }),

    enrollmentGroup: Ember.computed('groups', function(){
        this.get('groups').forEach(function(group) {
            if (group.get('name') === 'Enrollment'){
                return group;
            }
        }):

    }),

    enrollmentGroupPercent: Ember.computed('enrollmentGroup', function() {
        var group = this.get('enrollmentGroup');
        return group.get('year') * this.get('someOtherNumber');
    }),

   ...

    actions: {
        setGroupYear: function(year){
            var group = this.get('enrollmentGroup');
            group.set('year', year);
            group.save();
        }
    }

});

UPDATE : The example above had a number flaws, the below code is to show the corrections per Lux's solution:

//controllers/groupPlanning.js

import Ember from 'ember';

export default Ember.Controller.extend({

    groups: Ember.computed('model.groups.[]', function(){
        return this.get('model.groups');
    }),

    enrollmentGroup: Ember.computed('groups', function(){
        return this.get('groups').findBy('name', 'Enrollment');
    }),

    enrollmentGroupPercent: Ember.computed('enrollmentGroup.year', 'someOtherNumber', function() {
        var group = this.get('enrollmentGroup');
        return group.year * this.get('someOtherNumber');
    }),

   ...

    actions: {
        setGroupYear: function(year){
            var group = this.get('enrollmentGroup');
            group.set('year', year);
            group.save();
        }
    }

});

Aside from asking if this approach seems reasonable, I'm having a problem actually setting the value of enrollmentGroup in my action:

group.set('year', year);

which throws and error of

Uncaught TypeError: Cannot read property 'set' of undefined

Any pointers here would be appreciated. Thanks!

First, your dependency on the enrollmentGroupPercent is missing the year . Instead of

enrollmentGroupPercent: Ember.computed('enrollmentGroup', function() {

use

enrollmentGroupPercent: Ember.computed('enrollmentGroup.year', 'someOtherNumber', function() {

You definitely need the year because you modify it.


second, your problem:

Uncaught TypeError: Cannot read property 'set' of undefined

this looks like group is null / undefined at this time! maybe check this with the debugger!

So definitly enrollmentGroup returns nothing. Maybe you just have no group with the name Enrollment . But I think you maybe ran into something more tricky.

I assume your model is an ember-data model, and groups an hasMany relationship.

This code does not make any sense:

groups: Ember.computed('model.@each.groups', function(){
    return this.get('model').get('groups');
}),

First I would recommend to replace this.get('model').get('groups') with this.get('model.groups') or get(this, 'model.groups') after setting get to Ember.get .

Then you use @each on model , which makes only sense if model is an array.

Probably you want to recompute the CP when groups are added/removed. In that case use model.groups.[] as dependency key. If you want to aggregate data from the groups, for example calculate a total you can use model.groups.@each.quantity and the CP will recalculate whenever a quantity on any group changes.

You actually need this if name could change, because then the list of groups does not change only the name so maybe you set the name later to Enrollment ?

Now you should be able to replace

enrollmentGroup: Ember.computed('groups', function(){

with

enrollmentGroup: Ember.computed('groups.@each.name', function(){

but due to a Bug in the current ember version this will not work !

It is important to understand then that the stores methods and model relationships don't return normal values but instances of ArrayProxy , ObjectProxy , PromiseArray and PromiseObject .

So actually groups is a PromiseArray of PromiseObjects. But you can unwrap the PromiseObjects in your groups CP:

groups: Ember.computed('model.groups.[]', function(){
    let promise = this.get('model.groups').then(groups => Ember.RSVP.all(groups));
    return DS.PromiseArray({promise});
}),

and then use content to observe the actual value:

enrollmentGroup: Ember.computed('groups.content.@each.name', function(){
    return this.get('groups').findBy('name', 'Enrollment');
}),

Now enrollmentGroup should always be up to date! So the only issue you might have is that you fire your action to early!

If you still have problems please provide a minimal example on ember-twiddle !

You are trying to set the value of a computed propery, which won't work like this. I would suggest the observer approach :

enrollmentGroupObserver: Ember.observer('groups', function(){
        this.get('groups').forEach(function(group) {
            if (group.get('name') === 'Enrollment'){
                this.set('enrollmentGroup', group);
            }
        }):

    }),
enrollmentGroup: null,

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