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
Extend/Inherit angular2 components annotations #7968
Comments
+1 |
There is a nice new GitHub feature to "add reaction"s instead of |
@decorators are just functions (so inheritance doesn't really make sense) You can compose them relatively easily (that is, wrap them in a function that itself is a decorator) but I'd advise against this, as it's possible this will interfere with build tooling that looks for @component decorators. I do understand the arguments re: DRY, but in general angular2 errs in favor of explicitness. |
I do not know if I understand well your arguments, but this behavior makes it difficult to build components with similar behaviors. Other languages that use Annotation keeps them accessible by reflection on objects that inherit them. So this would be expected behavior for Angular or typescript in the construction of its components? @component, keeps much of a Angular component behavior, it can not be inherited is something that does not seem to me the best, or is there any other way to configure |
We are also in need of inheritance. Right now we are duplicating the code unnecessarily to extend a component. |
This has been discussed in depth before, and the conclusion is that it is not clear what does it mean to inherit decorators / annotations. Yes we could make a set of rules, but it would be rather arbitrary, and than we would have to define a set of merge rules, (how do you merge parent and child properties on the decorator). While this sounds like a great idea at first, the more you dig into it the more arbitrary it becomes as to how to handle corner cases is. For this reason, we will not support this. Sorry. |
@mhevery, Ok. I understand their arguments, and difficulties in making it viable and not create something arbitrary. Something similar to this, for example:
import {Component, Input} from 'angular2/core';
export class BasePanelComponent {
constructor() {
// Set @Component in code
this._component = {
selector: 'base-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
};
}
@Input() content: string;
color: string = "red";
onClick(event) {
console.log("Click color: " + this.color);
}
}
import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'
export class MyPanelComponent extends BasePanelComponent {
constructor() {
super();
// Set @Component override properties
this._component.selector = 'my-panel';
this.color = "blue";
}
} Where This is a feature of extreme importance to build a concise application. Please consider an alternative approach. |
Today without a choice to inherit, I had to duplicate the code. Our's is enterprise application and was planning to use Angular 2 for our web UI development. We want to make some common services and common fields available to all the components as part of BaseComponent and the inherited component can avail those services and data easily. Looks like this is not feasible. Now we have to inject these services in every component we are developing. Also for some cases, we want to only change the component selector and style for semantic reasons rest of the code will remain the same; without inheritance we have to duplicate the code. Right now only few of us are trying to use Angular 2 as part of Research, eventually lot many developers will be involved to develop components at that time they should not be worrying about injecting common services in their components it should be available to them from the BaseComponent. I am surprised that Angular team is not considering this seriously. |
If Decorator/Annotation have these limitations, which according to its arguments can not be easily circumvented in the library, I make you a question:
Or pretender invest time/money in a library with primary limitations as listed in this issue? I believe this is even the time for major changes that can alter the route of angular2 as the library is still in RC1. It would be extremely disappointing to see a stable version of a library as angular2 be released with this kind of limitation. |
The following answers on StackOverflow could interest you regarding this issue:
The solution is to implement a custom decorator that gets annotations from the parent class and merge it into the ones of the current one. |
Is there any reason you couldn't extend the ComponentMetadata class and then set some default values? |
@iDev0urer, how I would inherit the decorator definitions of the base class? I do not know if I understood well how it would be. You could create a sample in Plunker? |
I posted an article on this: I would be interested in your feedbacks ;-) |
@templth I would advise against The basic gist is that Composition is better than Inheritance. I would love to see a blog post which discusses how composition is intended to work in Angular2. Would you be willing to work on such an article? If yes, I would be happy to explain the details and limitations and work with you. |
@mhevery I see what you mean. I would definitely be pleased / interested in working on such a blog post! |
@templth write it up, and I can review. Perhaps start with a google doc? |
@mhevery sure! Shared a google doc with you... |
@fernandocode: you can notice that you could also override the value of the
You can use the component and the directive this way:
|
@templth well written article on decorator inheritance. Looking forward for the article on the composition on angular2 |
We are using inheritance and we would like to avoid it, but we still don't know how to go for composition with Angular2. So we are also looking forward for this article. |
The /**
* Define component options for the base class
*/
const baseComponentOptions = {
selector: 'base-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel {
padding: 50px;
}
`]
};
/**
* Base class
*/
@Component(baseComponentOptions)
export class BasePanelComponent {
@Input() content: string;
color: string = "red";
onClick(event){
console.log("Click color: " + this.color);
}
}
/**
* Subclass that over-rides the selector and constructor
*/
const myPanelComponentOptions = Object.assign({}, baseComponentOptions, {
selector: 'my-panel'
});
@Component(myPanelComponentOptions)
export class MyPanelComponent extends BasePanelComponent{
constructor(){
this.color = "blue";
}
} It's not as elegant as JUST doing "MyThing extends BaseThing", but is it good enough? |
Not looking into some internal details, but just focusing on code aesthetics, some times we have to repeat ourselves on injecting directives, parametrize our component, importing code that is imported everywhere and sometimes child component have different styles and/or html tags. As a suggestion, and following object orientation principles, we can make an component have a tag, like used on the router, to specify where the child template will be rendered. Let the communication between those two components, flow using super() on constructor, super.(), or even super.variable. For illustration of the solution, below the code: import { ALotOfThings, ALotOfDirectives } from '@angular/core';
import { ALotOfThings2, ALotOfDirectives2 } from 'somecomponent';
@Component({
template: '<h1 (click)="click()">{{ title }}</h1> <child> </child> <footer>...</footer>',
directives: [ALotOfDirectives, MoreDirectives, EvenMoreDirectives, MoreDirectivesThatICanHandle]
})
abstract class Parent {
constructor(private title: string){
}
abstract click();
} On child component import { Parent } from './parent';
@Component({
template: '<p> Im the child component </p>'
directives: [ OnlyWhatChildNeeds, NoJunkDirectives, CleanCodeForEveryOne ]
})
class Child extends Parent{
constructor(){
super('Title from child');
}
click(){
console.log('Click from parent component');
}
} What we get with this implementation ? A good use of the strong type checking, that will require that the child component implement what is abstract on parent, or be abstract. And a flexible way to position child content inside the parent component. |
@opsxcq . I could not understand where his approach would help to resolve the annotations inheritance problem, the problem is reported in issue. I tried to create a Plunker to follow what your solution proposes, but could not understand what the relationship of this with the problem of the issue. If you can create a Plunker demonstrating what use of this solution to the problem of the question! = D |
@fernandocode this is an hypothetical code, it won't work on actual angular2, but is a suggestion about how the code would be. Just an example of its construction. And as angular2 is an open source project, we can all share ideas and code. I'm facing the same problem here, and this construction would bring more clarity and flexibility to my code. Take your problem at the most generic level, where components act like objects in typescript. Keeping the same The Focusing on the implementation on angular2, I think that it won't be impossible, considering the tag information and the object hierarchy. Not focusing only on your problem, but on a complete component inheritance solution, did you think that this construction solve what you would ever need about |
I finally published the first part of the article on the composition on angular2: https://medium.com/@ttemplier/component-composition-in-angular2-part-1-33f50f402906#.duudocsbc. Feel free to make me your comments! |
It's a good article @templth , but I don't think that it will solve the inheritance problem that we are discussing about. But your technique is interesting, I will give it a try today. |
@mhevery There seem to be a few contradictory statements here. I completely agree that composition is preferable to inheritance (in fact composition is far superior). For one thing decorators are literally syntactic sugar for function composition. They can be chained (again sugar for composition) to produce new values as they aggregate information and return to the decorator above them. What you are basically saying is that inheritance is problematic and so thusly inheritance of decorators is problematic. I buy that argument. But I have no need to use decorators on classes with an extends clause if I can wrap common behavior by abstracting over decorators. Since inheritance of decorators is not supported and since I cannot abstract over angular decorators, I conclude two things.
Furthermore, with TSC-wrapped and AOT, which were not part of the original design, TypeScript's semantics are being changed, limited, and altered by Angular. I feel like this is the @script debacle all over again |
https://plnkr.co/edit/2ufBCokJXNHNwscw9rAA?p=preview I've just been scratching the surface of Angular2 recently, but I messed around in a plnkr I found to see what would happen... seems to handle the class extension better than the const object approach. //our root app component
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
export class Metadata {
protected readonly selector: string = 'my-app';
protected readonly template: string = 'Test';
}
export class MetadataChild extends Metadata {
protected readonly template: string = this.template + ' child';
constructor() {
super();
}
}
@Component(new MetadataChild())
export class App {
name:string;
constructor() {
this.name = 'Angular2'
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {} |
I fully agree with you @aluanhaddad, and I also agree that the composition is better than inheritance. But not always the problem can be solved with composition, if that were true there would be no need to have inheritance. This should be seen as a recommendation is not as a rule. This should be seen as a recommendation is not as a rule, is being used here this argument is a recommendation, to justify a limitation of the library.I agree that there will always be limitations, just do not agree to limitations on key features such as inheritance, in a large library as angular2. Look at this scenario: I library using a of third UI component (primeng) for development. To require a specific behavior to the Dropdown component of the library (source Dropdown component), for example, make the query directly in the database for the filter options, and this behavior is not implemented with ability to register an external event, the only choice short-term is to extend the component and implement the method according to the need, there is no possibility to solve this with composition, unique possibilities I see are:
Being that the latter is not feasible because is of a third component. Example extends Dropdown component in plunker. Question: Is there any way to do this without changing the original component or duplicate code in extended component? |
@fernandocode I agree. Indeed there are cases where inheritance is the necessary or even the elegant solution. As you say, code duplication is a very bad practice indeed. While, frameworks like Angular have a very real and practical need to constrain the literally hundreds of ad-hoc augmentation patterns that exist in the JavaScript ecosystem, they have an equally strong need to provide and bless a structured subset of these patterns (or introduce their own) to allow developers to reuse code. For example, AngularJS had a notion of service customization, which while slightly intimidating for newcomers was an extremely powerful composition mechanism. It was great when a service in a shared Any solution where copy and paste is the recommended approach is flat out wrong. |
I've been reading almost all issues about inheritance and I try to avoid "me too" comment but I've been working with angular 2 for some time now I would like to say that : I can totally imagine how complicated or even impossible it could be to make something works correctly as said here by @mhevery. If your concern is also just about style and template you can +1 this post (using reaction emoji) so we'll see if it's worth focusing only on those properties and not into a full inheritance of Components. |
Hey, sorry for the late question but wouldn't it be possible to achieve this by not using decorators. Thanks for any feedback. |
Isn't this finally already implemented in 2.3.0 http://angularjs.blogspot.it/2016/12/angular-230-now-available.html?m=1 |
It looks like f5c8e09 to be specific, has taken care of this issue. |
I do not understand some arguments of @mhevery at discussion at https://docs.google.com/document/d/16D3U3wdDBMEncUUEXBxiCaI11SnhjBijggZGq8JMjP0 about difficulties on decission how to inherit "template/templateUrl" or "styles/styleUrls" between a base and a derived component. What is wrong on the following rules?
That is all. It is no doubt that for some cases the inheritance principle on templates and styles has the real advantages. |
@f-mon @smoke no, this achieves nothing. Your child component can't override template, styles or anything specified in a decorator of a parent, you can only override the code of the class and export it instead of the parent class in your module. So you can't use both classes/components, can't modify presentation, etc. The idea of inheritance/proper composition is to be able to create proper UI libs with extendable components (naive example, you have Button which only shows textual caption and have some default click behaviours, then some other dev creates IconButton, which has different template with an icon embedded, everything else stays the same), Angular 2 does not allow that in any sane way. There are two ways right now - use JS API and lose AOT capabilities or hack into handling of decorators and write some custom stuff. |
@Auxx I do not understand your formulation:
because this is what actually creates a problem - we can override templates, styles (Angular 2.3.0 and later) but we also need to inherit them in other case ... and we can't. |
@mlc-mlapis if I have component X and I want to create a descendant Y I also want to inherit all of the @component() AND override |
Parser destroyed < x > and < y > |
@Auxx Fine, it is true but I am missing something ... how it relates with your previous post? |
@Auxx, your example:
Is a perfect situation where angular inheritance does not facilitate the extension of components. My original problem that motivated this issue is similar to that. And if I have not lost anything that has been done so far, this problem continues. I understand that it is not an easy change, but it discourages me in relation to angular2 future. |
@fernandocode Would the combination of #13764 and #13766 be sufficient to address your concerns? |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
I would like to create extensions for some components already deployed in Angular 2, without having to rewrite them almost completely, as the base component could undergo changes and wish these changes were also reflected in its derived components.
Example
I created this simple example to try to explain better the problem:
With the following base component
app/base-panel.component.ts
:Would you like to create another derivative component only alter, for example, a basic component behavior in the case of the example color,
app/my-panel.component.ts
:Example in Plunker
Problem/Limitation
As you can see in the implementation of the derivative component
app/my-panel.component.ts
, much of the implementation was repeated, and the single part really inherited was theclass
BasePanelComponent
, but the annotation@Component
had to basically be completely repeated, not just the changed portions, as theselector: 'my-panel'
.Feature request
After a few days of research around this possible limitation (question on StackOverflow and StackOverflowPt (my native language)), concludes that even the current version there is no mechanism for the annotations inheritance. So I wonder if it is possible to be implemented this possibility/feature functionality for future versions of the angular library?
The text was updated successfully, but these errors were encountered: