基于Ionic 5/Angular实现单页一题表单的方案咨询及示例请求
解决方案:Ionic 5 + Angular 实现单题分步表单
一、关于跨路由拆分<form>的问题
结论:无法直接跨路由拆分<form>标签
Angular路由切换时,原组件会被销毁,DOM树重新构建,<form>元素无法跨越不同路由的组件存在。替代方案是用共享服务存储表单数据,在各路由组件中独立维护表单控件,最后统一提交。
二、单屏单题分步表单(同一组件内)
这种方式适合不需要路由跳转,仅在同一页面切换问题的场景,用Angular响应式表单管理数据,通过控制显示/隐藏切换问题。
1. 创建表单共享服务
// src/app/services/quiz-form.service.ts import { Injectable } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; @Injectable({ providedIn: 'root' }) export class QuizFormService { quizForm: FormGroup; currentStep = 0; questions = [ { key: 'q1', label: '你最喜欢的编程语言是?', type: 'text' }, { key: 'q2', label: '你使用Ionic多久了?', type: 'number' }, { key: 'q3', label: '你常用的前端框架是?', options: ['Angular', 'React', 'Vue'] } ]; constructor(private fb: FormBuilder) { this.initForm(); } initForm() { const formControls = {}; this.questions.forEach(q => { formControls[q.key] = ['', Validators.required]; }); this.quizForm = this.fb.group(formControls); } nextStep() { if (this.currentStep < this.questions.length - 1) { this.currentStep++; } } prevStep() { if (this.currentStep > 0) { this.currentStep--; } } submitQuiz() { if (this.quizForm.valid) { console.log('提交的答案:', this.quizForm.value); // 此处添加提交到后端的逻辑 } } }
2. 实现问卷组件
// src/app/quiz/quiz.component.ts import { Component } from '@angular/core'; import { QuizFormService } from '../services/quiz-form.service'; @Component({ selector: 'app-quiz', templateUrl: './quiz.component.html', styleUrls: ['./quiz.component.scss'], }) export class QuizComponent { constructor(public quizService: QuizFormService) {} get currentQuestion() { return this.quizService.questions[this.quizService.currentStep]; } isLastStep() { return this.quizService.currentStep === this.quizService.questions.length - 1; } }
3. 组件模板(Ionic UI)
<!-- src/app/quiz/quiz.component.html --> <ion-header> <ion-toolbar> <ion-title>分步问卷</ion-title> </ion-toolbar> </ion-header> <ion-content class="ion-padding"> <form [formGroup]="quizService.quizForm"> <!-- 当前问题区域 --> <div *ngIf="currentQuestion"> <ion-label class="ion-text-wrap">{{ currentQuestion.label }}</ion-label> <!-- 文本输入框 --> <ion-input *ngIf="currentQuestion.type === 'text'" formControlName="{{ currentQuestion.key }}" type="text" placeholder="请输入答案" class="ion-margin-top" ></ion-input> <!-- 数字输入框 --> <ion-input *ngIf="currentQuestion.type === 'number'" formControlName="{{ currentQuestion.key }}" type="number" placeholder="请输入数字" class="ion-margin-top" ></ion-input> <!-- 单选选项组 --> <ion-radio-group *ngIf="currentQuestion.options" formControlName="{{ currentQuestion.key }}" class="ion-margin-top" > <ion-item *ngFor="let opt of currentQuestion.options"> <ion-label>{{ opt }}</ion-label> <ion-radio slot="start" [value]="opt"></ion-radio> </ion-item> </ion-radio-group> </div> <!-- 导航与提交按钮 --> <div class="ion-margin-top ion-text-center"> <ion-button *ngIf="quizService.currentStep > 0" (click)="quizService.prevStep()" fill="outline" >上一题</ion-button> <ion-button *ngIf="!isLastStep()" (click)="quizService.nextStep()" [disabled]="!quizService.quizForm.get(currentQuestion.key).valid" class="ion-margin-start" >下一题</ion-button> <ion-button *ngIf="isLastStep()" (click)="quizService.submitQuiz()" [disabled]="!quizService.quizForm.valid" color="success" class="ion-margin-start" >提交问卷</ion-button> </div> <!-- 提交后显示答案汇总 --> <div *ngIf="quizService.quizForm.valid && isLastStep() && quizService.quizForm.dirty" class="ion-margin-top"> <h3 class="ion-text-center">你的答案</h3> <ion-list> <ion-item *ngFor="let q of quizService.questions"> <ion-label>{{ q.label }}</ion-label> <ion-note slot="end">{{ quizService.quizForm.value[q.key] }}</ion-note> </ion-item> </ion-list> </div> </form> </ion-content>
三、跨路由分步表单(多组件)
如果需要通过路由跳转切换问题,用共享服务存储表单状态,每个路由对应一个问题组件。
1. 扩展共享服务(添加路由导航)
// 在QuizFormService中新增 import { Router } from '@angular/router'; constructor(private fb: FormBuilder, private router: Router) { ... } goToNextQuestion() { this.nextStep(); this.router.navigate([`/question/${this.currentStep + 1}`]); } goToPrevQuestion() { this.prevStep(); this.router.navigate([`/question/${this.currentStep + 1}`]); }
2. 创建单个问题组件
// src/app/question/question.component.ts import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QuizFormService } from '../services/quiz-form.service'; @Component({ selector: 'app-question', templateUrl: './question.component.html', styleUrls: ['./question.component.scss'], }) export class QuestionComponent implements OnInit { questionIndex: number; question: any; constructor( private route: ActivatedRoute, public quizService: QuizFormService ) {} ngOnInit() { this.route.paramMap.subscribe(params => { this.questionIndex = +params.get('index') - 1; this.quizService.currentStep = this.questionIndex; this.question = this.quizService.questions[this.questionIndex]; }); } isLastQuestion() { return this.questionIndex === this.quizService.questions.length - 1; } }
3. 配置路由
// src/app/app-routing.module.ts import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { QuestionComponent } from './question/question.component'; const routes: Routes = [ { path: '', redirectTo: '/question/1', pathMatch: 'full' }, { path: 'question/:index', component: QuestionComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
核心要点
- 响应式表单:统一管理所有问题的控件,简化验证和数据收集逻辑。
- 共享服务:跨组件/路由时,通过服务存储表单状态,避免数据丢失。
- 步骤控制:通过
currentStep变量控制当前显示的问题,切换前验证输入有效性。
内容的提问来源于stack exchange,提问作者ALLAN ABU
相关产品推荐
相关产品推荐

