1. #1
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    98
    Vote Rating
    5
    plleeuwm is on a distinguished road

      2  

    Default Are there ever going to be useful events on Ext.data.Model ?

    Are there ever going to be useful events on Ext.data.Model ?


    Currently in the 4.2 beta and the lower the Ext.data.Model methods of afterEdit, afterReject, afterCommit just call methods on the actual store object(s) if there is one linked. If you have a model without a store you are SOL, the only event that gets fired from Model is of all things 'idchanged'. Are there any plans to put these events into models?

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    36,811
    Vote Rating
    834
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    You could easily add events, for example a datachanged when you hit set (disclaimer: needs to be furthered to handle an object)

    Code:
    Ext.define('Override.data.Model', {
        override : 'Ext.data.Model',
    
        set : function(fieldName, newValue) {
            var oldValue = this.get(fieldName),
                ret      = this.callParent([fieldName, newValue]);
    
            this.fireEvent('datachanged', this, fieldName, newValue, oldValue);
    
            return ret;
        }
    });
    
    Ext.define('MyModel', {
        extend : 'Ext.data.Model',
    
        fields : ['foo']
    });
    
    Ext.application({
        name : 'Test',
    
        launch : function() {
    
            var model = new MyModel({
                foo : 'bar'
            });
    
            model.on('datachanged', function(record, fieldName, newValue, oldValue) {
                console.log('data changed', fieldName, newValue, oldValue);
            });
    
            model.set('foo', 'mitch');
    
        }
    });
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    98
    Vote Rating
    5
    plleeuwm is on a distinguished road

      0  

    Default


    Quote Originally Posted by mitchellsimoens View Post
    You could easily add events, for example a datachanged when you hit set (disclaimer: needs to be furthered to handle an object)

    Code:
    Ext.define('Override.data.Model', {
        override : 'Ext.data.Model',
    
        set : function(fieldName, newValue) {
            var oldValue = this.get(fieldName),
                ret      = this.callParent([fieldName, newValue]);
    
            this.fireEvent('datachanged', this, fieldName, newValue, oldValue);
    
            return ret;
        }
    });
    
    Ext.define('MyModel', {
        extend : 'Ext.data.Model',
    
        fields : ['foo']
    });
    
    Ext.application({
        name : 'Test',
    
        launch : function() {
    
            var model = new MyModel({
                foo : 'bar'
            });
    
            model.on('datachanged', function(record, fieldName, newValue, oldValue) {
                console.log('data changed', fieldName, newValue, oldValue);
            });
    
            model.set('foo', 'mitch');
    
        }
    });
    But this logic already exists in store why would we want to duplicate it?

    Code:
    model.set('foo', 'mitch');
    model.set('foo', 'mitch');
    Would fire datachanged twice. The stores update event logic already handles this.

    ***
    Yes I know we can have overrides that can do anything to the code but I think that this should be handled at the framework level. If you guys have no intention adding the events we can add them ourselves which is fine with us. I'd be worried about if we publish these events in our code developers rely on these events but then in 4.2 you guys publish something very similar. That would start us down the path of a complicated deprecation and refactoring process.

  4. #4
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    98
    Vote Rating
    5
    plleeuwm is on a distinguished road

      0  

    Default


    Here is my proposed solution to the problem if there there will be no work done to improve the model event system. There probably is some bugs in it but I whipped it in less than 30 mins. It is a complete hack of a bad design imo.

    Code:
    Ext.override(Ext.data.Model, {
        relayStoreEvents: function(events) {
            if (!this._eventsStore) {
                this._eventsStore = new Ext.data.Store({
                    model: this.self
                });
                this.join(this._eventsStore);
                this.relayEvents(this._eventsStore, events || ['update']);
            }
        }
    });
    I have designed a record based form with has it's saving, reading and writing and other logic go through the model instead of the Form submit actions. My problem is that there are no useful update events on the model itself without a store.

    But I can do something like :

    Code:
    Ext.define('RecordForm', 
        loadRecord: function(rec) {
        var ret = this.callParent(arguments);
        rec.relayStoreEvents();
        return ret;
        }    
    }
    In another part of the code I can publish the update events on the rec through the RecordForm.

  5. #5
    Sencha - Support Team slemmon's Avatar
    Join Date
    Mar 2009
    Location
    Boise, ID
    Posts
    4,913
    Vote Rating
    179
    slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold slemmon is a splendid one to behold

      0  

    Default


    +1

  6. #6
    Ext JS Premium Member
    Join Date
    Jan 2010
    Location
    Bavaria
    Posts
    23
    Vote Rating
    2
    _alex is on a distinguished road

      0  

    Default


    I spent some hours on the topic of missing CRUD Events on models, this is what i got so far:
    (changes in Bold)

    Code:
    Ext.override(Ext.data.Model,{    inheritableStatics: {
            load: function(id, config) {
                config = Ext.apply({}, config);
                config = Ext.applyIf(config, {
                    action: 'read',
                    id    : id
                });
    
    
                var operation  = new Ext.data.Operation(config),
                        scope      = config.scope || this,
                        callback,
                        me = this.prototype;
    
    
                callback = function(operation) {
                    var record = null,
                            success = operation.wasSuccessful();
                    if (success) {
                        record = operation.getRecords()[0];
                        record.fireEvent('read',record,operation);
                        // If the server didn't set the id, do it here
                        if (!record.hasId()) {
                            record.setId(id);
                        }
                        Ext.callback(config.success, scope, [record, operation]);
                    } else {
                        Ext.callback(config.failure, scope, [record, operation]);
                    }
                    Ext.callback(config.callback, scope, [record, operation, success]);
                };
    
    
                this.getProxy().read(operation, callback, this);
            }
        },
        save: function(options) {
            options = Ext.apply({}, options);
    
    
            var me     = this,
                    action = me.phantom ? 'create' : 'update',
                    scope  = options.scope || me,
                    stores = me.stores,
                    i = 0,
                    storeCount,
                    store,
                    operation,
                    callback;
    
    
            Ext.apply(options, {
                records: [me],
                action : action
            });
    
    
            operation = new Ext.data.Operation(options);
    
    
            callback = function(operation) {
                var success = operation.wasSuccessful();
                me.fireEvent(operation.action,me,operation);
    
    
                if (success) {
                    for(storeCount = stores.length; i < storeCount; i++) {
                        store = stores[i];
                        store.fireEvent('write', store, operation);
                        store.fireEvent('datachanged', store);
                        // Not firing refresh here, since it's a single record
                    }
                    Ext.callback(options.success, scope, [me, operation]);
                }
                else {
                    Ext.callback(options.failure, scope, [me, operation]);
                }
    
    
                Ext.callback(options.callback, scope, [me, operation, success]);
            };
    
    
            me.getProxy()[action](operation, callback, me);
    
    
            return me;
        },
        destroy: function(options) {
            options = Ext.apply({
                records: [this],
                action : 'destroy'
            }, options);
    
    
            var me = this,
                    isNotPhantom = me.phantom !== true,
                    scope  = options.scope || me,
                    stores,
                    i = 0,
                    storeCount,
                    store,
                    args,
                    operation,
                    callback;
    
    
            operation = new Ext.data.Operation(options);
    
    
            callback = function(operation) {
                me.fireEvent(operation.action,me,operation);
                args = [me, operation];
    
    
                // The stores property will be mutated, so clone it first
                stores = Ext.Array.clone(me.stores);
                if (operation.wasSuccessful()) {
                    for (storeCount = stores.length; i < storeCount; i++) {
                        store = stores[i];
    
    
                        // If the store has a remove (it's not a TreeStore), then
                        // remove this record from Store. Avoid Store handling anything by passing the "isMove" flag
                        if (store.remove) {
                            store.remove(me, true);
                        }
    
    
                        // Other parties may need to know that the record as gone
                        // eg View SelectionModels
                        store.fireEvent('bulkremove', store, [me], [store.indexOf(me)], false);
                        if (isNotPhantom) {
                            store.fireEvent('write', store, operation);
                        }
                    }
                    me.clearListeners();
                    Ext.callback(options.success, scope, args);
                } else {
                    Ext.callback(options.failure, scope, args);
                }
                Ext.callback(options.callback, scope, args);
            };
    
    
            // Not a phantom, then we must perform this operation on the remote datasource.
            // Record will be removed from the store in the callback upon a success response
            if (isNotPhantom) {
                me.getProxy().destroy(operation, callback, me);
            }
            // If it's a phantom, then call the callback directly with a dummy successful ResultSet
            else {
                operation.complete = operation.success = true;
                operation.resultSet = me.getProxy().reader.nullResultSet;
                callback(operation);
            }
            return me;
        }
    });
    For me this solution works for what i need it, in special syncing hasMany Associations after create in a custom Model Class:

    Code:
            'create': function(model,operation) {            var result = operation.response.result;
                Ext.iterate(model.associations.items,function(association){
                    if(association.type == 'hasMany') {
                        if(result[association.name]) {
                            resultData = result[association.name];
                            modelStore = model[association.storeName];
    
    
                            delete modelStore.snapshot;
                            modelStore.clearData(true);
    
    
                            extractedResults = modelStore.proxy.reader.extractData(resultData);
                            modelStore.data.addAll(extractedResults);
                       }
                    }
                });
            },
    There might be more elegant ways to get these events, in special the load within the statics is a bit painful and might not be reliable.

    Anyway, it would be great if sencha could offer CRUD Events on models by default.

    Best regards,
    Alex

  7. #7
    Sencha Premium Member
    Join Date
    Sep 2010
    Posts
    98
    Vote Rating
    5
    plleeuwm is on a distinguished road

      0  

    Default


    _alex lets keep this conversation going and try to keep hacking this design to offer up some functionality on model.

    Check this out:

    Code:
    Ext.override(Ext.data.Model, {
        relayStoreEvents: true,
        constructor: function() {
            this.callParent(arguments);
            if(this.relayStoreEvents) {
                this._relayStoreEvents()
            }
        },
    
    
        _relayStoreEvents: function(events) {
            var me;
    
    
            if (!this._eventsStore) {
                this._eventsStore = new Ext.data.Store({
                    model: this.self
                });
                
                me = this;
                this._eventsStore.fireEvent = function() {
                    var args = Array.prototype.slice.call(arguments);
                    //remove the store from the event being fired
                    args.splice(1,1);
                    return me.fireEvent.apply(me, args);
                }
    
    
                this.join(this._eventsStore);
            }
        }
    });
    Code:
    Ext.define('User', {    extend: 'Ext.data.Model',
        fields: [
            {name: 'name',  type: 'string'},
            {name: 'age',   type: 'int', convert: null},
            {name: 'phone', type: 'string'},
            {name: 'alive', type: 'boolean', defaultValue: true, convert: null}
        ],
    
    
        changeName: function() {
            var oldName = this.get('name'),
                newName = oldName + " The Barbarian";
    
    
            this.set('name', newName);
        }
    });
    
    
    var rec =  Ext.create('User', {
        name : 'Conan',
        age  : 24,
        phone: '555-555-5555'
    });
    
    
    rec.on('update', function(record, operation) {
        console.log(this === record);
        console.log(arguments);
    });
    
    
    rec.set('name', 'Patrick');
    rec.set('age', 25);
    rec.set('age', 25);
    rec.set('age', 25);
    rec.set('age', 25);
    true
    [i, "edit", Array[1]]

    true
    [i, "edit", Array[1]]