{"action":"created","issue":{"url":"https://api.github.com/repos/emberjs/rfcs/issues/21","labels_url":"https://api.github.com/repos/emberjs/rfcs/issues/21/labels{/name}","comments_url":"https://api.github.com/repos/emberjs/rfcs/issues/21/comments","events_url":"https://api.github.com/repos/emberjs/rfcs/issues/21/events","html_url":"https://github.com/emberjs/rfcs/pull/21","id":48711167,"number":21,"title":"Provide a way to mark a model as dirty when relationships or related model properties change.","user":{"login":"timrwood","id":643885,"avatar_url":"https://avatars.githubusercontent.com/u/643885?v=3","gravatar_id":"","url":"https://api.github.com/users/timrwood","html_url":"https://github.com/timrwood","followers_url":"https://api.github.com/users/timrwood/followers","following_url":"https://api.github.com/users/timrwood/following{/other_user}","gists_url":"https://api.github.com/users/timrwood/gists{/gist_id}","starred_url":"https://api.github.com/users/timrwood/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timrwood/subscriptions","organizations_url":"https://api.github.com/users/timrwood/orgs","repos_url":"https://api.github.com/users/timrwood/repos","events_url":"https://api.github.com/users/timrwood/events{/privacy}","received_events_url":"https://api.github.com/users/timrwood/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/emberjs/rfcs/labels/ember-data","name":"ember-data","color":"0052cc"},{"url":"https://api.github.com/repos/emberjs/rfcs/labels/Needs%20Response","name":"Needs Response","color":"e11d21"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":33,"created_at":"2014-11-13T23:49:03Z","updated_at":"2015-08-04T23:35:07Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/emberjs/rfcs/pulls/21","html_url":"https://github.com/emberjs/rfcs/pull/21","diff_url":"https://github.com/emberjs/rfcs/pull/21.diff","patch_url":"https://github.com/emberjs/rfcs/pull/21.patch"},"body":"# Summary\r\n\r\nProvide a way to mark a model as dirty when relationships or related model properties change.\r\n\r\n# Motivation\r\n\r\nThere is a common pattern to conditionally show a save button if a model needs saving.\r\n\r\nFor most cases, using the `isDirty` flag on the model is enough to determine whether a model needs saving or not. However, this flag currently ignores relationships, making it unusable for anything other than simple properties.\r\n\r\nIdeally, the user would be able to specify which relationships are saved when the model is saved, and thus, which ones should mark the model as being dirty.\r\n\r\n# Detailed design\r\n\r\nIn order to prevent dirtiness from spreading across the entire object graph, dirtying would have to be opt in. Users would specify on each relationship whether it would dirty the model or not.\r\n\r\n```js\r\nvar Comment = DS.Model.extend({\r\n  post: DS.belongsTo('post', { dirties: true }),\r\n  user: DS.belongsTo('user')\r\n});\r\n\r\nvar comment = this.store.find('comment', 1);\r\ncomment.get('post');    // null\r\ncomment.get('user');    // null\r\ncomment.get('isDirty'); // false\r\n\r\ncomment.set('user', someUser);\r\ncomment.get('isDirty'); // false\r\n\r\ncomment.set('post', somePost);\r\ncomment.get('isDirty'); // true\r\n\r\ncomment.set('post', null);\r\ncomment.get('isDirty'); // false\r\n```\r\n\r\nFor `hasMany` relationships, adding or removing a relationship would dirty the model.\r\n\r\n```js\r\nvar Post = DS.Model.extend({\r\n  tags:     DS.hasMany('tag', { dirties: true }),\r\n  comments: DS.hasMany('comment')\r\n});\r\n\r\nvar post = this.store.find('post', 1);\r\npost.get('isDirty');         // false\r\npost.get('tags.length');     // 0\r\npost.get('comments.length'); // 0\r\n\r\npost.get('comments').pushObject(someComment);\r\npost.get('comments.length'); // 1\r\npost.get('isDirty');         // false\r\n\r\npost.get('tags').addObject(someTag);\r\npost.get('tags.length'); // 1\r\npost.get('isDirty');     // true\r\n\r\npost.save();\r\npost.get('isDirty'); // false\r\n\r\npost.get('tags').removeObject(someTag);\r\npost.get('tags.length'); // 0\r\npost.get('isDirty');     // true\r\n```\r\n\r\nSometimes, entire related models are serialized when a model is saved. In those circumstances, it would be useful to allow the model to be dirtied when any of the related model properties are saved.\r\n\r\n```js\r\nvar User = DS.Model.extend({\r\n  address: DS.belongsTo('address', { dirties: 'properties' })\r\n});\r\n\r\nvar Address = DS.Model.extend({\r\n  street: DS.attr('string')\r\n});\r\n\r\nvar user = this.store.find('user', 1);\r\nuser.get('isDirty'); // false\r\nuser.get('address.street'); // 4th Ave\r\n\r\nuser.set('address.street', 'Main Street');\r\nuser.get('isDirty'); // true\r\n\r\nuser.set('address.street', '4th Ave');\r\nuser.get('isDirty'); // false\r\n```\r\n\r\n# Drawbacks\r\n\r\nThis would significantly increase the complexity of the `isDirty` computed property.\r\n\r\nThere would probably need to be checks in place to prevent recursive dirty checking. In the example below, once either of the models is made dirty, they would never become clean again by reverting a property, as each one's `isDirty` state is dependent on the other's.\r\n\r\n```js\r\nvar A = DS.Model.extend({\r\n  b: DS.belongsTo('b', { dirties: true })\r\n});\r\nvar B = DS.Model.extend({\r\n  a: DS.belongsTo('a', { dirties: true })\r\n});\r\n```\r\n\r\n# Alternatives\r\n\r\n[Paul Ferrett's isFilthy computed property](http://paulferrett.com/2014/ember-model-isdirty-when-belongsto-changes/)\r\n\r\n[Steven Lindberg's Dependent Relationships](https://gist.github.com/slindberg/8660986)\r\n\r\n[Tim Wood's Dirty Relationships Mixins](https://gist.github.com/timrwood/c58b667650bee749d5af)\r\n\r\n# Unresolved questions\r\n\r\nThe syntax feels like it could use some bikeshedding.\r\n\r\n```js\r\nDS.belongsTo('other', { dirtiesOn: 'relationship' })\r\nDS.belongsTo('other', { dirtiesOn: 'properties' })\r\n```\r\n\r\nConceptually, a relationship should be considered dirty if the relationship is being saved by the model, so it could be named something related to saving.\r\n\r\n```js\r\nDS.belongsTo('other', { savesRelation: true })\r\nDS.belongsTo('other', { savesModel: true })\r\n```"},"comment":{"url":"https://api.github.com/repos/emberjs/rfcs/issues/comments/127796666","html_url":"https://github.com/emberjs/rfcs/pull/21#issuecomment-127796666","issue_url":"https://api.github.com/repos/emberjs/rfcs/issues/21","id":127796666,"user":{"login":"Kilowhisky","id":7075666,"avatar_url":"https://avatars.githubusercontent.com/u/7075666?v=3","gravatar_id":"","url":"https://api.github.com/users/Kilowhisky","html_url":"https://github.com/Kilowhisky","followers_url":"https://api.github.com/users/Kilowhisky/followers","following_url":"https://api.github.com/users/Kilowhisky/following{/other_user}","gists_url":"https://api.github.com/users/Kilowhisky/gists{/gist_id}","starred_url":"https://api.github.com/users/Kilowhisky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Kilowhisky/subscriptions","organizations_url":"https://api.github.com/users/Kilowhisky/orgs","repos_url":"https://api.github.com/users/Kilowhisky/repos","events_url":"https://api.github.com/users/Kilowhisky/events{/privacy}","received_events_url":"https://api.github.com/users/Kilowhisky/received_events","type":"User","site_admin":false},"created_at":"2015-08-04T23:35:07Z","updated_at":"2015-08-04T23:35:07Z","body":"Anyone have a workaround for this right now? I've been avoiding it while working on other parts of my app but now i actually need cancel buttons to do what they say. "}}