在Angular中运用类Redux东西—ngrx/store

原文:Connect Angular Forms to @ngrx/store

《在Angular中运用类Redux东西—ngrx/store》

这篇文章中,我们将要议论如何用ngrx/effects衔接Angular表单和ngrx/store

我们终究的效果是如许的

\\new-story0.component.ts

@Component({
  selector: 'new-story-form',
  template: `
    <form [formGroup]="newStory"
          (submit)="submit($event)"
          (success)="onSuccess()"
          (error)="onError($event)"
          connectForm="newStory">
       ...controls
    </form>
    <div *ngIf="success">Success!</div>
    <div *ngIf="error">{{error}}</div>
})
class NewStoryFormComponent {...}

《在Angular中运用类Redux东西—ngrx/store》

The Reducer

轻易起见,我们会写一个简朴的reducer来构造治理我们运用里的一切的表单

状况(the state)将由一个用ID作为key,表格数据作为value的简朴对象构成

举个例子

\\connect-form.reducer.ts

const initialState = {
  newStory: {
    title: '',
    description: ''
  },
  contactUs: {
    email: '',
    message: ''
  }
}

export function forms(state = initialState, action) {
}

我们先构建一个action——UPDATE_FORM。这个action由两个key:path和value构成


\\connect-form1.reducer.ts

store.dispatch({
  type: UPDATE_FORM,
  payload: {
    path: 'newStory',
    value: formValue
  }
});

然后这个reducer将担任更新state

\\connect-form2.reducer.ts

export function forms(state = initialState, action) {
  if(action.type === UPDATE_FORM) { 
                       // newStory:           formValue
    return { ...state, [action.payload.path]: action.payload.value }
  }
}

衔接表单的组件——ConnectForm Directive

猎取State

我们想要基于state更新表单,所以我们须要path作为输入,然后掏出store中准确的片断


\\connect-form.directive.ts

@Directive({
  selector: '[connectForm]'
})
export class ConnectFormDirective {
  @Input('connectForm') path: string;

  constructor(private formGroupDirective: FormGroupDirective,
    private store: Store<AppState> ) {
      
    ngOnInit() {
      // Update the form value based on the state
      this.store.select(state => state.forms[this.path]).take(1).subscribe(formValue => {
        this.formGroupDirective.form.patchValue(formValue);
      });
    }
  }
}

我们抓取表单directive实例然后从store里更新表单数据

更新State

当表单数据转变时我们也须要更新表单状况。我们能够经由过程定阅(subscribe)这个valueChanges的可视察对象(observable)然后调理(dispatch)这个UPDATE_FORM的action来猎取值

\\connect-form1.directive.ts

this.formChange = this.formGroupDirective.form.valueChanges
  .subscribe(value => {
    this.store.dispatch({
      type: UPDATE_FORM,
      payload: {
        value,
        path: this.path, // newStory
      }
    });
})

这就是表单和State同步所要做的悉数工作了

关照和重置

有两件事我们要在这个部份完成

  1. 基于HTTP相应返返来显现关照给用户——我们须要保证关照直接传给组件并且不贮存信息在store里

有两点缘由

  • 一般,没有其他的组件须要这个信息

  • 我们不想每次都重置store

  1. 当提交胜利时重置表单

我们将让Angular尽其所能,处理好前端表单校验并重置表单

胜利的Action

胜利的Action包括表单的path属性所以我们能够晓得究竟哪一个表单须要重置,同时什么时刻须要去运用(emit)这个胜利的事宜


\\connect-form2.directive.ts

const FORM_SUBMIT_SUCCESS = 'FORM_SUBMIT_SUCCESS';
const FORM_SUBMIT_ERROR = 'FORM_SUBMIT_ERROR';
const UPDATE_FORM = 'UPDATE_FORM';

export const formSuccessAction = path => ({
  type: FORM_SUBMIT_SUCCESS,
  payload: {
    path
  }
});

非常的Action

同胜利的action一样,由于有path的存在,我们也晓得什么时刻去运用(emit)毛病非常 的事宜

\\connect-form3.directive.ts

export const formErrorAction = ( path, error ) => ({
  type: FORM_SUBMIT_ERROR,
  payload: {
    path,
    error
  }
});

我们须要建立 胜利 和 毛病非常 的输出 然后 监听 FORM_SUBMIT_ERRORFORM_SUBMIT_SUCCESS 的 action。

由于我们恰好要运用 ngrx/effects ,此时我们就能够用 Action 的效劳(service)来监听actions了


\\connect-form3.directive.ts

@Directive({
  selector: '[connectForm]'
})
export class ConnectFormDirective {
  @Input('connectForm') path : string;
  @Input() debounce : number = 300;
  @Output() error = new EventEmitter();
  @Output() success = new EventEmitter();
  formChange : Subscription;
  formSuccess : Subscription;
  formError : Subscription;

  constructor( private formGroupDirective : FormGroupDirective,
               private actions$ : Actions,
               private store : Store<any> ) {
  }

  ngOnInit() {
    this.store.select(state => state.forms[this.path])
      .debounceTime(this.debounce)
      .take(1).subscribe(val => {
      this.formGroupDirective.form.patchValue(val);
    });

    this.formChange = this.formGroupDirective.form.valueChanges
      .debounceTime(this.debounce).subscribe(value => {
        this.store.dispatch({
          type: UPDATE_FORM,
          payload: {
            value,
            path: this.path,
          }
        });
      });

    this.formSuccess = this.actions$
      .ofType(FORM_SUBMIT_SUCCESS)
      .filter(( { payload } ) => payload.path === this.path)
      .subscribe(() => {
        this.formGroupDirective.form.reset();
        this.success.emit();
      });

    this.formError = this.actions$
      .ofType(FORM_SUBMIT_ERROR)
      .filter(( { payload } ) => payload.path === this.path)
      .subscribe(( { payload } ) => this.error.emit(payload.error))
  }
}

固然,我们不能忘了清空定阅


\\connect-form4.directive.ts

ngOnDestroy() {
  this.formChange.unsubscribe();
  this.formError.unsubscribe();
  this.formSuccess.unsubscribe();
}

末了一步就是在有返回的时刻挪用表单的actions


\\connect-form4.directive.ts

import {
  formErrorAction,
  formSuccessAction
} from '../connect-form.directive';

@Effect() addStory$ = this.actions$
  .ofType(ADD_STORY)
  .switchMap(action =>
    this.storyService.add(action.payload)
    .switchMap(story => (Observable.from([{
      type: 'ADD_STORY_SUCCESS'
    }, formSuccessAction('newStory')])))
    .catch(err => (Observable.of(formErrorAction('newStory', err))))
  )

 

如今我们能够在组件里显现提醒了


\\new-story.component.ts

 @Component({
  selector: 'new-story-form',
  template: `
    <form [formGroup]="newStory"
          (submit)="submit($event)"
          (success)="onSuccess()"
          (error)="onError($event)"
          connectForm="newStory">
       ...controls
      <button [disabled]="newStory.invalid" type="submit">Submit</button>
    </form>
    <div *ngIf="success">Success!</div>
    <div *ngIf="error">{{error}}</div>
  `
})
class NewStoryFormComponent {

  constructor(private store: Store<AppState> ) {}

  onError(error) {
    this.error = error;
  }

  onSuccess() {
    this.success = true;
  }

  submit() {
    // You can also take the payload from the form state in your effect 
    // with the withLatestFrom observable
    this.store.dispatch({
      type: ADD_STORY,
      payload: ...
    })
  }

}
    原文作者:Asterisk
    原文地址: https://segmentfault.com/a/1190000010306020
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞