วิธีการขยาย / รับช่วงส่วนประกอบ?


160

ฉันต้องการสร้างส่วนขยายสำหรับส่วนประกอบบางส่วนที่ปรับใช้แล้วใน Angular 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";
  }
}

ทำตัวอย่างการทำงานให้สมบูรณ์ใน Plunker

หมายเหตุ: เห็นได้ชัดว่าตัวอย่างนี้ง่ายและสามารถแก้ไขได้มิฉะนั้นไม่จำเป็นต้องใช้การสืบทอด แต่มันมีวัตถุประสงค์เพียงเพื่อแสดงให้เห็นถึงปัญหาที่แท้จริง

ที่คุณสามารถดูในการดำเนินงานขององค์ประกอบอนุพันธ์app/my-panel.component.tsมากของการดำเนินการซ้ำและส่วนเดียวที่สืบทอดมาจริงๆเป็นclass BasePanelComponentแต่มีการทำซ้ำโดยทั่วไปสมบูรณ์ไม่ได้เป็นเพียงบางส่วนที่มีการเปลี่ยนแปลงเป็น@Componentselector: 'my-panel'

มีวิธีใดบ้างที่จะสร้างการสืบทอดแบบเต็มรูปแบบของส่วนประกอบ Angular2 โดยการสืบทอดclassคำจำกัดความของการทำเครื่องหมาย / คำอธิบายประกอบตัวอย่างเช่น@Component?

แก้ไข 1 - คำขอคุณสมบัติ

เพิ่มคำขอคุณลักษณะ angular2 ในโครงการบน GitHub: เพิ่ม/ รับมรดกส่วนประกอบ angular2 คำอธิบายประกอบ # 7968

แก้ไข 2 - ปิดคำขอ

คำขอถูกปิดด้วยเหตุนี้ที่จะไม่ทราบวิธีรวมผู้ตกแต่งภายในสั้น ๆ ปล่อยให้เราไม่มีทางเลือก ดังนั้นความคิดของฉันจะถูกอ้างถึงในประเด็น


ตรวจสอบคำตอบนี้stackoverflow.com/questions/36063627/…ขอแสดงความนับถือ
NicolasB

ตกลง NicolasB แต่ปัญหาของฉันคือการสืบทอดของ decorator @Component ซึ่งไม่ได้ใช้กับข้อมูลเมตาของการสืบทอด = /
Fernando Leal

คนโปรดหลีกเลี่ยงการใช้มรดกกับเชิงมุม เช่นคลาสส่งออก PlannedFilterComponent ขยาย AbstractFilterComponent ใช้ OnInit {ไม่ดีมาก มีวิธีอื่นในการแชร์รหัสเช่นบริการและส่วนประกอบที่เล็กลง การสืบทอดไม่ใช่วิธีเชิงมุม ฉันอยู่ในโครงการมุมที่พวกเขาใช้การสืบทอดและมีบางสิ่งที่แตกหักเช่นการส่งออกส่วนประกอบที่สืบทอดมาจากส่วนประกอบนามธรรมที่ไม่มีอินพุตของคลาสนามธรรม
เบิร์ตคิง

1
ใช้การฉายเนื้อหาแทนเช่นgithub.com/angular/components/blob/master/src/material/card/ …อย่าใช้การสืบทอด
robert king

คำตอบ:


39

ทางเลือกอื่น ๆ :

คำตอบของ Thierry Templier นี้เป็นอีกทางเลือกหนึ่งในการแก้ไขปัญหา

หลังจากคำถามบางข้อกับ Thierry Templier ฉันมาถึงตัวอย่างการทำงานต่อไปนี้ที่ตรงกับความคาดหวังของฉันเป็นทางเลือกสำหรับข้อ จำกัด การสืบทอดที่กล่าวถึงในคำถามนี้:

1 - สร้างมัณฑนากรที่กำหนดเอง:

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);
  }
}

2 - ส่วนประกอบฐานพร้อมมัณฑนากร @Component:

@Component({
  // create seletor base for test override property
  selector: 'master',
  template: `
    <div>Test</div>
  `
})
export class AbstractComponent {

}

3 - ส่วนประกอบย่อยด้วยมัณฑนากร @CustomComponent:

@CustomComponent({
  // override property annotation
  //selector: 'sub',
  selector: (parentSelector) => { return parentSelector + 'sub'}
})
export class SubComponent extends AbstractComponent {
  constructor() {
  }
}

Plunkr พร้อมตัวอย่างสมบูรณ์


3
ฉันคิดว่าสิ่งนี้จะเข้ากันไม่ได้กับคอมไพเลอร์เทมเพลตออฟไลน์
GünterZöchbauer

@ GünterZöchbauerฉันไม่มีความรู้เกี่ยวกับ "เทมเพลตคอมไพเลอร์ออฟไลน์" ของ Angular2 แต่ฉันคิดว่ามันอาจจะไม่เข้ากันและมันก็เป็นทางเลือกอื่น โหมด "ตัวแปลเทมเพลตออฟไลน์" ของ Angular2 จะมีประโยชน์ที่ไหน คุณสามารถแสดงบางสิ่งให้ฉันเข้าใจเกี่ยวกับเรื่องนี้ได้ดีขึ้น ดังนั้นฉันสามารถเข้าใจถึงความสำคัญของความเข้ากันได้นี้สำหรับโครงการของฉัน
Fernando Leal

คอมไพเลอร์เทมเพลตออฟไลน์ (OTC) ยังไม่สามารถใช้งานได้แม้ว่าจะรวมอยู่ใน RC.3 แล้วก็ตาม OTC จะวิเคราะห์มัณฑนากรและสร้างรหัสในระหว่างขั้นตอนการสร้างเมื่อมีการสร้างการปรับใช้ OTC อนุญาตให้ลบตัวแยกวิเคราะห์และคอมไพเลอร์ของ Angular2 ที่ประมวลผลตัวตกแต่งและการรวมที่รันไทม์ซึ่งนำไปสู่ขนาดรหัสที่เล็กลงและการเริ่มต้นแอปพลิเคชันและส่วนประกอบที่เร็วขึ้น OTC อาจใช้งานได้ด้วยการอัปเดตครั้งต่อไป
GünterZöchbauer

1
@ GünterZöchbauerตอนนี้ฉันเข้าใจความสำคัญในการบำรุงรักษาฟังก์ชั่นที่เข้ากันได้กับ OTC มันจะเป็นการรวบรวมล่วงหน้าของเครื่องมือตกแต่งมุมเพื่อลดค่าใช้จ่ายในการเริ่มต้นส่วนประกอบ ฉันต้องการทราบเกี่ยวกับการทำงานของกระบวนการนี้และเนื่องจากคำตอบของคำตอบนี้จะไม่สามารถทำงานร่วมกับ OTC ได้หรือไม่ การรวบรวมนักตกแต่งล่วงหน้าเป็นอย่างไร? มีความรู้นี้เราอาจคิดว่าบางสิ่งบางอย่างเพื่อให้ทางเลือกการทำงานนี้เพื่อ OTC ขอบคุณสำหรับการชี้แจง!
Fernando Leal

24

Angular 2 เวอร์ชัน 2.3 เพิ่งเปิดตัวและรวมถึงการสืบทอดส่วนประกอบดั้งเดิม ดูเหมือนว่าคุณสามารถสืบทอดและแทนที่สิ่งที่คุณต้องการยกเว้นเทมเพลตและสไตล์ อ้างอิงบางส่วน:


"gotcha" หนึ่งที่นี่เกิดขึ้นเมื่อคุณลืมระบุ "ตัวเลือก" ใหม่ในองค์ประกอบลูก คุณจะได้รับข้อผิดพลาดรันไทม์ตามบรรทัดMore than one component matched on this elementหากคุณไม่ได้
Aelfinn

@TheAelfinn ใช่: แต่ละองค์ประกอบจะต้องมีข้อมูลจำเพาะที่สมบูรณ์ใน@Component()แท็ก แต่คุณสามารถแบ่งปัน. html หรือ. css ได้โดยอ้างอิงไฟล์เดียวกันหากคุณต้องการ ทั้งหมดในทุกมันเป็นใหญ่บวก
Daniel Griscom

ในลิงค์ที่สองของคุณscotch.io/tutorials/component-inheritance-in-angular-2ผู้เขียนอ้างว่าส่วนประกอบต่างๆสืบทอดการบริการของผู้ปกครองที่พึ่งพารหัสของฉันแนะนำอย่างอื่น คุณสามารถยืนยันได้ว่าสิ่งนี้ได้รับการสนับสนุนหรือไม่?
Aelfinn

18

ตอนนี้TypeScript 2.2รองรับMixins ผ่านการแสดงออก Classเรามีวิธีที่ดีกว่าในการแสดง Mixins บน Components โปรดทราบว่าคุณสามารถใช้การสืบทอดส่วนประกอบได้ตั้งแต่ 2.3 เชิงมุม ( การสนทนา ) หรือผู้ตกแต่งที่กำหนดเองตามที่กล่าวไว้ในคำตอบอื่น ๆ ที่นี่ อย่างไรก็ตามฉันคิดว่า Mixins มีคุณสมบัติบางอย่างที่ทำให้พวกเขาดีขึ้นสำหรับการใช้ซ้ำพฤติกรรมในส่วนประกอบ:

  • Mixins สร้างความยืดหยุ่นได้มากขึ้นเช่นคุณสามารถผสมและจับคู่ Mixins กับส่วนประกอบที่มีอยู่หรือรวม Mixins เพื่อสร้างส่วนประกอบใหม่
  • องค์ประกอบ Mixin ยังคงง่ายต่อการเข้าใจเนื่องจากการสร้างเส้นตรงที่ชัดเจนให้กับลำดับชั้นการสืบทอดคลาส
  • คุณสามารถหลีกเลี่ยงปัญหาเกี่ยวกับมัณฑนากรและคำอธิบายประกอบที่ทำให้เกิดปัญหาการสืบทอดองค์ประกอบ ( การสนทนา ) ได้ง่ายขึ้น

ฉันขอแนะนำให้คุณอ่านประกาศ TypeScript 2.2 ข้างต้นเพื่อทำความเข้าใจวิธีการทำงานของ Mixins การอภิปรายที่เชื่อมโยงในประเด็น GitHub เชิงมุมให้รายละเอียดเพิ่มเติม

คุณจะต้องมีประเภทเหล่านี้:

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

export class MixinRoot {
}

จากนั้นคุณสามารถประกาศ Mixin เช่นDestroyablemixin นี้ที่ช่วยให้ส่วนประกอบติดตามการสมัครสมาชิกที่ต้องกำจัดใน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
    }
  };
}

ในการมิกซ์อินDestroyableเป็น a Componentคุณต้องประกาศองค์ประกอบของคุณดังนี้:

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

โปรดทราบว่าMixinRootจำเป็นเมื่อคุณต้องการextendจัดองค์ประกอบ Mixin เท่านั้น คุณสามารถขยาย mixins A extends B(C(D))หลายเช่น นี้เป็นเชิงเส้นที่ชัดเจนของ mixins A -> B -> C -> Dฉันถูกพูดถึงข้างต้นเช่นคุณอย่างมีประสิทธิภาพเขียนลำดับชั้น

ในกรณีอื่น ๆ เช่นเมื่อคุณต้องการเขียน Mixins ในคลาสที่มีอยู่คุณสามารถใช้ Mixin ดังนี้:

const MyClassWithMixin = MyMixin(MyClass);

อย่างไรก็ตามฉันพบว่าวิธีแรกใช้งานได้ดีที่สุดComponentsและDirectivesเนื่องจากสิ่งเหล่านี้ต้องตกแต่งด้วย@Componentหรือ@Directiveไม่


นี่มันเจ๋งมาก! ขอบคุณสำหรับคำแนะนำ เหตุใด MixinRoot จึงถูกใช้เป็นตัวยึดตำแหน่งว่างเปล่าที่นี่ แค่ต้องการให้แน่ใจว่าความเข้าใจของฉันถูกต้อง
Alex Lockwood

@AlexLockwood yup ตัวยึดคลาสว่างเปล่าเป็นสิ่งที่ฉันใช้ ฉันหลีกเลี่ยงการใช้อย่างมีความสุข แต่ตอนนี้ฉันไม่พบวิธีที่ดีกว่าในการทำเช่นนี้
Johannes Rudolph

2
function Destroyable<T extends Constructor<{}>>(Base = class { } as T)ฉันสิ้นสุดการใช้ วิธีที่ฉันสามารถสร้าง mixins extends Destroyable()ใช้
Alex Lockwood

1
สิ่งนี้ดูดีมาก แต่ดูเหมือนว่า AoT build (Cli1.3) จะลบ ngOnDestroy ออกจาก DashBoardComponent ตามที่ไม่เคยได้รับการเรียก (เช่นเดียวกับ ngOnInit)
dzolnjan

ขอบคุณสำหรับการแก้ปัญหานี้ อย่างไรก็ตามหลังจากการสร้างแยงด้วยไอออนิกหรือแองกูลาร์ - คลีไอมิกซ์นั้นก็ใช้งานไม่ได้ราวกับว่ามันไม่ได้ขยายออกไป
Han Che

16

ปรับปรุง

สนับสนุนการสืบทอดส่วนประกอบตั้งแต่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

2
นี่คือสิ่งที่ฉันต้องการ ผู้ตกแต่งภายใน @Component มีลักษณะอย่างไรสำหรับ BasePanelComponent มันสามารถอ้างอิงไฟล์ html / css ที่แตกต่างกันได้หรือไม่? มันสามารถอ้างอิงไฟล์ html / css เดียวกันที่อ้างอิงโดย MyPanelComponent ได้หรือไม่?
ebhh2001

1
สิ่งนี้ไม่ได้สืบทอด@Input()และ@Output()ตกแต่งภายในใช่ไหม
Leon Adler

10

เท่าที่ฉันรู้ว่าการสืบทอดองค์ประกอบยังไม่ได้รับการดำเนินการใน Angular 2 และฉันไม่แน่ใจว่าพวกเขามีแผนที่จะทำอย่างไรเนื่องจาก Angular 2 กำลังใช้ typescript (ถ้าคุณตัดสินใจที่จะไปเส้นทางนั้น) คุณสามารถใช้การสืบทอดคลาส class MyClass extends OtherClass { ... }โดยการทำ สำหรับการสืบทอดองค์ประกอบฉันขอแนะนำให้มีส่วนร่วมกับโครงการ Angular 2 โดยไปที่https://github.com/angular/angular/issuesและส่งคำขอคุณสมบัติ!


เข้าใจแล้วฉันจะลองในวันถัดไปทำซ้ำฉันโครงการ angular2 และตรวจสอบคุณสมบัติคำขอไม่ได้อยู่ในปัญหาโครงการใน Git และถ้าไม่ฉันจะวาดคำขอทรัพยากรเนื่องจากดูเหมือนว่าฉันน่าสนใจมาก ลักษณะเฉพาะ. มีแนวคิดที่ขัดแย้งกันใดที่จะทำให้คำขอที่น่าสนใจที่สุด?
Fernando Leal

1
เกี่ยวกับ typescript ของทรัพยากรการสืบทอดที่ฉันใช้อยู่แล้วในโซลูชันเริ่มต้นของฉัน ( export class MyPanelComponent extends BasePanelComponent) ปัญหาจะเกิดขึ้นเฉพาะในกรณีของ Annotations / Decorators เท่านั้นที่ไม่ได้รับมรดก
Fernando Leal

1
ใช่ฉันไม่รู้จริงๆว่าคุณสามารถเพิ่มอะไรได้อีก ฉันชอบความคิดของการมีมัณฑนากรใหม่ (คล้าย@SubComponent()) ที่ทำเครื่องหมายชั้นเรียนเป็นส่วนประกอบย่อยหรือมีเขตข้อมูลพิเศษใน@Componentมัณฑนากรที่ช่วยให้คุณสามารถอ้างอิงองค์ประกอบผู้ปกครองที่จะสืบทอด
watzon

1
เพิ่มคำขอคุณลักษณะ angular2 ในโครงการบน GitHub: ขยาย / รับสัญญาณประกอบองค์ประกอบ angular2 คำอธิบายประกอบ # 7968
Fernando Leal

9

ให้เราเข้าใจถึงข้อ จำกัด และคุณสมบัติที่สำคัญบางประการในระบบการสืบทอดส่วนประกอบของ Angular

คอมโพเนนต์สืบทอดคลาสตรรกะเท่านั้น:

  • ข้อมูลเมตาทั้งหมดในมัณฑนากร @Component ไม่ได้รับการถ่ายทอด
  • คุณสมบัติ Component @Input และคุณสมบัติ @Output ได้รับการสืบทอด
  • วงจรชีวิตของคอมโพเนนต์ไม่ได้รับการสืบทอด

คุณสมบัติเหล่านี้มีความสำคัญอย่างยิ่งที่จะต้องคำนึงถึงดังนั้นให้เราตรวจสอบแต่ละรายการอย่างอิสระ

คอมโพเนนต์สืบทอดคลาสตรรกะเท่านั้น

เมื่อคุณรับช่วงส่วนประกอบตรรกะทั้งหมดที่อยู่ในนั้นจะได้รับการสืบทอดเท่า ๆ กัน เป็นที่น่าสังเกตว่ามีเพียงสมาชิกสาธารณะเท่านั้นที่จะได้รับมรดกเนื่องจากสมาชิกส่วนตัวสามารถเข้าถึงได้เฉพาะในชั้นเรียนเท่านั้น

ข้อมูลเมตาทั้งหมดในมัณฑนากร @Component ไม่ได้รับการถ่ายทอด

ความจริงที่ว่าไม่มีเมตาดาต้าใดที่ได้รับการถ่ายทอดมานั้นอาจดูเหมือนง่ายในตอนแรก แต่ถ้าคุณคิดเกี่ยวกับสิ่งนี้มันก็สมเหตุสมผลดี หากคุณรับช่วงจาก Component say (componentA) คุณจะไม่ต้องการให้ตัวเลือกของ ComponentA ซึ่งคุณกำลังรับช่วงจากเพื่อแทนที่ตัวเลือกของ ComponentB ซึ่งเป็นคลาสที่สืบทอด เทมเพลต / templateUrl เช่นเดียวกับสไตล์ / styleUrls

คุณสมบัติ Component @Input และ @Output ได้รับการสืบทอด

นี่เป็นอีกคุณสมบัติที่ฉันชอบเกี่ยวกับการสืบทอดองค์ประกอบในเชิงมุมจริงๆ ในประโยคง่ายๆเมื่อใดก็ตามที่คุณมีคุณสมบัติ @Input และ @Output ที่กำหนดเองคุณสมบัติเหล่านี้จะได้รับการสืบทอด

วงจรชีวิตของคอมโพเนนต์ไม่ได้รับการสืบทอด

ส่วนนี้เป็นส่วนที่ไม่ชัดเจนโดยเฉพาะอย่างยิ่งกับคนที่ไม่ได้ทำงานอย่างกว้างขวางกับหลักการ OOP ตัวอย่างเช่นสมมติว่าคุณมี ComponentA ซึ่งใช้ฮุคของวงจรชีวิตหลายอันของ Angular เช่น OnInit ถ้าคุณสร้าง ComponentB และสืบทอด ComponentA วงจรชีวิต OnInit จาก ComponentA จะไม่เริ่มทำงานจนกว่าคุณจะเรียกมันอย่างชัดเจนแม้ว่าคุณจะมีวงจรชีวิต OnInit นี้สำหรับ ComponentB ก็ตาม

การเรียกใช้เมธอด Super / Base Component

ในการรับเมธอด ngOnInit () จาก ComponentA เราจำเป็นต้องใช้คีย์เวิร์ด super จากนั้นเรียกเมธอดที่เราต้องการซึ่งในกรณีนี้คือ ngOnInit คำสำคัญสุดหมายถึงอินสแตนซ์ขององค์ประกอบที่จะได้รับมรดกซึ่งในกรณีนี้จะเป็น ComponentA


5

ถ้าคุณอ่านผ่านห้องสมุด CDK และห้องสมุดวัสดุพวกเขากำลังใช้การสืบทอด แต่ไม่มากสำหรับส่วนประกอบเองการฉายเนื้อหาคือราชา IMO เห็นลิงค์นี้https://blog.angular-university.io/angular-ng-content/ที่มันบอกว่า "ปัญหาสำคัญกับการออกแบบนี้"

ฉันรู้ว่านี้ไม่ตอบคำถามของคุณ แต่ฉันคิดว่ามันสืบทอด / ชิ้นส่วนขยายที่ควรหลีกเลี่ยง นี่คือเหตุผลของฉัน:

หากคลาสนามธรรมที่ขยายโดยสองคอมโพเนนต์ขึ้นไปมีตรรกะที่ใช้ร่วมกันให้ใช้เซอร์วิสหรือสร้างคลาส typescript ใหม่ที่สามารถแบ่งใช้ระหว่างสองคอมโพเนนต์

หากคลาสนามธรรม ... มีตัวแปรที่แชร์หรือฟังก์ชั่น onClicketc จะมีการทำซ้ำระหว่าง html ของมุมมองคอมโพเนนต์ที่ขยายสองส่วน นี่เป็นแนวปฏิบัติที่ไม่ดี & HTML ที่ใช้ร่วมกันจะต้องแบ่งออกเป็นส่วน ๆ ชิ้นส่วนเหล่านี้ (ส่วน) สามารถใช้ร่วมกันระหว่างสององค์ประกอบ

ฉันไม่มีเหตุผลอื่นในการมีคลาสนามธรรมสำหรับส่วนประกอบหรือไม่

ตัวอย่างที่ฉันเห็นเมื่อเร็ว ๆ นี้คือส่วนประกอบที่ขยายการสมัครอัตโนมัติ:

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();
    });
  }
}

นี่เป็นรูปปั้นเพราะทั่วทั้ง codebase ขนาดใหญ่infiniteSubscriptions.push()ใช้เพียง 10 ครั้ง นอกจากนี้การนำเข้าและขยายAutoUnsubscribeจริง ๆ แล้วใช้รหัสมากกว่าการเพิ่มmySubscription.unsubscribe()ในngOnDestroy()วิธีการของส่วนประกอบเองซึ่งต้องการตรรกะเพิ่มเติมอยู่แล้ว


ตกลงฉันเข้าใจการจัดระเบียบของคุณและฉันเห็นด้วยว่าการรวมตัวเกือบจะแก้ปัญหาทั้งหมดที่ดูเหมือนว่าต้องการการสืบทอด และเป็นที่น่าสนใจเสมอที่จะคิดว่าส่วนประกอบต่างๆเป็นส่วนเล็ก ๆ ของแอปพลิเคชั่นที่สามารถเชื่อมต่อได้หลายวิธี แต่ในกรณีของคำถามปัญหาคือฉันไม่สามารถควบคุม / เข้าถึงการแก้ไขในองค์ประกอบที่ฉันต้องการรับช่วง (เป็นองค์ประกอบที่สาม) จากนั้นการรวมจะกลายเป็นไม่สามารถทำได้และการสืบทอดจะเป็นทางออกที่ดีที่สุด
Fernando Leal

ทำไมคุณไม่สามารถสร้างองค์ประกอบใหม่ที่สรุปส่วนประกอบของบุคคลที่สามได้ องค์ประกอบของบุคคลที่สามที่คุณสนใจคืออะไร? เช่น <my-calendar [stuff] = stuff> <third-party-calendar [stuff] = stuff> </ .. > </ .. >
robert king

@ Robertbert การทำซ้ำตัวเองเป็นรูปแบบที่อ่อนแอมาก ... นั่นเป็นเหตุผลที่คุณจะเริ่มเกลียดงานของคุณ .. แทนที่จะสนุกกับมัน
Dariusz Filipiak

สำหรับฉันมันเป็นความคิดที่ดีที่จะขยายส่วนประกอบในกรณีที่คุณต้องการมีพารามิเตอร์อินพุต / เอาท์พุตเดียวกันสำหรับชุดของส่วนประกอบดังนั้นพวกเขาจึงสามารถทำงานเป็นหนึ่งเดียว ตัวอย่างเช่นฉันมีหลายขั้นตอนการลงทะเบียน (credentialsStep, addressStep, selectBenefitsStep) พวกเขาทั้งหมดควรมีตัวเลือกอินพุตเดียวกัน (stepName, actionButtons ... ) และ Outputs (สมบูรณ์, ยกเลิก)
Sergey_T

@Sergey_T คุณสามารถพิจารณาองค์ประกอบหนึ่งที่มีการเลือก ng และการฉายเนื้อหาได้หรือไม่ การป้อนข้อมูลซ้ำสองสามครั้งดูเหมือนว่าคุณจะประหยัดกับการใช้งาน TBH
เบิร์ตคิง

2

หากใครกำลังมองหาโซลูชันที่ปรับปรุงแล้วคำตอบของ Fernando นั้นสมบูรณ์แบบมาก ยกเว้นว่าComponentMetadataได้เลิกใช้แล้ว ใช้Componentแทนทำงานได้สำหรับฉัน

CustomDecorator.tsไฟล์Custom Decorator แบบเต็มมีลักษณะดังนี้:

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สิ่งนี้:

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

...

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

  constructor() {
    super();
  }

  // Add new logic here!
}

นักตกแต่งที่กำหนดเองไม่ท้อแท้อย่างมาก? จากการโพสต์ / กระทู้อื่น ๆ อีกมากมายโซลูชันนี้ได้รับการทำเครื่องหมายว่าผิดอย่างสมบูรณ์เพราะ AOT จะไม่สนับสนุนพวกเขา?
TerNovi

2

คุณสามารถรับช่วง @Input, @Output, @ViewChild ฯลฯ ดูตัวอย่างได้ที่:

@Component({
    template: ''
})
export class BaseComponent {
    @Input() someInput: any = 'something';

    @Output() someOutput: EventEmitter<void> = new EventEmitter<void>();

}

@Component({
    selector: 'app-derived',
    template: '<div (click)="someOutput.emit()">{{someInput}}</div>',
    providers: [
        { provide: BaseComponent, useExisting: DerivedComponent }
    ]
})
export class DerivedComponent {

}

1

คอมโพเนนต์สามารถขยายได้เช่นเดียวกับการสืบทอดคลาส typescript เพียงแค่คุณต้องแทนที่ตัวเลือกด้วยชื่อใหม่ คุณสมบัติอินพุต () และเอาท์พุท () ทั้งหมดจากองค์ประกอบหลักทำงานตามปกติ

ปรับปรุง

@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


คุณช่วยยกตัวอย่าง (โดยใช้ตัวอย่างคำถาม) ได้ไหมว่ามันทำงานอย่างไร คุณสามารถใช้stackblitzเพื่อพัฒนาตัวอย่างและแชร์ลิงก์
Fernando Leal

@Component เป็นมัณฑนากรมัณฑนากรจะถูกนำไปใช้ในระหว่างการประกาศคลาสที่ไม่ได้อยู่ในวัตถุ
MAHESH VALIYA VEETIL

คุณพูดถูก นักตกแต่งไม่ได้สร้างความแตกต่าง มันเป็นสิ่งจำเป็นเท่านั้นหากองค์ประกอบฐานที่ใช้เป็นบางส่วนประกอบอื่น
MAHESH Valiya VEETIL

0
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);
        }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.