Je voudrais créer des extensions pour certains composants déjà déployés dans Angular 2, sans avoir à les réécrire presque complètement, car le composant de base pourrait subir des modifications et souhaiter que ces modifications soient également reflétées dans ses composants dérivés.
J'ai créé cet exemple simple pour essayer de mieux expliquer mes questions:
Avec le composant de base suivant app/base-panel.component.ts
:
import {Component, Input} from 'angular2/core';
@Component({
selector: 'base-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class BasePanelComponent {
@Input() content: string;
color: string = "red";
onClick(event){
console.log("Click color: " + this.color);
}
}
Voulez - vous que créer un autre composant dérivé alter, par exemple, un comportement des composants de base dans le cas de la couleur par exemple, app/my-panel.component.ts
:
import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'
@Component({
selector: 'my-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class MyPanelComponent extends BasePanelComponent{
constructor() {
super();
this.color = "blue";
}
}
Exemple de travail complet dans Plunker
Remarque: évidemment, cet exemple est simple et pourrait être résolu sinon pas besoin d'utiliser l'héritage, mais il ne vise qu'à illustrer le vrai problème.
Comme vous pouvez le voir dans l'implémentation du composant dérivé app/my-panel.component.ts
, une grande partie de l'implémentation a été répétée, et la seule partie réellement héritée était le class
BasePanelComponent
, mais il @Component
fallait essentiellement répéter complètement, pas seulement les parties modifiées, comme le selector: 'my-panel'
.
Existe-t-il un moyen de faire un héritage littéralement complet d'un composant Angular2, en héritant de la class
définition des marquages / annotations, comme par exemple @Component
?
Modifier 1 - Demande de fonctionnalité
Demande de fonctionnalité angular2 ajoutée au projet sur GitHub: Extend / Inherit angular2 components annotations # 7968
Modifier 2 - Demande fermée
La demande a été fermée, pour cette raison , que brièvement ne sauraient pas comment fusionner le décorateur sera faite. Nous laissant sans options. Mon opinion est donc citée dans le numéro .
la source
Réponses:
Solution alternative:
Cette réponse de Thierry Templier est une manière alternative de contourner le problème.
Après quelques questions avec Thierry Templier, je suis arrivé à l'exemple de travail suivant qui répond à mes attentes en tant qu'alternative à la limitation d'héritage évoquée dans cette question:
1 - Créer un décorateur personnalisé:
2 - Composant de base avec le décorateur @Component:
3 - Sous-composant avec le décorateur @CustomComponent:
Plunkr avec un exemple complet.
la source
Angular 2 version 2.3 vient de sortir et inclut l'héritage des composants natifs. Il semble que vous pouvez hériter et remplacer ce que vous voulez, à l'exception des modèles et des styles. Quelques références:
Nouvelles fonctionnalités d'Angular 2.3
Héritage des composants dans Angular 2
Un Plunkr démontrant l'héritage des composants
la source
More than one component matched on this element
si vous ne le faites pas.@Component()
balise. Mais vous pouvez partager le .html ou le .css en vous référant au même fichier si vous le souhaitez. Dans l'ensemble, c'est un gros plus.Maintenant que TypeScript 2.2 prend en charge les mixins via les expressions de classe, nous avons une bien meilleure façon d'exprimer des mixins sur des composants. N'oubliez pas que vous pouvez également utiliser l'héritage des composants depuis angular 2.3 ( discussion ) ou un décorateur personnalisé comme indiqué dans d'autres réponses ici. Cependant, je pense que les Mixins ont certaines propriétés qui les rendent préférables pour la réutilisation du comportement entre les composants:
Je vous suggère fortement de lire l'annonce TypeScript 2.2 ci-dessus pour comprendre le fonctionnement de Mixins. Les discussions liées dans les problèmes angulaires de GitHub fournissent des détails supplémentaires.
Vous aurez besoin de ces types:
Et puis vous pouvez déclarer un Mixin comme ce
Destroyable
mixin qui aide les composants à garder une trace des abonnements qui doivent être supprimés dansngOnDestroy
:Pour mélanger
Destroyable
dans unComponent
, vous déclarez votre composant comme ceci:Notez que cela
MixinRoot
n'est nécessaire que lorsque vous souhaitezextend
une composition Mixin. Vous pouvez facilement étendre plusieurs mixins, par exempleA extends B(C(D))
. C'est la linéarisation évidente des mixins dont je parlais ci-dessus, par exemple, vous composez effectivement une hiérarchie d'héritageA -> B -> C -> D
.Dans d'autres cas, par exemple lorsque vous souhaitez composer des Mixins sur une classe existante, vous pouvez appliquer le Mixin comme ceci:
Cependant, j'ai trouvé que la première méthode fonctionne le mieux pour
Components
etDirectives
, car ils doivent également être décorés avec@Component
ou de@Directive
toute façon.la source
function Destroyable<T extends Constructor<{}>>(Base = class { } as T)
. De cette façon, je peux créer des mixins en utilisantextends Destroyable()
.mettre à jour
L'héritage des composants est pris en charge depuis la version 2.3.0-rc.0
original
Jusqu'à présent, le plus pratique pour moi est de conserver le modèle et les styles dans des fichiers
*html
& séparés*.css
et de les spécifier viatemplateUrl
etstyleUrls
, donc c'est facilement réutilisable.la source
@Input()
et@Output()
décorateurs, le fait?Pour autant que je sache, l'héritage des composants n'a pas encore été implémenté dans Angular 2 et je ne suis pas sûr s'ils ont l'intention de le faire, mais comme Angular 2 utilise du dactylographié (si vous avez décidé de suivre cette voie), vous pouvez utiliser l'héritage de classe en faisant
class MyClass extends OtherClass { ... }
. Pour l'héritage des composants, je vous suggère de vous impliquer dans le projet Angular 2 en allant sur https://github.com/angular/angular/issues et en soumettant une demande de fonctionnalité!la source
export class MyPanelComponent extends BasePanelComponent
), le problème est uniquement dans le cas où les annotations / décorateurs ne sont pas hérités.@SubComponent()
) qui marque une classe comme sous-composant ou d'avoir un champ supplémentaire sur le@Component
décorateur qui vous permet de référencer un composant parent dont hériter.Laissez-nous comprendre certaines limitations et fonctionnalités clés du système d'héritage des composants d'Angular.
Le composant n'hérite que de la logique de classe:
Ces caractéristiques sont très importantes à garder à l'esprit, alors examinons chacune d'elles indépendamment.
Le composant n'hérite que de la logique de classe
Lorsque vous héritez d'un composant, toute la logique à l'intérieur est également héritée. Il est à noter que seuls les membres publics sont hérités car les membres privés ne sont accessibles que dans la classe qui les implémente.
Toutes les méta-données du décorateur @Component ne sont pas héritées
Le fait qu'aucune méta-donnée ne soit héritée peut sembler contre-intuitif au début, mais si vous y réfléchissez, cela a en fait un sens. Si vous héritez d'un composant, disons (componentA), vous ne voudriez pas que le sélecteur de ComponentA, dont vous héritez, remplace le sélecteur de ComponentB qui est la classe qui hérite. La même chose peut être dite pour le template / templateUrl ainsi que pour le style / styleUrls.
Les propriétés des composants @Input et @Output sont héritées
C'est une autre fonctionnalité que j'aime vraiment à propos de l'héritage de composants dans Angular. En une phrase simple, chaque fois que vous avez une propriété @Input et @Output personnalisée, ces propriétés sont héritées.
Le cycle de vie des composants n'est pas hérité
Cette partie est celle qui n'est pas si évidente, en particulier pour les personnes qui n'ont pas beaucoup travaillé avec les principes de la POO. Par exemple, supposons que vous ayez ComponentA qui implémente l'un des nombreux hooks de cycle de vie d'Angular comme OnInit. Si vous créez ComponentB et héritez de ComponentA, le cycle de vie OnInit de ComponentA ne se déclenchera pas tant que vous ne l'aurez pas appelé explicitement, même si vous avez ce cycle de vie OnInit pour ComponentB.
Appel des méthodes de composant Super / Base
Afin d'avoir la méthode ngOnInit () de ComponentA fire, nous devons utiliser le mot clé super, puis appeler la méthode dont nous avons besoin, qui dans ce cas est ngOnInit. Le mot-clé super fait référence à l'instance du composant hérité dont dans ce cas sera ComponentA.
la source
si vous lisez les bibliothèques CDK et les bibliothèques de matériaux, elles utilisent l'héritage mais pas tant pour les composants eux-mêmes, la projection de contenu est roi de l'OMI. voir ce lien https://blog.angular-university.io/angular-ng-content/ où il est dit "le problème clé avec cette conception"
Je sais que cela ne répond pas à votre question, mais je pense vraiment que l'héritage / l'extension des composants doit être évité . Voici mon raisonnement:
Si la classe abstraite étendue par deux ou plusieurs composants contient une logique partagée: utilisez un service ou même créez une nouvelle classe dactylographiée pouvant être partagée entre les deux composants.
Si la classe abstraite ... contient des variables partagées ou des fonctions onClicketc, alors il y aura duplication entre le html des deux vues de composants étendus. C'est une mauvaise pratique et que le code HTML partagé doit être divisé en composant (s). Ces composants (parties) peuvent être partagés entre les deux composants.
Est-ce que je manque d'autres raisons pour avoir une classe abstraite pour les composants?
Un exemple que j'ai vu récemment était des composants étendant AutoUnsubscribe:
c'était bas parce que dans une grande base de code,
infiniteSubscriptions.push()
n'a été utilisé que 10 fois. L'importation et l'extensionAutoUnsubscribe
nécessitent en fait plus de code que l'ajoutmySubscription.unsubscribe()
de langOnDestroy()
méthode du composant lui-même, ce qui nécessitait de toute façon une logique supplémentaire.la source
Si quelqu'un cherche une solution mise à jour, la réponse de Fernando est à peu près parfaite. Sauf que cela
ComponentMetadata
est obsolète. En utilisantComponent
place a fonctionné pour moi.Le
CustomDecorator.ts
fichier complet du décorateur personnalisé ressemble à ceci:Ensuite, importez-le dans votre nouveau
sub-component.component.ts
fichier de composant et utilisez à la@CustomComponent
place de@Component
ceci:la source
Vous pouvez hériter de @Input, @Output, @ViewChild, etc. Regardez l'exemple:
la source
Les composants peuvent être étendus de la même manière qu'un héritage de classe dactylographié, simplement que vous devez remplacer le sélecteur avec un nouveau nom. Toutes les propriétés d'entrée () et de sortie () du composant parent fonctionnent normalement
Mettre à jour
@Component est un décorateur,
Les décorateurs sont appliqués lors de la déclaration de classe et non sur les objets.
Fondamentalement, les décorateurs ajoutent des métadonnées à l'objet de classe et qui ne sont pas accessibles via l'héritage.
Si vous souhaitez obtenir l'héritage du décorateur, je vous suggère d'écrire un décorateur personnalisé. Quelque chose comme l'exemple ci-dessous.
Référez-vous: https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7
la source
la source