I am using a component-based mat stepper component to display a linear process. Each step have own component as below
<mat-card>
<mat-horizontal-stepper [linear]="isLinear" labelPosition="bottom" #stepper>
<!-- Step-1 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Select Items</ng-template>
<select-item-component>
<select-item-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Next</button>
</div>
</mat-step>
<!-- Step-2 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Add Quantity</ng-template>
<add-qty-component>
<add-qty-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Next</button>
</div>
</mat-step>
<!-- Step-3 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Conform</ng-template>
<conform-step-component>
<conform-step-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Done</button>
</div>
</mat-step>
</mat-horizontal-stepper>
</mat-card>
I am using a component-based mat stepper component to display a linear process. Each step have own component as below
<mat-card>
<mat-horizontal-stepper [linear]="isLinear" labelPosition="bottom" #stepper>
<!-- Step-1 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Select Items</ng-template>
<select-item-component>
<select-item-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Next</button>
</div>
</mat-step>
<!-- Step-2 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Add Quantity</ng-template>
<add-qty-component>
<add-qty-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Next</button>
</div>
</mat-step>
<!-- Step-3 -->
<mat-step [stepControl]="firstFormGroup">
<ng-template matStepLabel>Conform</ng-template>
<conform-step-component>
<conform-step-component>
<div class="mt-5">
<button mat-flat-button color="primary" matStepperNext>Done</button>
</div>
</mat-step>
</mat-horizontal-stepper>
</mat-card>
Step-1 shows the multi selectable list of items and pass selected item list to the next step-2 and add a quantity of each item in step-2.
How to pass selected items on Next click from step-1 to step-2 and display passed item to enter a quantity in step-2?
I have created a common service layer to set and get selected items. ngOnInit
of a component of step-2 trying to get the selected list from common service but issue is component-2 is already initiated before the next click.
Can do initialize or re-initialize the second component after the click of next in step-1?
How to display the selected items list in step-2 after moving from step-1?
What will be the best approach for the above scenario?
Just a link to any reference that can answer my question, it should be enough.
Thank you.
Share Improve this question asked Mar 3, 2020 at 13:22 Vijay VavdiyaVijay Vavdiya 1,3091 gold badge13 silver badges20 bronze badges 1- are you still looking for solutions or found one?? – Aravind Commented Mar 10, 2020 at 17:58
4 Answers
Reset to default 8 +100To make a component to output a value use @Output.
To consume data from outside a component, use @Input.
As I don't know the type of your item I'll use ItemType
in this example.
select-item-component
Inside select-item-component, declare an attribute:
@Output() onDataChange: EventEmitter<ItemType> = new EventEmitter();
and when the select changes, just do
this.onDataChange.emit(formValue);
add-qty-component
@Input() item: ItemType;
If you want to trigger some actions when the value changes, use set
. For example:
@Input() set item(value: ItemType) {
this.loadOptions(value);
}
You could do the same in select-item-component
with select1
and select2
variables.
parent
Use the output and the input values.
...
<select-item-component (onDataChange)="select1 = $event"></select-item-component>
<select-item-component (onDataChange)="select2 = $event"></select-item-component>
...
<add-qty-component [item]="select1"></add-qty-component>
<add-qty-component [item]="select2"></add-qty-component>
...
You can use a combination of @Output and @Input decorators.
@Output decorator can be used in the child component (stepper 1) to transmit the data to its parent component.
@input decorator can be used to send that data from the parent component to the child component (stepper 2).
You can read more about it here.
You can use ControlContainer
for the same purpose which allows you to access the form controls both in the child component as well as in the parent component
<button mat-raised-button (click)="isLinear = !isLinear" id="toggle-linear">
{{!isLinear ? 'Enable linear mode' : 'Disable linear mode'}}
</button>
<mat-horizontal-stepper [linear]="isLinear" #stepper>
<mat-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<step1></step1>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="secondFormGroup">
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>Fill out your address</ng-template>
<step2></step2>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
</mat-horizontal-stepper>
Your Step1
component should look like
import {Component, OnInit} from '@angular/core';
import {ControlContainer, FormGroup, Validators} from '@angular/forms';
@Component({
selector: 'step1',
template:`
<form [formGroup]="form">
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
</form>
`
})
export class Step1 implements OnInit {
isLinear = false;
form: FormGroup;
constructor(private controlContainer:ControlContainer) {}
ngOnInit() {
this.form=<FormGroup>this.controlContainer.control;
this.form.valueChanges.subscribe(data=>console.log(data));
}
}
Your Step2
component will look like
import {Component, OnInit} from '@angular/core';
import {ControlContainer, FormGroup, Validators} from '@angular/forms';
/**
* @title Stepper overview
*/
@Component({
selector: 'step2',
template:`
<form [formGroup]="form">
<mat-form-field>
<mat-label>Address</mat-label>
<input matInput formControlName="secondCtrl" placeholder="Ex. 1 Main St, New York, NY"
required>
</mat-form-field>
</form>
`
})
export class Step2 implements OnInit {
form: FormGroup;
constructor(private controlContainer:ControlContainer) {}
ngOnInit() {
this.form=<FormGroup>this.controlContainer.control;
this.form.valueChanges.subscribe(data=>console.log(data));
}
}
stackblitz
Another option is to use a shared service. I would use a Subject on the service, and call next in the component that changes the data, passing the new data on the next call.
public $itemsAdded: Subject<Item> = new Subject<Item>();
Then in the next component, subscribe to your Subject and set your local variable to that data.
this.sharedService.$itemsAdded.subscribe(items => {
this.items = items.map(x => mapItem());
});