700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > [Angular实战网易云]——29 注册

[Angular实战网易云]——29 注册

时间:2019-11-28 03:10:50

相关推荐

[Angular实战网易云]——29 注册

注册

注册功能是此项目中最后一个功能模块,我原本以为注册的逻辑很简单,没想到如此的繁琐,以至于项目频频短路,而且没有测试用的注册账号,所以在真的注册时并没有拿到相应的数据。

其次还有和收藏分享有关的功能,其基本的实现逻辑与登录等类似,区别之处就是数据的交互并不只是单一的接口,有关数据状态的变更还是要依靠ngrx来检测,所以关于分享与收藏并不打算繁琐的总结了。

场景

在登录页面和首页头像处有注册的入口,在点击触发后能够将弹窗显示并且类型为事先定义的register类型。在注册页面注入表单组件能够获取手机号码以及设置密码,然后能够根据密码来调用验证码的发送与判断。.

页面跳转

ponent.html

<li nz-menu-item (click)="openModalByMenu('register')"><i nz-icon nzType="user-add" nzTheme="outline"></i>注册</li>

ponent.ts

constructor(private memberBatchActionsServer: MemberBatchActionsService,) {}openModalByMenu (type: 'loginByPhone' | 'register') {if (type === 'loginByPhone') {this.openModal(ModalTypes.LoginByPhone);} else {this.openModal(ModalTypes.Register);}}// 打开弹窗openModal (type: ModalTypes) {this.memberBatchActionsServer.controlModal(true, type);}

弹窗的状态逻辑也是根据之前设置的action实现的。而在首页订阅弹框的显隐状态与类型即可动态的添加浮层。

wy-layer-ponent.html

<div class="register modal-content"><div class="modal-wrap" *ngIf="!showCode else code"><form nz-form nzLayout="vertical" [formGroup]="formModel" (ngSubmit)="onSubmit()"><nz-form-item><nz-form-label>手机号</nz-form-label><nz-form-control nzHasFeedback nzErrorTip="请填写正确的手机号"><nz-input-group nzPrefixIcon="mobile"><input type="tel" nz-input placeholder="请输入手机号" formControlName="phone"></nz-input-group></nz-form-control></nz-form-item><nz-form-item><nz-form-label>密码</nz-form-label><nz-form-control nzHasFeedback nzErrorTip="请输入密码"><nz-input-group nzPrefixIcon="lock"><input type="password" nz-input placeholder="请输入密码" formControlName="password"></nz-input-group></nz-form-control></nz-form-item><nz-form-item><nz-form-control><button nz-button nzBlock nzType="primary" [disabled]="!formModel.valid">下一步</button></nz-form-control></nz-form-item></form></div><ng-template #code><div class="modal-wrap"><app-wy-check-code[phone]="formModel.get('phone').value"[codePass]="codePass"[timing]="timing"(onCheckCode)="onCheckCode($event)"(onReatSendCode)="sendCode()"(onCheckExist)="onCheckExist()"></app-wy-check-code></div></ng-template></div><div class="m-footer clearfix"><a (click)="changeType()">&lt; 其它登陆方式</a></div>

打开页面时会显示表单组件,动态响应输入值后点击下一步即是发送验证码,此时页面会改变,因此可以创建新的组件来单独实现验证码的检查。

wy-layer-ponent.ts

enum Exist {'存在' = 1,'不存在' = -1}@Component({selector: 'app-wy-layer-register',templateUrl: './wy-layer-ponent.html',styleUrls: ['./wy-layer-ponent.less'],changeDetection: ChangeDetectionStrategy.OnPush})export class WyLayerRegisterComponent implements OnInit, OnChanges {@Input() visible = false;@Output() onChangeModalType = new EventEmitter<string>();@Output() onRegister = new EventEmitter<string>();showCode = false;formModel: FormGroup;timing: number;codePass: string | boolean = '';constructor(private fb: FormBuilder,private message: NzMessageService,private memberServe: MemberService,private cdr: ChangeDetectorRef) {}ngOnChanges (changes: SimpleChanges): void {if (changes.visible && !changes.visible.firstChange) {this.formModel.markAllAsTouched();if (!this.visible) {this.showCode = false;}}}ngOnInit (): void {this.formModel = this.fb.group({phone: ['', [Validators.required, Validators.pattern(/^1\d{10}$/)]],password: ['', [Validators.required, Validators.minLength(6)]]})}onSubmit () {if (this.formModel.valid) {this.sendCode();}}sendCode () {this.memberServe.sendCode(this.formModel.get('phone').value).subscribe(() => {this.timing = 60;if (!this.showCode) {this.showCode = true;}this.cdr.markForCheck();interval(1000).pipe(take(60)).subscribe(() => {this.timing--;this.cdr.markForCheck();})}, error => this.message.error(error.message))}onCheckCode (code: string) {this.memberServe.checkCode(this.formModel.get('phone').value, Number(code)).subscribe(() => this.codePass = true,() => this.codePass = false,() => this.cdr.markForCheck());}onCheckExist () {const phone = this.formModel.get('phone').value;this.memberServe.checkExist(Number(phone)).subscribe(res => {console.log('checkExist', res);if (Exist[res] === '存在') {this.message.error('账号已存在,可直接登陆');this.changeType(ModalTypes.LoginByPhone);} else {this.onRegister.emit(phone);}});}changeType (type = ModalTypes.Default) {this.onChangeModalType.emit(type);this.showCode = false;this.formModel.reset()}}

输入手机号与密码之后点击下一步将会触发onSubmit中sendCode回调,其中有段timer的递减是用来提示重新发送验证码的倒计时,,其中有点问题的就是变更监测的模式是使用了onPush策略,所以timer的递减后传给验证码组件时就会出现停顿的现象,,此时要手动的触发变更监测。就是使用ChangeDelectorRef类型中markForCheck方法。

验证码组件

wy-check-ponent.html

<div class="check-code"><div class="js-mobwrap"><p class="s-fc3">你的手机号:<strong class="s-fc1"><span class="js-mob">{{phone}}</span></strong></p><p class="s-fc4">为了安全,我们会给您发送短信验证码</p></div><div class="form"><form nz-form nzLayout="vertical" [formGroup]="formModel" (ngSubmit)="onSubmit()"><nz-form-item><nz-form-control><app-wy-code formControlName="code"></app-wy-code><div class="send clearfix"><span class="err" [hidden]="!showErrorTip">验证码不正确</span><span class="txt" *ngIf="!showRepeatBtn else repeatBtn">{{timing}}s</span><ng-template #repeatBtn><span class="txt repeat" (click)="onReatSendCode.emit()">重新发送</span></ng-template></div></nz-form-control></nz-form-item><nz-form-item><nz-form-control><button nz-button nzType="primary" nzBlock>下一步</button></nz-form-control></nz-form-item></form></div></div>

此页面是下一步后的跳转页面,其中红色框中的验证码输入内容及逻辑放在了新的组件中。

wy-check-ponent.ts

private phoneHideStr = '';formModel: FormGroup;showRepeatBtn = false;showErrorTip = false;@Input() codePass = false;@Input() timing: number;@Input()set phone (phone: string) {const arr = phone.split('');arr.splice(3, 4, '****');this.phoneHideStr = arr.join('');}get phone () {return this.phoneHideStr;}@Output() onCheckCode = new EventEmitter<string>();@Output() onReatSendCode = new EventEmitter<string>();@Output() onCheckExist = new EventEmitter<void>();constructor() {}ngOnInit () {this.formModel = new FormGroup({code: new FormControl('', [Validators.required, Validators.pattern(/\d{4}/)])});const codeControl = this.formModel.get('code');codeControl.statusChanges.subscribe(status => {if (status === 'VALID') {this.onCheckCode.emit(this.formModel.value.code);}});}ngOnChanges (changes: SimpleChanges): void {if (changes.timing) {this.showRepeatBtn = this.timing <= 0;}if (changes.codePass && !changes.codePass.firstChange) {this.showErrorTip = !this.codePass;}}onSubmit () {if (this.formModel.valid && this.codePass) {this.onCheckExist.emit();}}

页面初始化时,会创建表单组件,添加表单控制器code。因为在展示手机号的时候要对数据加密,所以添加了对手机号二次处理的set、get方法。然后对表单控制器属性添加状态监测,当表单验证通过时会将输入的验证码发射出去,然后调用验证接口。

处理验证码

验证码的输入逻辑以及样式放在了单独的组件中。但是验证逻辑仍然在父组件中,所以关于其输入的结构都会在父层做处理。

wy-ponent.html

<div class="code-wrap clearfix" #codeWrap><div class="u-word" *ngFor="let item of inputArr; index as i" [class.focus]="result[i]"><input class="item" maxlength="1" /></div></div>

这里是循环了四个输入框,但是样式上有点小复杂,自己尝试实现,未果!

wy-ponent.ts

const CODELEN = 4;@Component({selector: 'app-wy-code',templateUrl: './wy-ponent.html',styleUrls: ['./wy-ponent.less'],providers: [{provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => WyCodeComponent),multi: true}]})export class WyCodeComponent implements OnInit, ControlValueAccessor {inputArr = [];inputsEl: HTMLElement[];private code: string;result: string[] = [];currentFocusIndex = 0;private destory$ = new Subject();@ViewChild('codeWrap', {static: true }) private codeWrap: ElementRef;constructor(private cdr: ChangeDetectorRef) {}ngOnInit () {this.inputArr = Array(CODELEN).fill('');}ngAfterViewInit (): void {this.inputsEl = this.codeWrap.nativeElement.getElementsByClassName('item') as HTMLElement[];this.inputsEl[0].focus();for (let a = 0; a < this.inputsEl.length; a++) {const item = this.inputsEl[a];fromEvent(item, 'keyup').pipe(takeUntil(this.destory$)).subscribe((event: KeyboardEvent) => this.listenKeyUp(event));fromEvent(item, 'click').pipe(takeUntil(this.destory$)).subscribe(() => this.currentFocusIndex = a);}}private listenKeyUp (event: KeyboardEvent) {const target = event.target as HTMLInputElement;const value = target.value;const isBackSpace = event.key === "Backspace";if (/\D/.test(value)) {target.value = '';this.result[this.currentFocusIndex] = '';} else if (value) {this.result[this.currentFocusIndex] = value;this.currentFocusIndex = (this.currentFocusIndex + 1) % CODELEN;this.inputsEl[this.currentFocusIndex].focus();} else if (isBackSpace) {this.result[this.currentFocusIndex] = '';this.currentFocusIndex = Math.max(this.currentFocusIndex - 1, 0);this.inputsEl[this.currentFocusIndex].focus();}this.checkResult(this.result);}private checkResult (result: string[]) {const codeStr = result.join('');this.setValue(codeStr);}private setValue (code: string) {this.code = code;this.onValueChange(code);this.cdr.markForCheck();}private onValueChange (value: string): void {}private onTouched (): void {}writeValue (code: string): void {this.setValue(code);}registerOnChange (fn: (value: string) => void): void {this.onValueChange = fn;}registerOnTouched (fn: () => void): void {this.onTouched = fn;}ngOnDestroy (): void {this.destory$.next();this.destory$.complete();}}

当组件视图初始化之后会对四个输入框进行监听,对删除键的交互以及匹配输入值不能为数值以外的其他值。

其中使用了自定义表单控件ControlValueAccessor,关于这个知识点我的能力是解释不清楚的,再看了一些博客和介绍之后,推荐有兴趣的可以去看一下这篇文章:

别再对 Angular 表单的 ControlValueAccessor 感到迷惑

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。