Observing changes to an ItemController in Array Controller

末鹿安然 提交于 2019-12-13 04:16:16

问题


I've been learning ember by recreating the TodoMVC in EmberCli. Ive recreated all the functionality, but I ran into an issue and I was hoping someone could shed some light on the situation.

It seems that my Todos ArrayController will observe and fire functions when properties in my model change but not when values in my Todo ObjectController change.

I moved isEditing into the Model so that when I call editTodo canToggle fires. But I would prefer to store that value in my controller and not the model.

I set up a test with with a propTest boolean. on a button click I fire propTogglebut todoPropToggle doesn't respond to the change. The only time that it does ever fire is on initialization.

Any insight would be super helpful.

TODOS CONTROLLER

import Ember from 'ember';
export default Ember.ArrayController.extend({
actions: {
  createTodo: function() {
    var title = this.get('newTitle');
    if (!title.trim()) {
      return;
    }
    var todo = this.store.createRecord('todo', {
      title: title,
      isCompleted: false,
      isEditing:false
    });

    this.set('newTitle', '');
    todo.save();
    }
  },

  canToggle: function() {
    var isEditing = this.isAny('isEditing');
    return this.get('length') && !isEditing;
  }.property('length','@each.isEditing'),

  todoPropToggle: function() {
    var hasPropTest = this.isAny('propTest');
    return hasPropTest;
  }.property('@each.propTest')
});

TODO CONTROLLER

import Ember from 'ember';

export default Ember.ObjectController.extend({

  actions: {
    editTodo: function() {
      var todo = this.get('model');
      todo.set('isEditing', true);
    },

    removeTodo: function() {
      var todo = this.get('model');
      todo.deleteRecord();
      todo.save();
    },

    acceptChanges: function() {
      var todo = this.get('model');
      todo.set('isEditing', false);
      if (Ember.isEmpty(this.get('model.title'))) {
        this.send('removeTodo');
      }
      else {
        this.get('model').save();
      }
    },

    propToggle:function(){
      this.set('propTest',!this.get('propTest'));
    }
  },

  propTest:true,

  isCompleted: function(key, value) {
    var model = this.get('model');
    if (value === undefined) {
      return model.get('isCompleted');
    }
    else {
      model.set('isCompleted', value);
      model.save();
      return value;
    }
  }.property('model.isCompleted')
});

回答1:


How about an alternative approach? We could toggle 'canToggle' in arrayController directly from the object controller by either using parentController or specifying needs. Avoids having to observer all the itemControllers which should be more efficient too.

TODOS CONTROLLER:

import Ember from 'ember';
export default
Ember.ArrayController.extend({
    /**
    * references the todo model that is currently being edited
    */
    editingTodo: null,
    canToggle: Ember.computed.notEmpty('editingTodo'),

    actions: {
        createTodo: function () {
            var title = this.get('newTitle');
            if (!title.trim()) {
                return;
            }
            var todo = this.store.createRecord('todo', {
                title: title,
                isCompleted: false,
                isEditing: false
            });

            this.set('newTitle', '');
            todo.save();
        }
    }
});

TODO CONTROLLER

import Ember from 'ember';

export default
Ember.ObjectController.extend({

    needs:['todos']
    todos : Ember.computed.alias('controllers.todos'),

    actions: {
        editTodo: function () {
            this.set('todos.editingTodo', this.get('model'));
        },

        removeTodo: function () {
            var todo = this.get('model');
            if (this.get('todos.editingTodo') === todo) {
                this.set('todos.editingTodo', null);
            }
            todo.deleteRecord();
            todo.save();
        },

        acceptChanges: function () {
            this.set('todos.editingTodo', null);
            if (Ember.isEmpty(this.get('model.title'))) {
                this.send('removeTodo');
            }
            else {
                this.get('model').save();
            }
        }
    },

    isCompleted: function (key, value) {
        var model = this.get('model');
        if (value === undefined) {
            return model.get('isCompleted');
        }
        else {
            model.set('isCompleted', value);
            model.save();
            return value;
        }
    }.property('model.isCompleted')
});

I have not tested it, but I hope you get the jist of the solution I am proposing.




回答2:


propTest in this scenario here looks like it's a computed property, and not an observable. (edit: not that computed properties don't have underlying observables powering them, but they're different in usage enough that I like to keep them separate) It'll fire when the underlying propTest fires, but if there's no change, ember will no-op that out in the run loop. If you'd rather this be a raw observable, use the observes() syntax. @each will work here, but I like being explicit and have an observable update what it needs to rather than using the computed property, unless I need direct access to that property to bind with in a template.

The "only firing on execution" stems from the initial bindings from the computed property getting created. If it never fires after that, the underlying bindings you're using for the computed property @each.propTest must be incorrect, otherwise that would indeed fire.

I think you may also be confused to the purpose of an ObjectController. An Ember.Object can do all of the observable business that a controller can, short of having a 'model' or 'content' property backing it. It looks like you may want to go with a straight up object rather than a controller here for the todo, since ember doesn't really have a 'model' type. I'd then put the objects themselves as part of the content of the ArrayController, at which point @each would be able to iterate on them as you'd expect.

The ObjectController sits at the same level of usage as an ArrayController. You can certainly nest them as you've done here, but my spidey-sense is tingling that it's the wrong thing to do given the application. You probably don't need to have a backing controller for each todo object, you just need the todo object itself.



来源:https://stackoverflow.com/questions/25774035/observing-changes-to-an-itemcontroller-in-array-controller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!