Angular2中多步骤表单之间的交换数据:经验证的方法是什么?

13 forms router multi-step data-exchange angular

我可以想象以下方法在多步骤表单之间交换数据:

1)为每个表单步骤创建一个组件,并通过@input,@ output输出组件之间的数据(例如,您不能从步骤5更改为2)

2)data在新路由器中使用新属性(参见此处)(例如,您无法从步骤5更改为2))

3)用于存储数据的共享服务(依赖注入)(组件交互)(例如,您可以从步骤5更改为2)

4)@ ngrx/store的新基础(还没有真正经验)

你能给出一些"获得经验值",你用了什么?为什么?

nik*_*ong 15

请参阅下面的编辑.


SessionStorage在我看来,使用并不是严格意义上的"角度"方式 - 共享服务是要走的路.在步骤之间实现路由会更好(因为每个组件可以有你自己的形式和你认为合适的不同逻辑:

const multistepRoutes: Routes = [
  {
    path: 'multistep',
    component: MultistepComponent,
    children: [
      {
        path: '',
        component: MultistepBaseComponent,
      },
      {
        path: 'step1',
        component: MultistepStep1Component
      },
      {
        path: 'step2',
        component: MultistepStep2Component
      }
    ]
  }
];
Run Code Online (Sandbox Code Playgroud)

该服务multistep.service可以保存模型并实现组件的逻辑:

import { Injectable, Inject } from '@angular/core';
import { Router } from '@angular/router';

@Injectable()
export class MultistepService { 

  public model = {};
  public baseRoute = '/multistep';
  public steps = [

    'step1', 
    'step2'

  ];

  constructor (
    @Inject(Router) public router: Router) { };

  public getInitialStep() {

    this.router.navigate([this.baseRoute + '/' + this.steps[0]]);

  };

  public goToNextStep (direction /* pass 'forward' or 'backward' to service from view */): any {

    let stepIndex = this.steps.indexOf(this.router.url.split('/')[2]);

    if (stepIndex === -1 || stepIndex === this.steps.length) return;

    this.router.navigate([this.baseRoute + '/' + this.steps[stepIndex + (direction === 'forward' ? 1 : -1)]]);

  };

}; 
Run Code Online (Sandbox Code Playgroud)

祝好运.


编辑12/6/2016


实际上,现在已经使用表单API一段时间了,我不相信我以前的答案是实现这一目标的最佳方法.

一种优选的方法是创建一个顶级FormGroup,其中包含多步形式中的每个步骤,就像它自己的属性FormControl(FormGroupFormArray)一样controls.在这种情况下,顶级表单将是表单状态的单一事实来源,并且创建的每个步骤(ngOnInit/constructor)都能够从顶层读取其各自步骤的数据FormGroup.看到伪代码:

   const topLevelFormGroup = new FormGroup({
       step1: new FormGroup({fieldForStepOne: new FormControl('')}),
       step2: new FormGroup({fieldForStepTwo}),
       // ...
   });

   ... 

   // Step1Component

   class Step1Component { 
       private stepName: string = 'step1';
       private formGroup: FormGroup;
       constructor(private topLevelFormGroup: any /* DI */) {
           this.formGroup = topLevelFormGroup.controls[this.stepName];
       }
    }
Run Code Online (Sandbox Code Playgroud)

因此,表单的状态和每个步骤都保持在它应该的形式本身!

  • 使用Nikk的方法也可以避免儿童路线.父组件模板可以包含子组件,父组件可以根据用户的步进移动显示/隐藏子组件.在Parent组件中,您可以使用Reactive Form Creation方法创建带有控件的主FormGroup.这些表单控件将用于子组件.您可以使用@Input将主FormGroup从父级传递给子级.这意味着您将拥有一个包含多个表单控件的表单,以便在子组件中使用.这种方式来自父组件,您将始终具有更新的表单值. (2认同)

Kam*_*ski 8

为什么不使用会话存储?例如,您可以使用此静态助手类(TypeScript):

export class Session {

  static set(key:string, value:any) {
      window.sessionStorage.setItem(key, JSON.stringify(value));
  }

  static get(key:string) {
      if(Session.has(key)) return JSON.parse(window.sessionStorage[key])
      return null;
  }

  static has(key:string) {
      if(window.sessionStorage[key]) return true;
      return false;
  }

  static remove(key:string) {
      Session.set(key,JSON.stringify(null)); // this line is only for IE11 (problems with sessionStorage.removeItem)
      window.sessionStorage.removeItem(key);
  }

}
Run Code Online (Sandbox Code Playgroud)

使用上面的类,您可以将您的对象与多步骤表单数据放在一起并共享它(想法类似于许多后端框架中的"会话助手",例如php laravel).


另一种方法是创建Singleton服务.它看起来像那样(为了清楚起见,非常简单)(我不测试下面的代码,我从头开始):

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

@Injectable()
export class SessionService {

    _session = {};

    set(key:string, value:any) {
         this._session[key]= value; // You can also json-ize 'value' here
    }

    get(key:string) {
         return this._session[key]; // optionally de-json-ize here
     }

     has(key:string) {
         if(this.get(key)) return true;
         return false;
     }

     remove(key:string) {         
         this._session[key]=null;
     }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的主文件中你引导应用程序:

...
return bootstrap(App, [
  ...
  SessionService
])
...
Run Code Online (Sandbox Code Playgroud)

最后一步 - 关键:当您想在组件中使用单例服务时 - 不要将int放在提供程序部分(这是由于angular2 DI行为 - 请阅读上面关于单例服务的链接).从表单步骤2到步骤3的示例如下:

import {Component} from '@angular/core';
import {SessionService} from './sessionService.service';
...

@Component({
  selector: 'my-form-step-2',
  // NO 'providers: [ SessionService ]' due to Angular DI behavior for singletons
  template: require('./my-form-step-2.html'),
})

export class MyFormStep2  {

  _formData = null;

  constructor(private _SessionService: SessionService) {
     this._formData = this._SessionService.get('my-form-data')
  }

  ...
  submit() {
     this._SessionService.set('my-form-data', this._formData)
  }

}
Run Code Online (Sandbox Code Playgroud)

看起来应该是这样的.