0

At the beginning I would like to say that I know that there is ready to use stepper from CDK or Materials but I would like to create "my own" in order to learning angular 8.

To be honest I don't know where I should search help now that is why I decided to write here. Maybe someone give me some advices and I will publish my work at the end.

So let's begin: The index HTML code looks like that:

<stepper>
<step>
    <ng-template step-label>First name</ng-template>
    <input placeholder="First name" required>
    <div>
        <button >Next</button>
    </div>
</step>
<step>
    <ng-template step-label>Last name</ng-template>
    <input placeholder="Last name" required>
    <div>
        <button >Next</button>
    </div>
</step>
<step>
    <ng-template step-label>Address name</ng-template>
    <input placeholder="Address" required>
    <div>
        <button >Next</button>
    </div>
</step>
</stepper>

Components + directive

@Directive({selector: '[step-label]'})
export class StepLabel{
    constructor(public template: TemplateRef<any>){};
}

@Component({selector: 'step-header', templateUrl: 'step-header.html'})
export class StepHeader{ 

    @Input() label: TemplateRef<StepLabel>;
}

@Component({
    selector: 'step',
    template: '<ng-template><ng-content></ng-content></ng-template>'
})
export class Step{ 

    @ViewChild(TemplateRef, {static: true}) content: TemplateRef<Step>;
    @ContentChild(StepLabel, {static: true}) label: TemplateRef<StepLabel>;

}

@Component({selector: 'stepper', templateUrl: './stepper.html'})
export class Stepper implements AfterContentInit {
    stepsArray = [];
    @ContentChildren(Step) steps: QueryList<Step>;

    ngAfterContentInit() {
        this.stepsArray = this.steps.toArray();
    }
}

stepper.html

 <div class="header-steps" style="bordeR: 1px solid green; padding: 10px;">
    <ng-container *ngFor="let step of stepsArray; let i = index; let isLast = last">
        <step-header [label]="step.label"></step-header>
    </ng-container>
</div>

<div style="border: 1px solid blue; padding: 5px; margin: 5px;" class="content-steps">      
    <div *ngFor="let step of stepsArray; let i = index">
        <ng-container *ngIf="step" [ngTemplateOutlet]="step.content"></ng-container>
    </div>
</div>

step=header.html

<div class="step-label">
  <ng-container *ngIf="label" [ngTemplateOutlet]="label.template"></ng-container>
</div>

I have updated whole "basic" project. It works. I have one quesition. Why in the Directive StepLabel have to be constructor. I tried to add somethink like that :

@ViewChild(TemplateRef, {static: true}) template: TemplateRef<StepLabel>;

but it doesn't work.

Micchaleq
  • 433
  • 1
  • 5
  • 21

1 Answers1

1

In your step component's template wrap ng-content in ng-template

template: '<ng-template><ng-content></ng-content></ng-template>'

And then get template using ViewChild

@ViewChild(TemplateRef, { static: true }) content: TemplateRef<any>;

In the end you'll get something like:

@Component({
    selector: 'step',
    template: '<ng-template><ng-content></ng-content></ng-template>'
})
export class Step { 
    @ViewChild(TemplateRef, { static: true }) content: TemplateRef<any>;
}
Yevgeniy.Chernobrivets
  • 3,194
  • 2
  • 12
  • 14
  • Thank you. It works. Could you explain why template of selector 'step' have to be like that? – Micchaleq Oct 24 '19 at 17:04
  • ngTemplateOutlet directive requires TemplateRef. One way to get it is by using ng-template. So by wrapping ng-content into ng-template you are basically getting content of your element into TemplateRef. – Yevgeniy.Chernobrivets Oct 24 '19 at 17:47
  • Thank you. I have updated project, I have one more question (about constructor). If u can please answer. – Micchaleq Oct 24 '19 at 19:58