{"action":"created","issue":{"url":"https://api.github.com/repos/angular/angular/issues/13387","repository_url":"https://api.github.com/repos/angular/angular","labels_url":"https://api.github.com/repos/angular/angular/issues/13387/labels{/name}","comments_url":"https://api.github.com/repos/angular/angular/issues/13387/comments","events_url":"https://api.github.com/repos/angular/angular/issues/13387/events","html_url":"https://github.com/angular/angular/issues/13387","id":194876917,"number":13387,"title":"Proposal: when inheriting components, merge decorators at the key level","user":{"login":"tycho01","id":3059397,"avatar_url":"https://avatars.githubusercontent.com/u/3059397?v=3","gravatar_id":"","url":"https://api.github.com/users/tycho01","html_url":"https://github.com/tycho01","followers_url":"https://api.github.com/users/tycho01/followers","following_url":"https://api.github.com/users/tycho01/following{/other_user}","gists_url":"https://api.github.com/users/tycho01/gists{/gist_id}","starred_url":"https://api.github.com/users/tycho01/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tycho01/subscriptions","organizations_url":"https://api.github.com/users/tycho01/orgs","repos_url":"https://api.github.com/users/tycho01/repos","events_url":"https://api.github.com/users/tycho01/events{/privacy}","received_events_url":"https://api.github.com/users/tycho01/received_events","type":"User","site_admin":false},"labels":[{"id":375501288,"url":"https://api.github.com/repos/angular/angular/labels/comp:%20core%20&%20compiler","name":"comp: core & compiler","color":"c7def8","default":false},{"id":130606845,"url":"https://api.github.com/repos/angular/angular/labels/type:%20feature","name":"type: feature","color":"EEEEEE","default":false}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2016-12-12T02:15:43Z","updated_at":"2016-12-14T04:59:27Z","closed_at":"2016-12-14T00:06:24Z","body":"**I'm submitting a ...**  (check one with \"x\")\r\n- [x] feature request\r\n\r\nI previously posted this suggestion at the [inheritance](https://github.com/angular/angular/issues/11606) thread, where as a follow-up proposal it was deemed out of scope -- hence the issue got closed.\r\n\r\n**Current behavior**\r\n\r\nWe [recently](https://github.com/angular/angular/issues/11606#issuecomment-262315256) gained component inheritance, with the following semantics:\r\n> only use the last decorator of each kind (e.g. @Component / ...)\r\n\r\n**Expected behavior / What is the motivation / use case for changing the behavior?**\r\n\r\nIt feels like it might make more sense to merge metadata on a by-key basis.\r\nYou'd likely need a new decorator to specify a new selector anyway, at which point you can no longer transfer e.g. that `ChangeDetectionStrategy` / `ViewEncapsulation`.\r\n\r\nTo illustrate, here was my existing solution for decorator inheritance, which did take care of key-basis merging (forgive my use of `lodash`), here including array concatenation (follow-up proposal):\r\n\r\n```ts\r\n// annotations.ts\r\n\r\nlet _ = require('lodash');\r\nimport { Component, Directive, Pipe } from '@angular/core';\r\n\r\n// extendible Component decorator, extended from here to merge items:\r\n// http://stackoverflow.com/questions/36837421/extending-component-decorator-with-base-class-decorator/36837482#36837482\r\nlet mergeMetadata = (metadataClass) => function(annotation: any) {\r\n  return function (target: Function) {\r\n    let parentTarget = Object.getPrototypeOf(target.prototype).constructor;\r\n    let parentAnns = Reflect.getMetadata('annotations', parentTarget);\r\n    if(_.size(parentAnns)) {\r\n      let parentAnn = parentAnns[0];\r\n      Object.keys(parentAnn).forEach(key => {\r\n        if (!_.isNil(parentAnn[key])) {\r\n          if (!_.isNil(annotation[key])) {\r\n            if (_.isArray(parentAnn[key])) {\r\n              annotation[key] = parentAnn[key].concat(annotation[key]);\r\n            } else if (_.isObject(parentAnn[key]) && _.isObject(annotation[key])) {\r\n              Object.assign(annotation[key], parentAnn[key]);\r\n            } else {\r\n              annotation[key] = parentAnn[key];\r\n            }\r\n          } else {\r\n            annotation[key] = parentAnn[key];\r\n          }\r\n        }\r\n      });\r\n    }\r\n    let metadata = new metadataClass(annotation);\r\n    Reflect.defineMetadata('annotations', [metadata], target);\r\n  }\r\n}\r\n\r\nexport let ExtComp = mergeMetadata(Component);\r\nexport let ExtDir = mergeMetadata(Directive);\r\nexport let ExtPipe = mergeMetadata(Pipe);\r\n\r\n// in component file: import `ExtComp`, then decorate components with `@ExtComp` rather than `@Component`\r\n```\r\n\r\nStated concern:\r\n> [I]t is easier to merge on an override strategy than it is to override on a merge strategy.\r\nYou can't really override a prop on a merge strategy, you'll have to redefine all the props (the whole `@Component`) while if you want to merge on an override strategy you just need to redefine the property you want to merge (for example `styles:` ).\r\n\r\nMy interpretation:\r\n\r\n- PR behavior: override. If the parent's decorator was exactly what you needed, you're good. If you want any property different, you'll have to redo them. You say \"property you want to merge (for example `styles:` )\". I suppose that's right, though it assumes you've exported the parent's decorator properties before using them there; Angular won't otherwise expose them to you to reuse (be it in adjusted form).\r\n\r\n - Alternative behavior: naive 'merge'. This is essentially an override on a per-property basis. If you wanted the same as the parent's decorator properties, you're good. If you wanted to change a property, you're good, just override it. If you wanted to remove one, override it with `null`. If you wanted to override an array, go ahead. If you wanted to add to an array, you have a problem, since unless you've manually exported the original for reuse, you won't have it available. AFAICT, this approach does not yet bring much controversy so far.\r\n"},"comment":{"url":"https://api.github.com/repos/angular/angular/issues/comments/266943152","html_url":"https://github.com/angular/angular/issues/13387#issuecomment-266943152","issue_url":"https://api.github.com/repos/angular/angular/issues/13387","id":266943152,"user":{"login":"vicb","id":248818,"avatar_url":"https://avatars.githubusercontent.com/u/248818?v=3","gravatar_id":"","url":"https://api.github.com/users/vicb","html_url":"https://github.com/vicb","followers_url":"https://api.github.com/users/vicb/followers","following_url":"https://api.github.com/users/vicb/following{/other_user}","gists_url":"https://api.github.com/users/vicb/gists{/gist_id}","starred_url":"https://api.github.com/users/vicb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vicb/subscriptions","organizations_url":"https://api.github.com/users/vicb/orgs","repos_url":"https://api.github.com/users/vicb/repos","events_url":"https://api.github.com/users/vicb/events{/privacy}","received_events_url":"https://api.github.com/users/vicb/received_events","type":"User","site_admin":false},"created_at":"2016-12-14T04:59:27Z","updated_at":"2016-12-14T04:59:27Z","body":"every one expect/wants a different behavior and it is not possible to satisfy every request.\r\nwe have picked the behavior that we think is the more flexible.\r\nalso we consider that this is case should be rare.\r\n "}}
{"actor":{"display_login":"vicb"}}