ค้นหาเว็บไซต์

typescript - Angular 5/6: อินเตอร์เฟซคอมโพเนนต์ - เป็นไปได้หรือ?


นี่คือสิ่งที่ฉันพยายามทำให้สำเร็จ: สร้างคอมโพเนนต์เพื่อห่อรอบ MatStepper ที่จะรับ 2 ถึง n ขั้นตอน โดยแต่ละขั้นตอนมีคอมโพเนนต์ของตัวเอง

พูดอย่างนั้น วิธีที่ฉันจะรู้ว่าจะทำสิ่งนั้นในภาษาต่างๆ ได้อย่างไร คือการสร้างอินเตอร์เฟซที่มีพฤติกรรมทั่วไป และนำไปใช้งานในคอมโพเนนต์ต่างๆ แต่ภายในคอมโพเนนต์ห่อหุ้มให้ใช้อินเตอร์เฟซนั้น

wizard-step.component.ts

export interface WizardStep {
  isValid: boolean;

  nextClicked(e);
  previousClicked(e);
}

wizard.component.ts

import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ProgressBarType } from '../progress-bar/ProgressBarType';
import { MatStepper } from '@angular/material';
import { WizardStep } from './wizard-step.component';

@Component({
  selector: 'app-wizard',
  templateUrl: './wizard.component.html',
  styleUrls: [ './wizard.component.css' ]
})
export class WizardComponent implements OnInit {

  progress: number;
  progressBarType = ProgressBarType.Progress;

  @Input() steps: WizardStep[] = [];

  /**
   * The material stepper instance.
   */
  @ViewChild('stepper') private stepper: MatStepper;

  constructor() {
  }


  ngOnInit() {
    this.calculateProgress(0);
  }

  public calculateProgress(index: number): void {
    this.progress = ((index + 1) / this.steps.length) * 100;
  }

  public next(e): void {
    this.steps[e.selectedIndex].nextClicked(e);
  }

  public previous(e): void {
    this.steps[e.selectedIndex].previousClicked(e);
  }

  public selectionChange(e):void {
    this.calculateProgress(e.selectedIndex);
  }
}

wizard.component.html

<panel>
  <progress-bar class="progress-margins" [progress]="progress" [type]="progressBarType"></progress-bar>
  <mat-horizontal-stepper class="hide-header" #stepper (selectionChange)="selectionChange($event)">
    <mat-step *ngFor="let step of steps">
      <ng-container *ngComponentOutlet="step"></ng-container>
    </mat-step>
  </mat-horizontal-stepper>

  <form-buttons
    primaryLabel="Next"
    (primaryButtonClicked)="stepper.next(); next($event)"
    secondaryLabel="Previous"
    (secondaryButtonClicked)="stepper.previous(); previous($event)">
  </form-buttons>
</panel>

จากนั้นฉันกำลังพยายามใช้คอมโพเนนต์นี้โดยการสร้างคอมโพเนนต์การตรวจสอบความถูกต้องพร้อมกับคอมโพเนนต์สำหรับแต่ละขั้นตอนของวิซาร์ด:

wizard-validation.component.ts

import { Component, OnInit } from '@angular/core';
import { WizardStep } from 'framework';
import { WizardValidationStep1Component } from '../wizard-validation-step1/wizard-validation-step1.component';
import { WizardValidationStep2Component } from '../wizard-validation-step2/wizard-validation-step2.component';
import { WizardValidationStep3Component } from '../wizard-validation-step3/wizard-validation-step3.component';

@Component({
  selector: 'app-wizard-validation',
  templateUrl: './wizard-validation.component.html',
  styleUrls: []
})
export class WizardValidationComponent implements OnInit {

  steps: WizardStep[] = [];

  constructor() {
  }

  ngOnInit() {
    this.steps.push(WizardValidationStep1Component);
    this.steps.push(WizardValidationStep2Component);
    this.steps.push(WizardValidationStep3Component);
  }

}

wizard-validation.component.html

<wizard [steps]="steps">
</wizard>

และสุดท้าย, WizardValidationStep1Component, WizardValidationStep2Component, และ WizardValidationStep3Component เหมือนกันในจุดนี้:

import { Component } from '@angular/core';

import { WizardStep } from 'framework';

@Component({
  selector: 'app-wizard-validation-step1',
  templateUrl: './wizard-validation-step1.component.html',
  styleUrls: []
})
export class WizardValidationStep1Component implements WizardStep {

  isValid: boolean;
  stepName = 'Step 1';

  constructor() {
    this.isValid = true;
  }

  nextClicked(e) {
    alert('Clicked next on ' + this.stepName);
  }

  previousClicked(e) {
    alert('Clicked previous on ' + this.stepName);
  }
}

ด้วย wizard-validation-step1.component.html:

<p>
  wizard-validation-step1 works!
</p>

ใน wizard.component.ts หากฉันใช้แอเรย์ที่ไม่มีการระบุชนิดแทนแอเรย์ WizardStep มันจะทำงานได้อย่างยอดเยี่ยม แต่แน่นอนว่าส่วนประกอบขั้นตอนของฉันจะไม่ลงทะเบียนการคลิก เป็นต้น

@Input() steps = [];

เมื่อใช้ตามที่แสดงด้านบน เมื่อฉันพยายามรัน ng build ฉันได้รับข้อความดังต่อไปนี้:

ERROR in src/app/wizard-validation/wizard-validation.component.ts(20,21): error TS2345: Argument of type 'typeof WizardValidationStep1Component' is not assignable to parameter of type 'WizardStep'.
  Property 'isValid' is missing in type 'typeof WizardValidationStep1Component'.
src/app/wizard-validation/wizard-validation.component.ts(21,21): error TS2345: Argument of type 'typeof WizardValidationStep2Component' is not assignable to parameter of type 'WizardStep'.
  Property 'isValid' is missing in type 'typeof WizardValidationStep2Component'.
src/app/wizard-validation/wizard-validation.component.ts(22,21): error TS2345: Argument of type 'typeof WizardValidationStep3Component' is not assignable to parameter of type 'WizardStep'.
  Property 'isValid' is missing in type 'typeof WizardValidationStep3Component'.

รูปแบบที่ฉันพยายามใช้นั้นถูกต้องใน Angular 5/6 หรือไม่? หากไม่ถูกต้อง มีวิธีอื่นในการทำสิ่งที่ฉันพยายามทำได้หรือไม่?

คำตอบ #1

ทำไมไม่ใช้ห้องสมุดล่ะ? อันนี้เจ๋งนะ

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

ดังนั้น แทนที่จะ

// wizard.component.ts

ngOnInit() {
  this.steps.push(WizardValidationStep1Component);
  this.steps.push(WizardValidationStep2Component);
  this.steps.push(WizardValidationStep3Component);
}

คุณจะทำมันแบบนี้

<!-- something-using-wizard.component.html -->

<my-wizard>
  <my-step1-component myStep [someInput]="true"></my-step1-component>
  <my-step2-component myStep [someOtherInput]="'foo'"></my-step2-component>
</my-wizard>

และจับตัวอย่างขั้นตอนของคุณใน WizardComponent โดยใช้ตัวตกแต่ง @ContentChildren()

// wizard.component.ts

@ContentChildren(MyStepDirective)
public steps: QueryList<MyStepInterface>;

คุณสามารถสร้างแรงบันดาลใจให้ตัวเองได้มากขึ้นจากโค้ดต้นฉบับของไลบรารีที่ฉันลิงก์ไว้ ผู้เขียนทำบางอย่างที่คล้ายกัน...

ฉันหวังว่าสิ่งนี้จะช่วยคุณได้บ้าง :-)

คำตอบ #2

ดังที่ได้กล่าวไว้แล้วในความคิดเห็น steps: WizardStep[]=[]; คาดหวังตัวอย่างของคอมโพเนนต์ คุณส่งฟังก์ชันตัวสร้าง ไปใช้รูปแบบไวยากรณ์สำหรับสิ่งนั้นคือ

steps: { new(): WizardStep }[] = [];

บทความที่เกี่ยวข้อง