Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

form record: partial update #405

Open
belusky opened this issue Feb 6, 2014 · 8 comments
Open

form record: partial update #405

belusky opened this issue Feb 6, 2014 · 8 comments

Comments

@belusky
Copy link

belusky commented Feb 6, 2014

Is there possible to send to the server only changed fields in payload?
Changed fields = changed object properties

Possible implementations

  1. Make diff between actual record and last loaded record = only diff will be send (how to make diff?)
  2. Store changed properties (how to catch programmatic change?)

Grid support similar functionality via grid.getChanges() - return changed fields.

Nice to have :)

@vitmalina
Copy link
Owner

Good question. It is not implemented right now, but I have thought of it. Currently there is form.record and form.original. But I will implement it.

@vitmalina
Copy link
Owner

I have looked at it and it is harder then it looks. In the grid it was simpler. The problem starts with nested objects and you can delete nodes, etc.

@belusky
Copy link
Author

belusky commented Feb 6, 2014

I can provide "diff" algorithm.
Dòa 6.2.2014 19:13 pou¾ívateµ "Vitali Malinouski" [email protected]
napísal:

I have looked at it and it is harder then it looks. In the grid it was
simpler. The problem starts with nested objects and you can delete nodes,
etc.

Reply to this email directly or view it on GitHubhttps://github.com//issues/405#issuecomment-34352444
.

@vitmalina
Copy link
Owner

That's would be awesome. Here is the current that I have

getChanges: function () {
    var differ = function(record, original, result) {
        for (var i in record) {
            if (typeof record[i] == "object") {
                result[i] = differ(record[i], original[i] || {}, {});
                if (!result[i] || $.isEmptyObject(result[i])) delete result[i];
            } else if (record[i] != original[i]) {
                result[i] = record[i];
            }
        }
        return result;
    }
    return differ(this.record, this.original, {});
}

One problem to fix, how do you do diff if some records were deleted. Lets say form has enum, I need to mark that some records were deleted, and some where added.

@belusky
Copy link
Author

belusky commented Feb 6, 2014

Returns changed values, only null value represents deleted property.

var original = {
    "id" : 8,
    "code" : "321",
    "active" : true,
    "nested" : {
        "a" : 2,
        "b" : false,
        "c" : "this will be deleted"
    }
};

var actual = {
    "id" : 8,
    "code" : "123",
    "active" : false,
    "nested" : {
        "a" : 2,
        "b" : true
    }
};

diff(original, actual); //getChanges() returns

//  {
//      "active" : false,
//      "code" : "123",
//      "nested" : {
//          "b" : true,
//          "c" : null
//      }
//  }

diff function

define([], function() {
    function undef(value) {
        return typeof value === "undefined";
    }
    function typeofReal(value) {
        return value instanceof Array ? "array" :
                value === null ? "null" :
                value instanceof Date ? "date" : typeof value;
    }
    function emptyObj(o) {
        var i;
        for (i in o) {
            return false;
        }
        return true;
    }
    function resolve(a, b) {
        var typeA = typeofReal(a);
        var typeB = typeofReal(b);
        if (typeA === "array" && typeB === "array") {
            return _array(a, b, _keys(a, b));
        } else if (typeA === "object" && typeB === "object") {
            return _object(a, b, _keys(a, b));
        } else if (typeA !== typeB || (typeA !== "object" && typeA !== "array")) {
            var val = _plain(a, b);
            if (!undef(val)) { return val; }
        }
        return /*undefined*/;
        function _plain(a, b) {
            if (undef(a)) {
                return b;
            } else if (undef(b)) {
                return null;
            } else if (a !== b) { return b; }
        }
        function _object(a, b, keys) {
            var o = {};
            for ( var i = 0, key; i < keys.length; i++) {
                key = keys[i];
                if (key === keys[i - 1]) {
                    continue;
                }
                var val = resolve(a && a[key], b && b[key]);
                if ((typeofReal(val) === "object" && !emptyObj(val)) 
                    || (typeofReal(val) !== "object" && !undef(val))) {
                    o[key] = val;
                }
            }
            return o;
        }
        function _array(a, b, keys) {
            var r = [];
            for ( var i = 0, key; i < keys.length; i++) {
                key = keys[i];
                if (key === keys[i - 1]) {
                    continue;
                }
                var val = resolve(a && a[key], b && b[key]);
                if ((typeofReal(val) === "object" && !emptyObj(val)) 
                    || (typeofReal(val) !== "object" && val)) {
                    r.push(val);
                }
            }
            return r;
        }
        function _keys(a, b) {
            var keys = [], i;
            for (i in a) {
                if (a.hasOwnProperty(i)) {
                    keys.push(i);
                }
            }
            for (i in b) {
                if (b.hasOwnProperty(i)) {
                    keys.push(i);
                }
            }
            keys.sort();
            return keys;
        }
    }
    return function(o1, o2) {
        return resolve(o1, o2, "root");
    };
});

@vitmalina
Copy link
Owner

Did you get it from underscore? Is there anyway to shorten it, looks long?

Belos is real form data with drop down, enum, file, etc controls.

{
   "field.text":"some text",
   "field.textarea":"more text",
   "field.date":"1/15/2014",
   "field.time":"10:11 am",
   "field.color":"999999",
   "field.list":{
      "id":0,
      "text":"Pickle, Susan"
   },
   "field.combo":{
      "id":1,
      "text":"Item 1"
   },
   "field.enum":[
      {
         "id":"1",
         "text":"item 1"
      },
      {
         "id":2,
         "text":"item 2"
      }
   ],
   "field.file":[
      {
         "name":"file.txt",
         "size":3033,
         "type":"text",
         "modified":"1/1/2014"
      }
   ],
   "field.email":"[email protected]",
   "field.password":"pass",
   "field.int":"10",
   "field.float":"20",
   "field.money":30,
   "field.currency":40,
   "field.percent":50,
   "field.alpha":"60",
   "field.select":3,
   "field.check":33
}

Modified:

{
   "field.text":"some text - chanes",
   "field.textarea":"more text",
   "field.date":"1/15/2014",
   "field.time":"10:11 am",
   "field.color":"999999",
   "field.list":{
      "id":1,
      "text":"Adams, John",
      "hidden":true
   },
   "field.combo":"",
   "field.enum":[
      {
         "id":"1",
         "text":"item 1"
      },
      {
         "id":"Cruz, Steve",
         "text":"Cruz, Steve"
      }
   ],
   "field.file":[

   ],
   "field.email":"[email protected]",
   "field.password":"pass",
   "field.int":"10",
   "field.float":"20",
   "field.money":30,
   "field.currency":40,
   "field.percent":50,
   "field.alpha":"60",
   "field.select":3,
   "field.check":33
}

can you diff it?

@belusky
Copy link
Author

belusky commented Feb 7, 2014

Here is the result.
Array diffing is little complicated, because differ do not know anything about any enum/file identity... but it would not know about... or?

{
    "field.combo" : "", // deleted value should be null?
    "field.enum" : [ // this is change... but what type of change? all other values were removed?
        {
            "id" : "Cruz, Steve",
            "text" : "Cruz, Steve"
        }
    ],
    "field.file" : [], // deleted files shoul be null?
    "field.list" : {
        "hidden" : true,
        "id" : 1,
        "text" : "Adams, John"
    },
    "field.text" : "some text - chanes"
}

I think that

  1. none or all array items should be in diff output (none if we do not change any item, all if we change someone)
  2. null value should be present if we delete value

@vitmalina
Copy link
Owner

Thanks for your help. I think there are some particularities in how diff should work. For example for enum fields if I have a structure

"field.enum":[
      {
         "id":"1",
         "text":"item 1"
      },
      {
         "id":"Cruz, Steve",
         "text":"Cruz, Steve"
      }
   ],

and if I delete one item, it will change to

"field.enum":[
      {
         "id":"1",
         "text":"item 1"
      }
   ],

And diff should produce this:

"field.enum":[
      {
         "id":"1",
         "text":"item 1"
      },
      {
         "id":"Cruz, Steve",
         "text": null
      }
   ],

or maybe

"field.enum":[
      {
         "id":"1",
         "text":"item 1"
      },
      deleted['Cruz Steve']
   ],

I think enum is the only non-standard field (and file type too). For all other it should be simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants