Angular Stepper 구성 요소 개요
Ignite UI for Angular Stepper는 콘텐츠를 프로세스로 시각화하고 콘텐츠를 연속적인 단계로 나누어 진행 상황을 보여주는 고도로 사용자 정의 가능한 구성 요소입니다. 수직 또는 수평 선으로 나타납니다. Ignite UI for Angular Component 라이브러리에서 제공하는 Stepper 구성 요소는 마법사와 같은 워크플로와 단계 검증, 스타일링, 방향 및 키보드 탐색과 같은 여러 기능을 제공합니다.
Angular 스테퍼 예제
이 Angular Stepper 예제에서는 사용자에게 신용 카드를 사용자 지정할 수 있는 기회가 주어지고 카드 유형 선택, 사업 정보 추가, 개인 정보 입력, 배송 세부 정보 제공 및 확인의 5가지 논리적 단계로 프로세스를 거치는 방법을 볼 수 있습니다. Angular Stepper 데모의 네 번째 단계는 사용자가 두 번째 단계의 확인란을 선택하여 우편 주소가 사업의 실제 주소와 다르다는 것을 나타내는 경우에만 활성화됩니다.
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
import { NgForm, FormsModule } from '@angular/forms';
import { IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxCardComponent, IgxCardMediaDirective, IgxCardContentDirective, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxSelectComponent, IgxSelectItemComponent, IgxMaskDirective, IgxCheckboxComponent, IgxHintDirective, IgxRadioGroupDirective, IgxRadioComponent, IgxButtonDirective, IgxIconComponent, IgxBadgeComponent } from 'igniteui-angular';
import { NgFor, NgClass, NgIf, DatePipe } from '@angular/common';
@Component({
selector: 'app-stepper-overview-sample',
styleUrls: ['./stepper-overview-sample.component.scss'],
templateUrl: './stepper-overview-sample.component.html',
imports: [IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxBadgeComponent, NgFor, IgxCardComponent, NgClass, NgIf, IgxCardMediaDirective, IgxCardContentDirective, FormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxSelectComponent, IgxSelectItemComponent, IgxMaskDirective, IgxCheckboxComponent, IgxHintDirective, IgxRadioGroupDirective, IgxRadioComponent, IgxButtonDirective, IgxIconComponent, DatePipe]
})
export class StepperOverviewSampleComponent {
@ViewChild('stepper', { static: true })
public stepper: IgxStepperComponent;
public today: Date = new Date();
public cards: any[] = [
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-gold.png',
price: 400,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Customized Advanced',
description: 'Cash Mastercard'
},
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-red.png',
price: 600,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Travel Advanced',
description: 'World Mastercard'
},
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-blue.png',
price: 500,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Golden',
description: 'World Mastercard'
}
];
public states: string[] = [
'Alabama', 'Arizona', 'California', 'Colorado', 'Florida',
'Georgia', 'Hawaii', 'Illinois', 'Louisiana', 'Minnesota',
'Nevada', 'New York', 'New Jersey', 'Ohio', 'Texas', 'Virginia', 'Washington'
];
public selectedCard: any;
public businessInformation: any = {
name: '',
physicalAddress: '',
city: '',
state: '',
zip: null,
taxIdNumber: null,
differentAddress: false,
nonUSBusinessActivity: null
};
public personalInformation: any = {
firstName: '',
lastName: '',
jobTitle: '',
phoneNumber: '',
email: '',
authorization: false,
agreementAccepted: false
};
public shippingDetails: any = {
firstName: '',
lastName: '',
mailingAddress: '',
city: '',
state: '',
zip: null
};
constructor(private cdr: ChangeDetectorRef) { }
public selectCard(card: any): void {
this.selectedCard = card;
this.cdr.detectChanges();
this.stepper.next();
}
public resetStepper(form1: NgForm, form2: NgForm, form3: NgForm): void {
this.stepper.reset();
this.selectedCard = null;
form1.reset();
this.businessInformation.differentAddress = false;
form2.reset();
this.personalInformation.authorization = false;
this.personalInformation.agreementAccepted = false;
form3.reset();
}
public handleKeyDown(evt: KeyboardEvent, card: any): void {
if (evt.key.toLowerCase() === ' ' || evt.key.toLowerCase() === 'spacebar' || evt.key.toLowerCase() === 'space') {
this.selectCard(card);
}
}
}
ts<igx-stepper #stepper [linear]="true">
<igx-step #step1 [isValid]="!!selectedCard" [completed]="!!selectedCard">
<span igxStepTitle>Card Type</span>
<div igxStepContent [tabIndex]="-1">
<h2 class="sample-step-title">Choose your business credit card</h2>
<div class="card-wrapper">
<igx-card *ngFor="let card of cards" (click)="selectCard(card)"
[ngClass]="{'selected-card': selectedCard === card}" [tabIndex]="0"
(keydown)="handleKeyDown($event, card)" elevated>
<igx-badge *ngIf="selectedCard === card" value="Your current choice"></igx-badge>
<igx-card-media>
<img src="{{card.img}}" alt="">
</igx-card-media>
<igx-card-content>
<span class="card-currency">{{card.price}}</span>
<span class="card-title">{{card.offer}}</span>
<p>{{card.type}}</p>
<p>{{card.description}}</p>
</igx-card-content>
</igx-card>
</div>
</div>
</igx-step>
<igx-step #step2 [isValid]="businessInformationForm.form.valid" [completed]="businessInformationForm.form.valid">
<span igxStepTitle>Business information</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" #businessInformationForm="ngForm">
<igx-input-group type="box">
<input igxInput name="name" id="name" type="text" [(ngModel)]="businessInformation.name"
required />
<label igxLabel for="name">Legal Business Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="physicalAddress" id="physicalAddress" type="text"
[(ngModel)]="businessInformation.physicalAddress" required />
<label igxLabel for="physicalAddress">Business Physical Address</label>
</igx-input-group>
<div class="form-row">
<igx-input-group type="box">
<input igxInput name="city" id="city" type="text" [(ngModel)]="businessInformation.city"
required />
<label igxLabel for="city">City</label>
</igx-input-group>
<igx-select type="box" name="state" [(ngModel)]="businessInformation.state" required>
<igx-select-item *ngFor="let state of states" [value]="state">
{{state}}
</igx-select-item>
<label igxLabel>State</label>
</igx-select>
<igx-input-group type="box">
<input igxInput name="zip" id="zip" type="text" pattern="[0-9]{5}" [igxMask]="'00000'"
[placeholder]="'#####'" [(ngModel)]="businessInformation.zip" required />
<label igxLabel for="zip">ZIP Code</label>
</igx-input-group>
</div>
<igx-checkbox name="differentAddress" [(ngModel)]="businessInformation.differentAddress">
The mailing address is different than the business physical address
</igx-checkbox>
<igx-input-group type="box">
<input igxInput name="taxIdNumber" id="taxIdNumber" type="text" pattern="[9]{1}[0-9]{8}"
[placeholder]="'9##-##-####'" [igxMask]="'000-00-0000'"
[(ngModel)]="businessInformation.taxIdNumber" required />
<igx-hint *ngIf="!businessInformationForm.controls['taxIdNumber']?.pristine
&& !businessInformationForm.controls['taxIdNumber']?.valid">
The Federal Tax ID Number should begin with 9
</igx-hint>
<label igxLabel for="taxIdNumber">Federal Tax ID Number</label>
</igx-input-group>
<p>Does this business operate outside the United States? <span class="sample-required">*</span></p>
<igx-radio-group type="box" name="nonUSBusinessActivity" alignment="vertical"
[(ngModel)]="businessInformation.nonUSBusinessActivity" required>
<igx-radio class="radio-sample" value="true">
Yes
</igx-radio>
<igx-radio class="radio-sample" value="false">
No
</igx-radio>
</igx-radio-group>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!businessInformationForm.form.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step3 [isValid]="personalInformationForm.form.valid" [completed]="personalInformationForm.form.valid">
<span igxStepTitle>Personal Information</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" #personalInformationForm="ngForm">
<igx-input-group type="box">
<input igxInput name="firstName" id="firstName" type="text"
[(ngModel)]="personalInformation.firstName" required />
<label igxLabel for="firstName">First Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="lastName" id="lastName" type="text"
[(ngModel)]="personalInformation.lastName" required />
<label igxLabel for="lastName">Last Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="jobTitle" id="jobTitle" type="text"
[(ngModel)]="personalInformation.jobTitle" required />
<label igxLabel for="jobTitle">Job Title</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="phoneNumber" id="phoneNumber" type="text"
[(ngModel)]="personalInformation.phoneNumber" required />
<label igxLabel for="phoneNumber">Phone Number</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="email" id="email" type="email" [(ngModel)]="personalInformation.email"
required email />
<label igxLabel for="email">Email Address</label>
</igx-input-group>
<igx-checkbox name="authorization" [(ngModel)]="personalInformation.authorization" required>
I confirm that I am authorized to borrow on behalf of this business. <span
class="sample-required">*</span>
</igx-checkbox>
<div class="sample-terms">
<igx-checkbox name="agreementAccepted" [(ngModel)]="personalInformation.agreementAccepted"
required>
I agree with the<span class="sample-required">*</span>
</igx-checkbox>
<a (click)="$event.stopPropagation()">Terms and Conditions</a>
</div>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!personalInformationForm.form.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step4 [disabled]="!businessInformation.differentAddress" [isValid]="shippingDetailsForm.form.valid"
[completed]="shippingDetailsForm.form.valid || (!businessInformation.differentAddress && step3.completed)">
<span igxStepTitle>Shipping Details</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" #shippingDetailsForm="ngForm">
<igx-input-group type="box">
<input igxInput name="sfirstName" id="sfirstName" type="text"
[(ngModel)]="shippingDetails.firstName" required />
<label igxLabel for="sfirstName">First Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="slastName" id="slastName" type="text"
[(ngModel)]="shippingDetails.lastName" required />
<label igxLabel for="slastName">Last Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="mailingAddress" type="text" [(ngModel)]="shippingDetails.mailingAddress"
required />
<label igxLabel for="mailingAddress" id="mailingAddress">Mailing Address</label>
</igx-input-group>
<div class="form-row">
<igx-input-group type="box">
<input igxInput name="scity" id="scity" type="text" [(ngModel)]="shippingDetails.city"
required />
<label igxLabel for="scity">City</label>
</igx-input-group>
<igx-select type="box" name="sstate" [(ngModel)]="shippingDetails.state" required>
<igx-select-item *ngFor="let state of states" [value]="state">
{{state}}
</igx-select-item>
<label igxLabel>State</label>
</igx-select>
<igx-input-group type="box">
<input igxInput name="szip" id="szip" type="text" pattern="[0-9]{5}" [placeholder]="'#####'"
[igxMask]="'00000'" [(ngModel)]="shippingDetails.zip" required />
<label igxLabel for="szip">ZIP Code</label>
</igx-input-group>
</div>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!shippingDetailsForm.form.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step5>
<span igxStepTitle>Confirmation</span>
<div igxStepContent [tabIndex]="-1">
<div class="success-message">
<div class="success-marker">
<igx-icon>check</igx-icon>
</div>
<div class="success-text">
<h2 class="sample-step-title">Your application for a business credit card has been sent
successfully!</h2>
<p>We will contact you within a few days.</p>
</div>
</div>
<igx-card class="sample-small-card" *ngIf="selectedCard">
<igx-card-content>
<strong class="sample-date">Application date: {{ today | date:'d MMM y' }}</strong>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
<div class="sample-step-actions">
<button igxButton="contained"
(click)="resetStepper(businessInformationForm, personalInformationForm, shippingDetailsForm)">Reset</button>
</div>
</div>
</igx-step>
</igx-stepper>
html@use '../../variables' as *;
$sample-gap: 20px;
:host {
display: block;
padding: $sample-gap;
max-width: rem(1150px);
p {
margin: 0;
}
}
.sample-step-actions,
.card-wrapper,
.sample-row,
.flex-form,
.form-row,
.success-message,
.success-text {
display: flex;
}
.sample-row {
align-items: flex-start;
gap: rem(50px);
> * {
flex: 1;
}
}
.sample-step-actions {
gap: $sample-gap;
margin-top: $sample-gap * 2;
}
.card-wrapper {
gap: rem(30px);
}
$my-card: card-theme(
$schema: $light-schema,
$border-radius: rem(8px),
$content-text-color: color($default-palette, 'gray', 900),
);
@include card($my-card);
.selected-card {
border: 2px solid color($default-palette, 'primary', 400);
position: relative;
overflow: initial;
igx-badge {
position: absolute;
top: rem(-11px);
left: 0;
right: 0;
margin: 0 auto;
content: 'Current choice';
padding: rem(4px) rem(8px);
max-width: 50%;
}
}
.igx-card {
cursor: pointer;
user-select: none;
min-width: 280px;
&:focus {
outline: none;
box-shadow: inset 0 0 0 1px color($default-palette, 'primary', 400),
0 5px 5px -3px rgba(0, 0, 0, 0.26),
0 8px 10px 1px rgba(0, 0, 0, 0.12),
0 3px 14px 2px rgba(0, 0, 0, 0.08);
}
}
.sample-step-title {
font-size: rem(20px);
letter-spacing: 0;
font-weight: 500;
line-height: normal;
margin-bottom: rem(24px);
}
.igx-card__media {
max-width: rem(340px);
padding: rem(30px) rem(30px) 0 rem(30px);
}
.igx-card-content {
text-align: center;
p {
font-size: rem(16px);
line-height: rem(24px);
}
padding: rem(30px);
}
.card-title {
display: block;
font-size: rem(14px);
font-weight: 500;
margin: 0 rem(8px);
color: color($default-palette, 'secondary');
}
.card-currency {
display: block;
font-size: rem(48px);
color: color($default-palette, 'secondary');
margin-bottom: rem(8px);
&::before {
content: '$';
font-size: rem(34px);
margin-right: rem(6px);
position: relative;
top: rem(-12px);
}
}
.flex-form,
.form-row {
gap: rem(16px);
}
.flex-form {
flex-direction: column;
igx-checkbox {
white-space: nowrap;
}
}
.sample-card {
align-items: center;
justify-content: center;
flex-wrap: wrap;
img {
width: rem(180px);
max-width: rem(300px);
min-width: rem(130px);
}
.card-title {
font-size: 12px;
margin:0 rem(4px);
}
.card-currency {
font-size: rem(36px);
margin-bottom: rem(4px);
&::before {
font-size: rem(22px);
margin-right: rem(4px);
}
}
p {
font-size: rem(14px);
white-space: nowrap;
}
}
.radio-sample {
margin-bottom: rem(16px);
}
.success-marker {
display:flex;
align-items: center;
justify-content: center;
width: rem(30px);
height: rem(30px);
border-radius: rem(15px);
background: color($default-palette, 'success');
color: #fff;
user-select: none;
}
.success-message {
align-items: center;
gap: rem(16px);
margin-bottom: rem(32px);
}
.success-text {
flex-direction: column;
.sample-step-title {
margin: 0;
}
}
.sample-date {
display: block;
margin-bottom: rem(16px);
text-align: left;
font-weight: 500;
}
.sample-small-card {
max-width: rem(500px);
}
.sample-terms {
display: flex;
align-items: center;
justify-content: flex-start;
> a {
margin-left: rem(8px);
color: color($default-palette, 'primary');
text-decoration: none;
&:hover {
color: color($default-palette, 'primary', 600);
}
}
}
.sample-required {
color: color($default-palette, 'error');
margin-left: rem(2px);
}
scss
이 샘플이 마음에 드시나요? 전체 Ignite UI for Angular 툴킷에 액세스하고 몇 분 안에 나만의 앱을 구축해 보세요. 무료로 다운로드하세요.
다음은 Angular Reactive Forms를 사용하여 위의 기능을 구현하는 방법을 보여주는 샘플입니다.
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxCardComponent, IgxCardMediaDirective, IgxCardContentDirective, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxSelectComponent, IgxSelectItemComponent, IgxMaskDirective, IgxCheckboxComponent, IgxHintDirective, IgxRadioGroupDirective, IgxRadioComponent, IgxButtonDirective, IgxIconComponent, IgxBadgeComponent } from 'igniteui-angular';
import { NgFor, NgClass, NgIf, DatePipe } from '@angular/common';
export interface BusinessInformation{
name: FormControl<string | null>,
physicalAddress: FormControl<string | null>,
city: FormControl<string | null>,
state: FormControl<string[] | null>,
zip: FormControl<number | null>,
taxIdNumber: FormControl<number | null>,
differentAddress: FormControl<boolean | null>,
nonUSBusinessActivity: FormControl<boolean | null>
}
export interface PersonalInformation{
firstName: FormControl<string | null>,
lastName: FormControl<string | null>,
jobTitle: FormControl<string | null>,
phoneNumber: FormControl<number | null>,
email: FormControl<string | null>,
authorization: FormControl<boolean | null>,
agreementAccepted: FormControl<boolean | null>
}
export interface ShippingDetails{
sfirstName: FormControl<string | null>,
slastName: FormControl<string | null>,
smailingAddress: FormControl<string | null>,
scity: FormControl<string | null>,
sstate: FormControl<string[] | null>,
szip: FormControl<number | null>
}
@Component({
selector: 'app-stepper-sample-reactive-forms',
styleUrls: ['./stepper-sample-reactive-forms.component.scss'],
templateUrl: './stepper-sample-reactive-forms.component.html',
imports: [IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxBadgeComponent, NgFor, IgxCardComponent, NgClass, NgIf, IgxCardMediaDirective, IgxCardContentDirective, FormsModule, ReactiveFormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxSelectComponent, IgxSelectItemComponent, IgxMaskDirective, IgxCheckboxComponent, IgxHintDirective, IgxRadioGroupDirective, IgxRadioComponent, IgxButtonDirective, IgxIconComponent, DatePipe]
})
export class StepperSampleReactiveFormsComponent {
@ViewChild('stepper', { static: true })
public stepper: IgxStepperComponent;
public today: Date = new Date();
public cards: any[] = [
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-gold.png',
price: 400,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Customized Advanced',
description: 'Cash Mastercard'
},
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-red.png',
price: 600,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Travel Advanced',
description: 'World Mastercard'
},
{
img: 'https://www.infragistics.com/angular-demos-lob/assets/images/stepper/card-blue.png',
price: 500,
offer: 'STATEMENT CREDIT OFFER',
type: 'Business Golden',
description: 'World Mastercard'
}
];
public states: string[] = [
'Alabama', 'Arizona', 'California', 'Colorado', 'Florida',
'Georgia', 'Hawaii', 'Illinois', 'Louisiana', 'Minnesota',
'Nevada', 'New York', 'New Jersey', 'Ohio', 'Texas', 'Virginia', 'Washington'
];
public selectedCard: any;
public businessInformation:FormGroup<BusinessInformation>;
public personalInformation:FormGroup<PersonalInformation>;
public shippingDetails:FormGroup<ShippingDetails>;
constructor(private cdr: ChangeDetectorRef) {
this.businessInformation = new FormGroup<BusinessInformation>({
name: new FormControl('', Validators.required),
physicalAddress: new FormControl('', Validators.required),
city: new FormControl('', Validators.required),
state: new FormControl(this.states, Validators.required),
zip: new FormControl(null, [Validators.required, Validators.pattern("[0-9]{5}")]),
taxIdNumber: new FormControl(null, [Validators.required,Validators.pattern("[9]{1}[0-9]{8}")]),
differentAddress: new FormControl(false, Validators.required),
nonUSBusinessActivity: new FormControl(null, Validators.required)
})
this.personalInformation = new FormGroup<PersonalInformation>({
firstName: new FormControl('', Validators.required),
lastName: new FormControl('', Validators.required),
jobTitle: new FormControl('', Validators.required),
phoneNumber: new FormControl(null, Validators.required),
email: new FormControl('', Validators.required),
authorization: new FormControl(false, Validators.requiredTrue),
agreementAccepted: new FormControl(false, Validators.requiredTrue)
})
this.shippingDetails = new FormGroup<ShippingDetails>({
sfirstName: new FormControl('', Validators.required),
slastName: new FormControl('', Validators.required),
smailingAddress: new FormControl('', Validators.required),
scity: new FormControl('', Validators.required),
sstate: new FormControl(this.states, Validators.required),
szip: new FormControl(null, [Validators.required, Validators.pattern("[0-9]{5}")])
})
};
public selectCard(card: any): void {
this.selectedCard = card;
this.cdr.detectChanges();
this.stepper.next();
}
public resetStepper(form1: FormGroup, form2: FormGroup, form3: FormGroup): void {
this.stepper.reset();
this.selectedCard = null;
form1.reset();
this.businessInformation.controls.differentAddress.setValue(false);
form2.reset();
this.personalInformation.controls.authorization.setValue(false);
this.personalInformation.controls.agreementAccepted.setValue(false);
form3.reset();
}
public handleKeyDown(evt: KeyboardEvent, card: any): void {
if (evt.key.toLowerCase() === ' ' || evt.key.toLowerCase() === 'spacebar' || evt.key.toLowerCase() === 'space') {
this.selectCard(card);
}
}
public errorMessages(){
if(this.businessInformation.controls.taxIdNumber.errors.required){
return "This field is required"
}
if(this.businessInformation.controls.taxIdNumber.errors.pattern){
return "The Federal Tax ID Number should begin with 9 and should have 9 digits"
}
}
}
ts<igx-stepper #stepper [linear]="true">
<igx-step #step1 [isValid]="!!selectedCard" [completed]="!!selectedCard">
<span igxStepTitle>Card Type</span>
<div igxStepContent [tabIndex]="-1">
<h2 class="sample-step-title">Choose your business credit card</h2>
<div class="card-wrapper">
<igx-card *ngFor="let card of cards" (click)="selectCard(card)"
[ngClass]="{'selected-card': selectedCard === card}" [tabIndex]="0"
(keydown)="handleKeyDown($event, card)" elevated>
<igx-badge *ngIf="selectedCard === card" value="Your current choice"></igx-badge>
<igx-card-media>
<img src="{{card.img}}" alt="">
</igx-card-media>
<igx-card-content>
<span class="card-currency">{{card.price}}</span>
<span class="card-title">{{card.offer}}</span>
<p>{{card.type}}</p>
<p>{{card.description}}</p>
</igx-card-content>
</igx-card>
</div>
</div>
</igx-step>
<igx-step #step2 [isValid]="businessInformation.valid" [completed]="businessInformation.valid">
<span igxStepTitle>Business information</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" [formGroup]="businessInformation">
<igx-input-group type="box">
<input igxInput name="name" id="name" type="text" formControlName="name" required/>
<label igxLabel for="name">Legal Business Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="physicalAddress" id="physicalAddress" type="text"
formControlName="physicalAddress" required />
<label igxLabel for="physicalAddress">Business Physical Address</label>
</igx-input-group>
<div class="form-row">
<igx-input-group type="box">
<input igxInput name="city" id="city" type="text" formControlName="city"
required />
<label igxLabel for="city">City</label>
</igx-input-group>
<igx-select type="box" name="state" formControlName="state" required>
<igx-select-item *ngFor="let state of states" [value]="state">
{{state}}
</igx-select-item>
<label igxLabel>State</label>
</igx-select>
<igx-input-group type="box">
<input igxInput name="zip" id="zip" type="text" [igxMask]="'00000'"
[placeholder]="'#####'" formControlName="zip" required />
<label igxLabel for="zip">ZIP Code</label>
</igx-input-group>
</div>
<igx-checkbox name="differentAddress" formControlName="differentAddress">
The mailing address is different than the business physical address
</igx-checkbox>
<igx-input-group type="box">
<input igxInput name="taxIdNumber" id="taxIdNumber" type="text"
[placeholder]="'9##-##-####'" [igxMask]="'000-00-0000'"
formControlName="taxIdNumber" required/>
<igx-hint *ngIf="businessInformation.controls.taxIdNumber.invalid && businessInformation.controls.taxIdNumber.touched">
{{errorMessages()}}
</igx-hint>
<label igxLabel for="taxIdNumber">Federal Tax ID Number</label>
</igx-input-group>
<p>Does this business operate outside the United States? <span class="sample-required">*</span></p>
<igx-radio-group type="box" name="nonUSBusinessActivity" formControlName="nonUSBusinessActivity" alignment="vertical"
required>
<igx-radio class="radio-sample" value="true">
Yes
</igx-radio>
<igx-radio class="radio-sample" value="false">
No
</igx-radio>
</igx-radio-group>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!businessInformation.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step3 [isValid]="personalInformation.valid" [completed]="personalInformation.valid">
<span igxStepTitle>Personal Information</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" [formGroup]="personalInformation">
<igx-input-group type="box">
<input igxInput name="firstName" id="firstName" type="text"
formControlName="firstName" required />
<label igxLabel for="firstName">First Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="lastName" id="lastName" type="text"
formControlName="lastName" required />
<label igxLabel for="lastName">Last Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="jobTitle" id="jobTitle" type="text"
formControlName="jobTitle" required />
<label igxLabel for="jobTitle">Job Title</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="phoneNumber" id="phoneNumber" type="text"
formControlName="phoneNumber" required />
<label igxLabel for="phoneNumber">Phone Number</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="email" id="email" type="email" formControlName="email"
required email />
<label igxLabel for="email">Email Address</label>
</igx-input-group>
<igx-checkbox name="authorization" formControlName="authorization" required>
I confirm that I am authorized to borrow on behalf of this business. <span
class="sample-required">*</span>
</igx-checkbox>
<div class="sample-terms">
<igx-checkbox name="agreementAccepted" formControlName="agreementAccepted"
required>
I agree with the<span class="sample-required">*</span>
</igx-checkbox>
<a (click)="$event.stopPropagation()">Terms and Conditions</a>
</div>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!personalInformation.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step4 [isValid]="shippingDetails.valid" [completed]="shippingDetails.valid || (!businessInformation.controls.differentAddress && step3.completed)">
<span igxStepTitle>Shipping Details</span>
<div igxStepContent [tabIndex]="-1">
<div class="sample-row">
<form class="flex-form" [formGroup]="shippingDetails">
<igx-input-group type="box">
<input igxInput name="sfirstName" id="sfirstName" type="text"
formControlName="sfirstName" required />
<label igxLabel for="sfirstName">First Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="slastName" id="slastName" type="text"
formControlName="slastName" required />
<label igxLabel for="slastName">Last Name</label>
</igx-input-group>
<igx-input-group type="box">
<input igxInput name="mailingAddress" type="text" formControlName="smailingAddress"
required />
<label igxLabel for="mailingAddress" id="mailingAddress">Mailing Address</label>
</igx-input-group>
<div class="form-row">
<igx-input-group type="box">
<input igxInput name="scity" id="scity" type="text" formControlName="scity"
required />
<label igxLabel for="scity">City</label>
</igx-input-group>
<igx-select type="box" name="sstate" formControlName="sstate" required>
<igx-select-item *ngFor="let state of states" [value]="state">
{{state}}
</igx-select-item>
<label igxLabel>State</label>
</igx-select>
<igx-input-group type="box">
<input igxInput name="szip" id="szip" type="text" pattern="[0-9]{5}" [placeholder]="'#####'"
[igxMask]="'00000'" formControlName="szip" required />
<label igxLabel for="szip">ZIP Code</label>
</igx-input-group>
</div>
</form>
<igx-card *ngIf="selectedCard">
<igx-card-content>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
</div>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Back</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!shippingDetails.valid">Continue</button>
</div>
</div>
</igx-step>
<igx-step #step5>
<span igxStepTitle>Confirmation</span>
<div igxStepContent [tabIndex]="-1">
<div class="success-message">
<div class="success-marker">
<igx-icon>check</igx-icon>
</div>
<div class="success-text">
<h2 class="sample-step-title">Your application for a business credit card has been sent
successfully!</h2>
<p>We will contact you within a few days.</p>
</div>
</div>
<igx-card class="sample-small-card" *ngIf="selectedCard">
<igx-card-content>
<strong class="sample-date">Application date: {{ today | date:'d MMM y' }}</strong>
<div class="sample-row sample-card">
<img src="{{selectedCard.img}}" alt="">
<div>
<span class="card-currency">{{selectedCard.price}}</span>
<span class="card-title">{{selectedCard.offer}}</span>
<p>{{selectedCard.type}}</p>
<p>{{selectedCard.description}}</p>
</div>
</div>
</igx-card-content>
</igx-card>
<div class="sample-step-actions">
<button igxButton="contained" (click)="resetStepper(businessInformation, personalInformation, shippingDetails)">Reset</button>
</div>
</div>
</igx-step>
</igx-stepper>
html@use '../../variables' as *;
$sample-gap: 20px;
:host {
display: block;
padding: $sample-gap;
max-width: rem(1150px);
p {
margin: 0;
}
}
.sample-step-actions,
.card-wrapper,
.sample-row,
.flex-form,
.form-row,
.success-message,
.success-text {
display: flex;
}
.sample-row {
align-items: flex-start;
gap: rem(50px);
> * {
flex: 1;
}
}
.sample-step-actions {
gap: $sample-gap;
margin-top: $sample-gap * 2;
}
.card-wrapper {
gap: rem(30px);
}
$my-card: card-theme(
$schema: $light-schema,
$border-radius: rem(8px),
$content-text-color: color($default-palette, 'gray', 900),
);
@include card($my-card);
.selected-card {
border: 2px solid color($default-palette, 'primary', 400);
position: relative;
overflow: initial;
igx-badge {
position: absolute;
top: rem(-11px);
left: 0;
right: 0;
margin: 0 auto;
content: 'Current choice';
padding: rem(4px) rem(8px);
max-width: 50%;
}
}
.igx-card {
cursor: pointer;
user-select: none;
min-width: 280px;
&:focus {
outline: none;
box-shadow: inset 0 0 0 1px color($default-palette, 'primary', 400),
0 5px 5px -3px rgba(0, 0, 0, 0.26),
0 8px 10px 1px rgba(0, 0, 0, 0.12),
0 3px 14px 2px rgba(0, 0, 0, 0.08);
}
}
.sample-step-title {
font-size: rem(20px);
letter-spacing: 0;
font-weight: 500;
line-height: normal;
margin-bottom: rem(24px);
}
.igx-card__media {
max-width: rem(340px);
padding: rem(30px) rem(30px) 0 rem(30px);
}
.igx-card-content {
text-align: center;
p {
font-size: rem(16px);
line-height: rem(24px);
}
padding: rem(30px);
}
.card-title {
display: block;
font-size: rem(14px);
font-weight: 500;
margin: 0 rem(8px);
color: color($default-palette, 'secondary');
}
.card-currency {
display: block;
font-size: rem(48px);
color: color($default-palette, 'secondary');
margin-bottom: rem(8px);
&::before {
content: '$';
font-size: rem(34px);
margin-right: rem(6px);
position: relative;
top: rem(-12px);
}
}
.flex-form,
.form-row {
gap: rem(16px);
}
.flex-form {
flex-direction: column;
igx-checkbox {
white-space: nowrap;
}
}
.sample-card {
align-items: center;
justify-content: center;
flex-wrap: wrap;
img {
width: rem(180px);
max-width: rem(300px);
min-width: rem(130px);
}
.card-title {
font-size: 12px;
margin:0 rem(4px);
}
.card-currency {
font-size: rem(36px);
margin-bottom: rem(4px);
&::before {
font-size: rem(22px);
margin-right: rem(4px);
}
}
p {
font-size: rem(14px);
white-space: nowrap;
}
}
.radio-sample {
margin-bottom: rem(16px);
}
.success-marker {
display:flex;
align-items: center;
justify-content: center;
width: rem(30px);
height: rem(30px);
border-radius: rem(15px);
background: color($default-palette, 'success');
color: #fff;
user-select: none;
}
.success-message {
align-items: center;
gap: rem(16px);
margin-bottom: rem(32px);
}
.success-text {
flex-direction: column;
.sample-step-title {
margin: 0;
}
}
.sample-date {
display: block;
margin-bottom: rem(16px);
text-align: left;
font-weight: 500;
}
.sample-small-card {
max-width: rem(500px);
}
.sample-terms {
display: flex;
align-items: center;
justify-content: flex-start;
> a {
margin-left: rem(8px);
color: color($default-palette, 'primary');
text-decoration: none;
&:hover {
color: color($default-palette, 'primary', 600);
}
}
}
.sample-required {
color: color($default-palette, 'error');
margin-left: rem(2px);
}
scss
Ignite UI for Angular 시작하기
Ignite UI for Angular Stepper 구성 요소를 시작하려면 먼저 Ignite UI for Angular 설치해야 합니다. 기존 Angular 애플리케이션에서 다음 명령을 입력합니다.
ng add igniteui-angular
cmd
Ignite UI for Angular에 대한 전체 소개는 시작 항목을 참조하십시오.
다음 단계는 IgxStepperModule
당신의 app.module.ts 파일.
// app.module.ts
...
import { IgxStepperModule } from 'igniteui-angular';
// import { IgxStepperModule } from '@infragistics/igniteui-angular'; for licensed package
@NgModule({
...
imports: [..., IgxStepperModule],
...
})
export class AppModule {}
typescript
또는 16.0.0
부터 IgxStepperComponent
독립 실행형 종속성으로 가져오거나 IGX_STEPPER_DIRECTIVES
토큰을 사용하여 구성 요소와 모든 지원 구성 요소 및 지시어를 가져올 수 있습니다.
// home.component.ts
import { HammerModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { IGX_STEPPER_DIRECTIVES } from 'igniteui-angular';
// import { IGX_STEPPER_DIRECTIVES } from '@infragistics/igniteui-angular'; for licensed package
@Component({
selector: 'app-home',
template: `<igx-stepper>
<igx-step>
<p igxStepTitle>Step 1</p>
</igx-step>
<igx-step>
<p igxStepTitle>Step 2</p>
</igx-step>
</igx-stepper>`,
styleUrls: ['home.component.scss'],
standalone: true,
imports: [IGX_STEPPER_DIRECTIVES, FormsModule, HammerModule]
/* or imports: [IgxStepperComponent, FormsModule, HammerModule] */
})
export class HomeComponent {
public task: Task;
}
typescript
이제 Angular Stepper 모듈 또는 지시어를 가져왔으므로 igx-stepper
와 그 단계의 기본 구성으로 시작할 수 있습니다. 이제 Angular Stepper 모듈 또는 지시어를 가져왔으므로 igx-stepper
와 그 단계의 기본 구성으로 시작할 수 있습니다.
Angular 스테퍼 사용
IgxStepComponent는 IgxStepperComponent에 속하는 모든 단계를 나타냅니다. 단계는 비즈니스 요구 사항에 따라 단계 상태를 구성할 수 있는 기능을 제공하는 isValid, active, option, 비활성화 및 완료 속성을 제공합니다.
스테퍼 선언
이제 스테퍼 모듈을 가져왔으므로 해당 구성을 시작하겠습니다.
다음 접근 방식 중 하나를 사용하여 단계를 선언할 수 있습니다.
- 데이터 세트 반복
<igx-stepper>
<igx-step *ngFor="let step of stepsData" [disabled]=”step.disabled”>
<igx-icon igxStepIndicator>
{{step.indicator}}
</igx-icon>
<p igxStepTitle>
{{step.title}}
</p>
</igx-step>
</igx-stepper>
html
- 정적 단계 생성
<igx-stepper>
<igx-step>
<p igxStepTitle>Step 1</p>
</igx-step>
<igx-step>
<p igxStepTitle>Step 2</p>
</igx-step>
</igx-stepper>
html
각 단계마다 사용자는 다음과 같이 igxStepIndicator
, igxStepTitle
, igxStepSubtitle
및 igxStepContent
지시어를 사용하여 표시기, 제목, 부제목 및 콘텐츠를 구성할 수 있습니다.
<igx-stepper>
<igx-step>
<igx-icon igxStepIndicator>home</igx-icon>
<p igxStepTitle>Home</p>
<p igxStepSubtitle>Home Sub Title</p>
<div igxStepContent>
...
</div>
</igx-step>
</igx-stepper>
html

스테퍼 방향 변경
노출된 방향 속성을 통해 스테퍼 방향을 사용자 정의할 수 있습니다. IgxStepperOrientation
열거형(Horizontal
(기본값) 또는 Vertical
의 멤버를 사용합니다.
수평 스테퍼 방향
horizontal
는 기본값입니다. igx-stepper
정위 재산. 스테퍼가 수평 방향인 경우 단계 내용이 단계 헤더 위 또는 아래에 표시되는지 결정할 수 있습니다. 이는 다음을 설정하여 달성할 수 있습니다. IgxStepper구성요소 콘텐츠상단 부울 속성, 기본값은 다음과 같습니다. false
. 활성화된 경우 단계의 내용은 단계의 헤더 위에 표시됩니다.

수직 스테퍼 방향
가로 레이아웃에서 세로 레이아웃으로 쉽게 전환할 수 있습니다. 기본 방향을 변경하려면 방향 속성을 vertical
으로 설정해야 합니다.
<igx-stepper [orientation]="'vertical'">
<igx-step>
…
</igx-step>
<igx-step>
…
</igx-step>
</igx-stepper>
html
아래 샘플은 스테퍼 방향과 제목 위치가 런타임에 어떻게 변경될 수 있는지 보여줍니다.
import { Component } from '@angular/core';
import { IButtonGroupEventArgs, IgxStepperOrientation, IgxStepperTitlePosition, IgxButtonGroupComponent, IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxButtonDirective } from 'igniteui-angular';
@Component({
selector: 'app-stepper-label-position-and-orientation-sample',
styleUrls: ['./stepper-label-position-and-orientation-sample.component.scss'],
templateUrl: './stepper-label-position-and-orientation-sample.component.html',
imports: [IgxButtonGroupComponent, IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxButtonDirective]
})
export class StepperLabelPositionAndOrientationSampleComponent {
public orientation: IgxStepperOrientation = 'horizontal';
public titlePosition: IgxStepperTitlePosition = 'bottom';
public stepperOrientations: any[] = [
{
label: 'Horizontal', orientation: 'horizontal',
selected: this.orientation === 'horizontal', togglable: true
},
{
label: 'Vertical', orientation: 'vertical',
selected: this.orientation === 'vertical', togglable: true
}
];
public stepperTitlePositions: any[] = [
{
label: 'Bottom', titlePosition: 'bottom',
selected: this.titlePosition === 'bottom', togglable: true
},
{
label: 'Top', titlePosition: 'top',
selected: this.titlePosition === 'top', togglable: true
},
{
label: 'End', titlePosition: 'end',
selected: this.titlePosition === 'end', togglable: true
},
{
label: 'Start', titlePosition: 'start',
selected: this.titlePosition === 'start', togglable: true
}
];
public toggleOrientation(event: IButtonGroupEventArgs): void {
this.orientation = this.stepperOrientations[event.index].orientation;
}
public toggleTitlePosition(event: IButtonGroupEventArgs): void {
this.titlePosition = this.stepperTitlePositions[event.index].titlePosition;
}
}
ts<div class="sample-split">
<igx-buttongroup [values]="stepperTitlePositions" (selected)="toggleTitlePosition($event)">
</igx-buttongroup>
<igx-buttongroup [values]="stepperOrientations" (selected)="toggleOrientation($event)">
</igx-buttongroup>
</div>
<igx-stepper #stepper [titlePosition]="titlePosition" [orientation]="orientation">
<igx-step>
<span igxStepTitle>Order</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.next()">NEXT</button>
</div>
</div>
</igx-step>
<igx-step>
<span igxStepTitle>Payment</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">PREVIOUS</button>
<button igxButton="contained" (click)="stepper.next()">NEXT</button>
</div>
</div>
</igx-step>
<igx-step>
<span igxStepTitle>Confirmation</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">PREVIOUS</button>
<button igxButton="contained" (click)="stepper.reset()">RESET</button>
</div>
</div>
</igx-step>
</igx-stepper>
html$sample-gap: 20px;
:host {
display: block;
padding: $sample-gap;
}
.sample-split,
.sample-step-actions {
display: flex;
gap: $sample-gap;
}
.sample-split {
> * {
max-width: 380px;
flex: 1;
}
margin-bottom: $sample-gap;
}
scss
단계 상태
IgxStepperComponent는 4가지 단계 상태를 지원하며 각 단계는 기본적으로 서로 다른 스타일을 적용합니다.
- active- 단계가 현재 표시되는지 여부를 결정합니다. 설계상, 사용자가 일부 단계의 활성 속성을 명시적으로
true
로 설정하지 않은 경우 초기 활성 단계는 비활성화되지 않은 첫 번째 단계가 됩니다. - 비활성화됨- 단계가 상호 작용 가능한지 여부를 결정합니다. 기본적으로 단계의 비활성화된 속성은
false
로 설정됩니다. - 선택사항- 기본적으로 단계의 선택사항 속성은
false
로 설정됩니다. 선형 스테퍼에서 단계의 유효성이 필요하지 않은 경우 단계 유효성과 독립적으로 앞으로 나아갈 수 있도록 선택적 속성을 활성화할 수 있습니다. - 완료- 기본적으로 단계의 완료 속성은
false
반환합니다. 그러나 사용자는 필요에 따라 완료 속성을 설정하여 이 기본 완료 동작을 재정의할 수 있습니다. 단계가 완료로 표시되면 기본적으로 단계 헤더의 스타일이 변경될 뿐만 아니라 완료된 단계와 다음 단계 사이의 진행 라인 스타일도 변경됩니다. 노출된 CSS 변수를 사용하여 두 스타일 모두 수정할 수 있습니다.
IgxStepperComponent는 양방향 바인딩 가능한 isValid 속성을 통해 각 단계에 대한 유효성 검사 논리를 설정할 수 있는 기회를 제공합니다. 해당 값에 따라 사용자가 선형 스테퍼 모드에서 앞으로 이동할 수 있는지 여부가 결정됩니다. 기본적으로 단계의 isValid 속성은 true
로 설정됩니다.
선형 스테퍼
igx-stepper
선형 속성을 사용하여 단계 흐름을 설정할 수 있는 기회를 제공합니다. 기본적으로 선형은 false
로 설정되고 사용자는 IgxStepperComponent에서 비활성화되지 않은 단계를 선택할 수 있습니다.
선형 속성이 true
로 설정된 경우 스테퍼는 다음 단계로 진행하기 전에 현재 선택 사항이 아닌 단계가 유효해야 합니다.
현재 비선택적 단계가 유효하지 않은 경우 현재 단계를 확인할 때까지 다음 단계로 진행할 수 없습니다.
앞으로 나아갈 때 선택적 단계 유효성은 고려되지 않습니다.
다음 예에서는 선형 스테퍼를 구성하는 방법을 보여줍니다.
import { Component } from '@angular/core';
import { IButtonGroupEventArgs, IgxButtonGroupComponent, IgxStepperComponent, IgxStepActiveIndicatorDirective, IgxIconComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxButtonDirective, IgxStepSubtitleDirective, IgxRadioGroupDirective, IgxRadioComponent } from 'igniteui-angular';
import { FormsModule } from '@angular/forms';
import { NgFor } from '@angular/common';
@Component({
selector: 'app-stepper-linear-sample',
styleUrls: ['./stepper-linear-sample.component.scss'],
templateUrl: './stepper-linear-sample.component.html',
imports: [IgxButtonGroupComponent, IgxStepperComponent, IgxStepActiveIndicatorDirective, IgxIconComponent, IgxStepComponent, IgxStepTitleDirective, IgxStepContentDirective, FormsModule, IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxButtonDirective, IgxStepSubtitleDirective, IgxRadioGroupDirective, NgFor, IgxRadioComponent]
})
export class StepperLinearSampleComponent {
public linear = false;
public user: any = {
fullName: '',
email: '',
city: '',
street: '',
city2: '',
street2: '',
payment: ''
};
public paymentTypes: string[] = [
'PayPal (n@mail.com; 18/02/2021)',
'Visa (**** **** **** 1234; 12/23)',
'MasterCard (**** **** **** 5678; 12/24)'
];
public modes: any[] = [
{
label: 'Linear', linear: true,
selected: this.linear === true, togglable: true
},
{
label: 'Non Linear', linear: false,
selected: this.linear === false, togglable: true
}
];
public toggleModes(event: IButtonGroupEventArgs): void {
this.linear = this.modes[event.index].linear;
}
}
ts<div class="sample-split">
<igx-buttongroup [values]="modes" (selected)="toggleModes($event)">
</igx-buttongroup>
</div>
<igx-stepper #stepper [linear]="linear">
<ng-template igxStepActiveIndicator>
<igx-icon>edit</igx-icon>
</ng-template>
<igx-step #step1 [isValid]="form1.form.valid">
<span igxStepTitle>Personal Info</span>
<div igxStepContent>
<form #form1="ngForm">
<igx-input-group>
<input igxInput name="fullName" type="text" [(ngModel)]="user.fullName" required />
<label igxLabel for="fullName" id="fullName">Full Name</label>
</igx-input-group>
<igx-input-group>
<input igxInput name="email" type="email" [(ngModel)]="user.email" required email />
<label igxLabel for="email" id="email">Email</label>
</igx-input-group>
</form>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!form1.form.valid && stepper.linear">NEXT</button>
</div>
</div>
</igx-step>
<igx-step #step2 [isValid]="form2.form.valid">
<span igxStepTitle>Delivery address</span>
<div igxStepContent>
<form #form2="ngForm">
<igx-input-group>
<input igxInput name="city" type="text" [(ngModel)]="user.city" required />
<label igxLabel for="city" id="city">City</label>
</igx-input-group>
<igx-input-group>
<input igxInput name="street" type="text" [(ngModel)]="user.street" required />
<label igxLabel for="street" id="street">Street</label>
</igx-input-group>
</form>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Previous</button>
<button igxButton="contained" (click)="stepper.next()"
[disabled]="!form2.form.valid && stepper.linear">Next</button>
</div>
</div>
</igx-step>
<igx-step #step3 [optional]="true">
<span igxStepTitle>Billing address</span>
<span igxStepSubtitle>(optional)</span>
<div igxStepContent>
<form #form3="ngForm">
<igx-input-group>
<input igxInput name="bil-city" type="text" [(ngModel)]="user.city2" />
<label igxLabel for="bil-city" id="bil-city">City</label>
</igx-input-group>
<igx-input-group>
<input igxInput name="bil-street" type="text" [(ngModel)]="user.street2" />
<label igxLabel for="bil-street" id="bil-street">Street</label>
</igx-input-group>
</form>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Previous</button>
<button igxButton="contained" (click)="stepper.next()">Next</button>
</div>
</div>
</igx-step>
<igx-step #step4 [isValid]="user.payment">
<span igxStepTitle>Payment</span>
<div igxStepContent>
<igx-radio-group [(ngModel)]="user.payment" alignment="vertical" required>
<igx-radio *ngFor="let paymentType of paymentTypes" value="paymentType">
{{ paymentType }}
</igx-radio>
</igx-radio-group>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Previous</button>
<button igxButton="contained" (click)="stepper.next()">Submit</button>
</div>
</div>
</igx-step>
<igx-step #step5>
<span igxStepTitle>Delivery status</span>
<div igxStepContent>
<p>Your order is on its way. Expect delivery on 25th September 2021. Delivery address: San Jose, CA 94243.
</p>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">Previous</button>
<button igxButton="contained" (click)="stepper.reset()">Reset</button>
</div>
</div>
</igx-step>
</igx-stepper>
html$sample-gap: 20px;
:host {
display: block;
padding: $sample-gap;
}
.sample-split,
.sample-step-actions {
display: flex;
gap: $sample-gap;
}
.sample-step-actions {
margin-top: $sample-gap * 2;
}
.sample-split {
> * {
max-width: 380px;
flex: 1;
}
margin-bottom: $sample-gap;
}
form {
> * {
margin-bottom: calc(#{$sample-gap} / 2);
}
margin-bottom: $sample-gap;
}
igx-radio {
margin-bottom: $sample-gap;
}
scss
단계 상호작용
IgxStepperComponent는 단계 상호 작용을 위해 다음과 같은 API 메서드를 제공합니다.
- NavigateTo– 주어진 인덱스로 단계를 활성화합니다.
- next- 비활성화되지 않은 다음 단계를 활성화합니다.
- prev– 비활성화되지 않은 이전 단계를 활성화합니다.
- 재설정– 스테퍼를 초기 상태로 재설정합니다.
재설정 방법은 단계의 내용을 지우지 않습니다. 이 작업은 수동으로 수행해야 합니다.
단계 사용자 정의
Ignite UI for Angular 하면 제목, 지표 등에 대한 다양한 옵션을 구성할 수 있습니다.
이는 IgxStepperComponent의 stepType 속성을 통해 달성할 수 있습니다. IgxStepType
열거형의 멤버를 사용합니다.
- 전체 (기본값)
- 지시자
- 제목
가득한
제목과 부제목이 정의된 경우 이 설정을 사용하면 표시기와 제목이 모두 렌더링됩니다.
또한 사용자는 단계 제목의 위치를 정의할 수 있으므로 단계 표시기 앞, 뒤, 위 또는 아래에 제목을 배치할 수 있습니다. 사용자는 titlePosition 속성을 사용하여 제목 위치를 구성할 수 있습니다. 두 속성 모두 IgxStepperTitlePosition
열거형의 멤버를 사용합니다.
- 끝
- 시작
- 맨 아래
- 맨 위
igx-stepper
가로 방향인 경우 제목 위치 기본값은 bottom
입니다.
방향이 세로 레이아웃으로 설정된 경우 기본적으로 제목 위치는 end
입니다.
지시자
단계에 대한 표시기만 표시하려면 stepType 옵션을 indicator
로 설정하십시오.
단계 표시기는 모든 콘텐츠를 지원하지만 크기는 항상 24픽셀로 제한됩니다. 이를 염두에 두고 IgxIconComponent 또는 IgxAvatarComponent를 단계 표시기로 사용하는 것이 좋습니다.
제목
단계의 제목만 표시하려면 stepType 옵션을 title
로 설정하세요.
이런 방식으로 자막이 정의되면 단계 제목 아래에도 렌더링됩니다.
이 컨테이너는 크기 제한 없이 요구 사항에 따라 다시 템플릿을 만들 수 있습니다. 예를 들어 내부에 24픽셀보다 큰 크기의 표시기를 추가할 수 있습니다.
아래 샘플은 노출된 모든 단계 유형과 변경 방법을 보여줍니다.
import { Component } from '@angular/core';
import { IButtonGroupEventArgs, IgxStepType, IgxButtonGroupComponent, IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective } from 'igniteui-angular';
@Component({
selector: 'app-stepper-steptypes-sample',
styleUrls: ['./stepper-steptypes-sample.component.scss'],
templateUrl: './stepper-steptypes-sample.component.html',
imports: [IgxButtonGroupComponent, IgxStepperComponent, IgxStepComponent, IgxStepTitleDirective]
})
export class StepperStepTypesSampleComponent {
public stepType: IgxStepType = 'full';
public stepTypes: any[] = [
{
label: 'Indicator', stepType: 'indicator',
selected: this.stepType === 'indicator', togglable: true
},
{
label: 'Title', stepType: 'title',
selected: this.stepType === 'title', togglable: true
},
{
label: 'Full', stepType: 'full',
selected: this.stepType === 'full', togglable: true
}
];
public toggleStepType(event: IButtonGroupEventArgs): void {
this.stepType = this.stepTypes[event.index].stepType;
}
}
ts<div class="sample-split">
<igx-buttongroup [values]="stepTypes" (selected)="toggleStepType($event)"></igx-buttongroup>
</div>
<igx-stepper #stepper [stepType]="stepType">
<igx-step>
<span igxStepTitle>Pricing Plan</span>
</igx-step>
<igx-step>
<span igxStepTitle>Car Details</span>
</igx-step>
<igx-step>
<span igxStepTitle>Payment</span>
</igx-step>
</igx-stepper>
html$sample-gap: 20px;
:host {
display: block;
padding: $sample-gap;
}
.sample-split,
.sample-step-actions {
display: flex;
gap: $sample-gap;
}
.sample-step-actions {
margin-top: $sample-gap * 2;
}
.sample-split {
> * {
max-width: 380px;
flex: 1;
}
margin-bottom: $sample-gap;
}
scss
IgxStepperComponent를 사용하면 활성 단계, 유효하지 않은 단계, 완료된 단계에 대해 렌더링된 표시기를 사용자 정의할 수도 있습니다. 이는 igxStepActiveIndicator
, igxStepInvalidIndicator
및 igxStepCompletedIndicator
지시문을 통해 달성할 수 있습니다.
<igx-stepper>
<ng-template igxStepActiveIndicator>
<igx-icon>edit</igx-icon>
</ng-template>
<ng-template igxStepInvalidIndicator>
<igx-icon>error</igx-icon>
</ng-template>
<ng-template igxStepCompletedIndicator>
<igx-icon>check</igx-icon>
</ng-template>
...
</igx-stepper>
html
이 템플릿은 관련 상태의 모든 단계에 적용됩니다.
Angular 스테퍼 애니메이션
Angular 스테퍼 애니메이션은 최종 사용자에게 정의된 단계와 상호 작용하는 아름다운 경험을 제공합니다. 사용 가능한 애니메이션 옵션은 스테퍼의 방향에 따라 다릅니다.
스테퍼가 수평 방향 인 경우 기본적으로 slide
애니메이션을 사용하도록 구성됩니다. 또한 대안으로 fade
지원합니다. 애니메이션은 horizonAnimationType 입력을 통해 구성됩니다.
수직 방향 레이아웃에서는 VerticalAnimationType 속성을 사용하여 애니메이션 유형을 정의할 수 있습니다. 기본적으로 해당 값은 grow
이며 사용자는 fade
되도록 설정할 수도 있습니다.
두 애니메이션 유형 입력 모두에 none
설정하면 스테퍼 애니메이션이 비활성화됩니다.
IgxStepperComponent는 단계 간 전환 기간을 구성하는 기능을 제공합니다. 이는 숫자를 인수로 사용하는 animationDuration 속성을 통해 달성할 수 있으며 두 방향 모두에 공통됩니다.
키보드 탐색
Angular Stepper는 최종 사용자에게 다양한 키보드 상호 작용을 제공합니다. 이 기능은 기본적으로 활성화되어 있으며 최종 사용자가 단계를 쉽게 탐색할 수 있도록 합니다. IgxStepperComponent 탐색은 W3 접근성 표준을 준수하며 사용하기 편리합니다.
주요 조합
- 탭- 다음 탭 가능한 요소로 포커스를 이동합니다.
- Shift + Tab- 이전 탭 가능 요소로 포커스를 이동합니다.
- 아래쪽 화살표-
igx-stepper
수직 방향 일 때 초점을 다음 액세스 가능한 단계의 헤더로 이동합니다. - 위쪽 화살표-
igx-stepper
수직 방향 일 때 액세스 가능한 이전 단계의 헤더로 초점을 이동합니다. - 왼쪽 화살표- 초점을 두 방향 모두에서 액세스 가능한 이전 단계의 헤더로 이동합니다.
- 오른쪽 화살표- 초점을 두 방향 모두에서 액세스 가능한 다음 단계의 헤더로 이동합니다.
- 홈-
igx-stepper
에서 활성화된 첫 번째 단계의 헤더로 초점을 이동합니다. - 종료-
igx-stepper
에서 마지막으로 활성화된 단계의 헤더로 포커스를 이동합니다. - Enter / Space- 현재 초점을 맞춘 단계를 활성화합니다.
의도적으로 사용자가 단계 헤더 위에서 Tab 키를 누르면 포커스가 단계 콘텐츠 컨테이너로 이동합니다. 컨테이너를 건너뛰어야 하는 경우 개발자는 콘텐츠 컨테이너 [tabIndex]="-1"
설정해야 합니다.
스테퍼 구성 요소는 로우 코드, 드래그 앤 드롭 App Builder ™ 에서도 사용할 수 있습니다.
Angular 스테퍼 스타일링
Ignite UI for Angular 사용하면 igx-stepper
의 모양을 크게 변경할 수 있습니다.
먼저 테마 엔진에서 제공하는 기능을 사용하려면 스타일 파일에 index
파일을 가져와야 합니다.
@use "igniteui-angular/theming" as *;
// IMPORTANT: Prior to Ignite UI for Angular version 13 use:
// @import '~igniteui-angular/lib/core/styles/themes/index';
scss
가장 간단한 접근 방식에 따라 스테퍼 테마를 확장하고 변경하려는 매개변수를 전달하는 새 테마를 만듭니다.
$custom-stepper-theme: stepper-theme(
$indicator-background: #fff,
$current-indicator-background: #f6cd28,
$current-indicator-outline: #351e65,
$current-title-color: #351e65,
$current-subtitle-color: #5f4691,
$complete-indicator-background: #351e65,
$complete-indicator-outline: #351e65,
$complete-title-color: red,
$complete-subtitle-color: #5f4691,
$border-radius-step-header: 16px,
$border-radius-indicator: 10px 4px 10px 4px,
$step-separator-color: #f6cd28,
$complete-step-separator-color: #351e65,
);
scss
마지막 단계는 구성 요소의 테마를 포함하는 것입니다.
@include css-vars($custom-stepper-theme);
scss
데모
아래 샘플은 Ignite UI for Angular 통해 적용된 간단한 스타일링을 보여줍니다.
import { Component, ViewChild } from '@angular/core';
import { IgxStepperComponent, IStepChangingEventArgs, IgxStepActiveIndicatorDirective, IgxIconComponent, IgxStepCompletedIndicatorDirective, IgxStepComponent, IgxStepTitleDirective, IgxStepSubtitleDirective, IgxStepContentDirective, IgxButtonDirective } from 'igniteui-angular';
@Component({
selector: 'app-stepper-styling-sample',
styleUrls: ['./stepper-styling-sample.component.scss'],
templateUrl: './stepper-styling-sample.component.html',
imports: [IgxStepperComponent, IgxStepActiveIndicatorDirective, IgxIconComponent, IgxStepCompletedIndicatorDirective, IgxStepComponent, IgxStepTitleDirective, IgxStepSubtitleDirective, IgxStepContentDirective, IgxButtonDirective]
})
export class StepperStylingSampleComponent {
@ViewChild('stepper', { read: IgxStepperComponent })
public stepper: IgxStepperComponent;
public activeStepChanging(evt: IStepChangingEventArgs): void {
this.stepper.steps.forEach(step => {
if (step.index >= evt.oldIndex && step.index < evt.newIndex) {
step.completed = true;
}
});
}
public reset(): void {
this.stepper.steps.forEach(step => step.completed = false);
this.stepper.reset();
}
}
ts<igx-stepper #stepper (activeStepChanging)="activeStepChanging($event)">
<ng-template igxStepActiveIndicator>
<igx-icon>edit</igx-icon>
</ng-template>
<ng-template igxStepCompletedIndicator>
<igx-icon>check</igx-icon>
</ng-template>
<igx-step>
<span igxStepTitle>Order</span>
<span igxStepSubtitle>required</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.next()">NEXT</button>
</div>
</div>
</igx-step>
<igx-step>
<span igxStepTitle>Payment</span>
<span igxStepSubtitle>Optional</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">PREVIOUS</button>
<button igxButton="contained" (click)="stepper.next()">NEXT</button>
</div>
</div>
</igx-step>
<igx-step>
<span igxStepTitle>Confirmation</span>
<span igxStepSubtitle>required</span>
<div igxStepContent>
<div class="sample-step-actions">
<button igxButton="contained" (click)="stepper.prev()">PREVIOUS</button>
<button igxButton="contained" (click)="reset()">RESET</button>
</div>
</div>
</igx-step>
</igx-stepper>
html@use "layout.scss";
@use "igniteui-angular/theming" as *;
$stepper-theme: stepper-theme(
$indicator-background: #fff,
$current-indicator-background: #f6cd28,
$current-indicator-outline: #351e65,
$current-title-color: #351e65,
$current-subtitle-color: #5f4691,
$complete-indicator-background: #351e65,
$complete-indicator-outline: #351e65,
$complete-title-color: red,
$complete-subtitle-color: #5f4691,
$border-radius-step-header: 16px,
$border-radius-indicator: 10px 4px 10px 4px,
$step-separator-color: #f6cd28,
$complete-step-separator-color: #351e65,
);
@include css-vars($stepper-theme);
scss
API 참조
추가 리소스
우리 커뮤니티는 활발하고 항상 새로운 아이디어를 환영합니다.