angular - घटकों को कैसे बढ़ाया/इनहेरिट किया जाए?




typescript inheritance (7)

मैं पहले से ही लगभग पूरी तरह से फिर से लिखने के बिना, कोणीय 2 में पहले से ही तैनात कुछ घटकों के लिए एक्सटेंशन बनाना चाहूंगा, क्योंकि आधार घटक परिवर्तनों से गुजर सकते हैं और चाहते हैं कि ये परिवर्तन इसके व्युत्पन्न घटकों में भी परिलक्षित हों।

मैंने अपने सवालों को बेहतर तरीके से समझाने की कोशिश करने के लिए यह सरल उदाहरण बनाया:

निम्नलिखित आधार घटक 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);
  }
}

क्या आप केवल एक और व्युत्पन्न घटक बनाना चाहेंगे, उदाहरण के लिए, रंग, 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";
  }
}

प्लंकर में काम करने का पूरा उदाहरण

नोट: स्पष्ट रूप से यह उदाहरण सरल है और इसे हल किया जा सकता है अन्यथा विरासत का उपयोग करने की कोई आवश्यकता नहीं है, लेकिन इसका उद्देश्य केवल वास्तविक समस्या का वर्णन करना है।

जैसा कि आप व्युत्पन्न घटक app/my-panel.component.ts के कार्यान्वयन में देख सकते हैं, कार्यान्वयन का अधिकांश भाग दोहराया गया था, और वास्तव में विरासत में मिला एकल भाग BasePanelComponent @Component था, लेकिन @Component को मूल रूप से पूरी तरह से दोहराया जाना चाहिए, selector: 'my-panel' रूप में केवल परिवर्तित भाग नहीं selector: 'my-panel'

क्या एक घटक Angular2 की शाब्दिक पूर्ण विरासत बनाने का कोई तरीका है, @Component के लिए @Component / एनोटेशन की class परिभाषा विरासत में मिली, जैसे कि @Component ?

संपादित 1 - फ़ीचर अनुरोध

फ़ीचर अनुरोध कोणीय 2 को GitHub पर प्रोजेक्ट में जोड़ा गया: विस्तार / Inherit कोणीय 2 घटक एनोटेशन # 7968

2 संपादित करें - बंद अनुरोध

अनुरोध बंद कर दिया गया था, इस कारण से , संक्षेप में नहीं पता होगा कि डेकोरेटर को कैसे मर्ज किया जाएगा। हमें कोई विकल्प नहीं छोड़कर। इसलिए मेरी राय को अंक में उद्धृत किया गया है


अगर किसी को एक अद्यतन समाधान की तलाश है, तो फर्नांडो का जवाब बहुत ज्यादा सही है। सिवाय इसके कि ComponentMetadata को हटा दिया गया है। मेरे बजाय काम करने वाले Component का उपयोग करना।

पूर्ण कस्टम डेकोरेटर CustomDecorator.ts फ़ाइल कुछ इस प्रकार है:

import 'zone.js';
import 'reflect-metadata';
import { Component } from '@angular/core';
import { isPresent } from "@angular/platform-browser/src/facade/lang";

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (isPresent(parentAnnotation[key])) {
        // verify is annotation typeof function
        if(typeof annotation[key] === 'function'){
          annotation[key] = annotation[key].call(this, parentAnnotation[key]);
        }else if(
          // force override in annotation base
          !isPresent(annotation[key])
        ){
          annotation[key] = parentAnnotation[key];
        }
      }
    });

    var metadata = new Component(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
  }
}

फिर इसे अपने नए कंपोनेंट sub-component.component.ts फ़ाइल में @CustomComponent करें और इस तरह से @Component @CustomComponent जगह @CustomComponent उपयोग करें:

import { CustomComponent } from './CustomDecorator';
import { AbstractComponent } from 'path/to/file';

...

@CustomComponent({
  selector: 'subcomponent'
})
export class SubComponent extends AbstractComponent {

  constructor() {
    super();
  }

  // Add new logic here!
}

अब जब टाइपस्क्रिप्ट 2.2 क्लास एक्सप्रेशंस के माध्यम से मिक्सिंस का समर्थन करता है तो हमारे पास मिक्सिंस को अवयवों पर व्यक्त करने का एक बेहतर तरीका है। आप पर ध्यान दें कि आप कोणीय 2.3 ( discussion ) या एक कस्टम डेकोरेटर के बाद से घटक उत्तराधिकार का उपयोग कर सकते हैं जैसा कि यहां अन्य उत्तरों में चर्चा की गई है। हालाँकि, मुझे लगता है कि मिक्सिंस में कुछ गुण हैं जो उन्हें घटकों के व्यवहार को पुन: उपयोग करने के लिए बेहतर बनाते हैं:

  • मिक्सिंस अधिक लचीले ढंग से रचना करते हैं, अर्थात आप मौजूदा घटकों पर मिक्सिन्स को मिला सकते हैं और नए घटकों को बनाने के लिए मिक्सिंस को मिला सकते हैं
  • मिक्सिन रचना एक वर्ग वंशानुक्रम पदानुक्रम के लिए इसके स्पष्ट रैखिककरण के लिए धन्यवाद समझना आसान रहता है
  • आप डेकोरेटर इनहेरिटेंस ( discussion ) में डेकोरेटर और एनोटेशन वाले मुद्दों से आसानी से बच सकते हैं

मैं दृढ़ता से सुझाव देता हूं कि मिक्सिन्स कैसे काम करते हैं, यह समझने के लिए आप ऊपर टाइपस्क्रिप्ट 2.2 की घोषणा पढ़ें। कोणीय GitHub मुद्दों में लिंक की गई चर्चा अतिरिक्त विस्तार प्रदान करती है।

आपको इन प्रकारों की आवश्यकता होगी:

export type Constructor<T> = new (...args: any[]) => T;

export class MixinRoot {
}

और फिर आप इस तरह के ngOnDestroy को ngOnDestroy घोषणा कर सकते हैं जो घटकों को सब्सक्रिप्शन का ट्रैक रखने में मदद करता है ngOnDestroy में ngOnDestroy आवश्यकता ngOnDestroy :

export function Destroyable<T extends Constructor<{}>>(Base: T) {
  return class Mixin extends Base implements OnDestroy {
    private readonly subscriptions: Subscription[] = [];

    protected registerSubscription(sub: Subscription) {
      this.subscriptions.push(sub);
    }

    public ngOnDestroy() {
      this.subscriptions.forEach(x => x.unsubscribe());
      this.subscriptions.length = 0; // release memory
    }
  };
}

एक Component में Destroyable करने योग्य मिश्रण करने के लिए, आप अपने घटक को इस तरह घोषित करते हैं:

export class DashboardComponent extends Destroyable(MixinRoot) 
    implements OnInit, OnDestroy { ... }

ध्यान दें कि MixinRoot केवल तभी आवश्यक है जब आप एक Mixin संरचना का extend करना चाहते हैं। आप आसानी से कई मिश्रणों का विस्तार कर सकते हैं जैसे कि A extends B(C(D)) । यह उन मिश्रणों का स्पष्ट रेखीयकरण है जिनके बारे में मैं ऊपर बात कर रहा था, जैसे आप प्रभावी रूप से एक वंशानुक्रम पदानुक्रम A -> B -> C -> D रचना कर रहे हैं।

अन्य मामलों में, उदाहरण के लिए, जब आप किसी मौजूदा वर्ग पर मिक्सिंस की रचना करना चाहते हैं, तो आप मिक्सिन को इस तरह से लागू कर सकते हैं:

const MyClassWithMixin = MyMixin(MyClass);

हालांकि, मैंने पाया कि पहला तरीका Components और @Component लिए सबसे अच्छा काम करता है, क्योंकि इन्हें भी वैसे भी @Component या @Directive साथ सजाया जाना चाहिए।


कोणीय 2 संस्करण 2.3 को अभी जारी किया गया था, और इसमें मूल घटक वंशानुक्रम शामिल है। ऐसा लगता है कि आप टेम्पलेट और शैलियों को छोड़कर, जो कुछ भी आप चाहते हैं उसे विरासत में प्राप्त कर सकते हैं और ओवरराइड कर सकते हैं। कुछ संदर्भ:


घटकों को एक टाइपस्क्रिप्ट वर्ग विरासत के रूप में ही बढ़ाया जा सकता है, बस आपको नए नाम के साथ चयनकर्ता को ओवरराइड करना होगा। मूल इनपुट से सभी इनपुट () और आउटपुट () गुण सामान्य के रूप में काम करते हैं

अद्यतन करें

@Component एक डेकोरेटर है,

डेकोरेटर्स को वस्तुओं पर नहीं कक्षा की घोषणा के दौरान लागू किया जाता है।

मूल रूप से, सज्जाकार वर्ग वस्तु में कुछ मेटाडेटा जोड़ते हैं और जिसे वंशानुक्रम के माध्यम से एक्सेस नहीं किया जा सकता है।

यदि आप डेकोरेटर इनहेरिटेंस प्राप्त करना चाहते हैं तो मैं एक कस्टम डेकोरेटर लिखना चाहूंगा। उदाहरण के नीचे कुछ।

export function CustomComponent(annotation: any) {
    return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;

    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
    var parentParamTypes = Reflect.getMetadata('design:paramtypes', parentTarget);
    var parentPropMetadata = Reflect.getMetadata('propMetadata', parentTarget);
    var parentParameters = Reflect.getMetadata('parameters', parentTarget);

    var parentAnnotation = parentAnnotations[0];

    Object.keys(parentAnnotation).forEach(key => {
    if (isPresent(parentAnnotation[key])) {
        if (!isPresent(annotation[key])) {
        annotation[key] = parentAnnotation[key];
        }
    }
    });
    // Same for the other metadata
    var metadata = new ComponentMetadata(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
    };
};

संदर्भ: https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7


मुझे पता है कि यह आपके सवाल का जवाब नहीं देता है लेकिन मुझे लगता है कि इनहेरिट करने / फैलाने वाले घटकों से बचा जाना चाहिए । यहाँ मेरा तर्क है:

यदि दो या दो से अधिक घटकों द्वारा विस्तारित सार वर्ग में साझा तर्क होते हैं: एक सेवा का उपयोग करें या यहां तक ​​कि एक नया टाइपस्क्रिप्ट वर्ग बनाएं जो दो घटकों के बीच साझा किया जा सकता है।

यदि सार वर्ग ... में साझा चर या onClicketc फ़ंक्शन शामिल हैं, तो दो अतिरिक्त घटक विचारों के HTML के बीच दोहराव होगा। यह बुरा अभ्यास है और साझा किए गए HTML को घटक (एस) में तोड़ने की आवश्यकता है। इन घटक (भागों) को दो घटकों के बीच साझा किया जा सकता है।

क्या मुझे घटकों के लिए एक सार वर्ग होने के अन्य कारण याद आ रहे हैं?

एक उदाहरण जो मैंने हाल ही में देखा, वह था AutoUnsubscribe करने वाले घटक:

import { Subscription } from 'rxjs';
import { OnDestroy } from '@angular/core';
export abstract class AutoUnsubscribeComponent implements OnDestroy {
  protected infiniteSubscriptions: Array<Subscription>;

  constructor() {
    this.infiniteSubscriptions = [];
  }

  ngOnDestroy() {
    this.infiniteSubscriptions.forEach((subscription) => {
      subscription.unsubscribe();
    });
  }
}

यह आधार था क्योंकि एक बड़े कोडबेस में, अनंतश्रेणी.स्पश () केवल 10 बार उपयोग किया गया था। इसके अलावा AutoUnsubscribe का आयात और विस्तार करना वास्तव में घटक के ngOnDestroy () विधि में mySubscription.unsubscribe () को जोड़ने की तुलना में अधिक कोड लेता है, जिसे वैसे भी अतिरिक्त तर्क की आवश्यकता होती है।


अद्यतन करें

घटक विरासत 2.3.0-rc.0 बाद से समर्थित है

मूल

अब तक, मेरे लिए सबसे सुविधाजनक टेम्पलेट और शैलियों को अलग-अलग *html और *.css फ़ाइलों में रखना है और templateUrl और styleUrls माध्यम से निर्दिष्ट करना है, इसलिए यह आसान पुन: प्रयोज्य है।

@Component {
    selector: 'my-panel',
    templateUrl: 'app/components/panel.html', 
    styleUrls: ['app/components/panel.css']
}
export class MyPanelComponent extends BasePanelComponent

just use inheritance,Extend parent class in child class and declare constructor with parent class parameter and this parameter use in super().

1.parent class
@Component({
    selector: 'teams-players-box',
    templateUrl: '/maxweb/app/app/teams-players-box.component.html'
})
export class TeamsPlayersBoxComponent {
    public _userProfile:UserProfile;
    public _user_img:any;
    public _box_class:string="about-team teams-blockbox";
    public fullname:string;
    public _index:any;
    public _isView:string;
    indexnumber:number;
    constructor(
        public _userProfilesSvc: UserProfiles,
        public _router:Router,
    ){}
2.child class
@Component({

    selector: '[teams-players-eligibility]',
    templateUrl: '/maxweb/app/app/teams-players-eligibility.component.html'
})
export class TeamsPlayersEligibilityComponent extends TeamsPlayersBoxComponent{

    constructor (public _userProfilesSvc: UserProfiles,
            public _router:Router) {
            super(_userProfilesSvc,_router);
        }
}




angular-directive