【Angular实战/网易云】登录组件

一. 表单的基础知识

<form>和</form>之间包裹起来的内容就是表单。其中,内部的<input>啊,<button>啊,<textarea>这些,都叫做表单控件

其中<button>的默认type为submit,这个意味着你点击这个button就会自动帮你提交,你如果强行绑定(click)事件就会引发错误。

相似的type还有reset, 这个也不能绑定事件。

 

二. ng-zorro表单

这里我使用的框架是ng-zorro,模板代码如下:

<div class="login-phone modal-content">
  <div class="modal-wrap">
    <form nz-form class="login-form" [formGroup]="formModal" (ngSubmit)="onSubmit()">
      <nz-form-item>
        <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-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>
          <div class="tools">
            <label nz-checkbox class="remember" formControlName="remember">
              <span>记住密码</span>
            </label>
          </div>
          <button nz-button class="login-form-button" [disabled]="!formModal.valid" nzType="primary" nzBlock>登陆</button>
        </nz-form-control>
      </nz-form-item>
    </form>
  </div>
</div>

注意到三点:

  • 我们通过绑定[formGroup]属性确定了表单模型变量。
  • 通过(ngOnSubmit)绑定了提交的事件
  • 每一个控件通过formControlName确定了表单控件的引用

 

三.  表单逻辑

其实在登录组件背后的逻辑就是表单的各种操作,比如在初始化的时候设置初值:

  constructor(private fb: FormBuilder) {
    this.setFormModal({phone: '', password: '', remember: false});
  }
  
  setFormModal({phone, password, remember}) {
    this.formModal = this.fb.group({
      phone: [phone, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
      password: [password, [Validators.required, Validators.minLength(6)]],
      remember: [remember]
    });
  }

然后当你点击按钮,出发onSubmit事件的时候把LoginParams发送出去:

  onSubmit(): void {
    if (this.formModal.valid) {
      this.onLogin.emit(this.formModal.value);
    }
  }

 

四. 调用接口登录

调用接口,老生长谈了。创建一个新的services.ts文件:

  login(loginParam: LoginParams): Observable<User> {
    const usernamePasswordPair = {phone: loginParam.phone, password: loginParam.password};
    const param = new HttpParams({fromString: queryString.stringify(usernamePasswordPair)});
    return this.http.get(this.urlPrefix + 'login/cellphone', {params: param}).pipe(
      map(user => user as User)
    );
  }

之前我们提到把登录的参数交给父组件处理了,接下来我们来看看父组件中是怎么处理的:

父组件中主要做这么几件事情:

  • 接收接口返回的数据,成功与否可以用messageService来替换console.log和console.error。注意成功了还得把模态框关了
  • 把userId保存在localStorage中,用来记住用户的登录状态;同时,利用状态管理插件改变userId这个状态,这样就能通知所有用来显示用户信息的组件渲染新的DOM。
  • 还可以把用户名和密码加密后保存在localStorage中,不过不推荐(原因见下)
  onChangeModalType(type) {
    this.memberBatchServices.controlModal(true, type);
  }

  onLogin(loginParam: LoginParams) {
    this.memberService.login(loginParam).subscribe(res => {
      this.user = res;
      console.log(this.user);
      this.memberBatchServices.controlModal(false);
      this.alertMessage('success', '登陆成功!');
      this.storageService.setStorage({
        key: 'wyUserId',
        value: this.user.profile.userId.toString()
      });
      this.store$.dispatch(SetUserId({userId: this.user.profile.userId.toString()}));

      if (loginParam.remember) {
        this.storageService.setStorage({
          key: 'wyRememberLogin',
          value: JSON.stringify(codeJson(loginParam))
        });
      } else {
        this.storageService.removeStorage('wyRememberLogin');
      }

    }, this.handleErrorMessage);
  }

  private alertMessage(type: string, msg: string) {
    this.messageService.create(type, msg);
  }

 

五. 记住用户登录状态

我们在登陆以后userId就直接保存在我们的localStorage中了。如果不logout的话不会清除这个。

之前也提到过,我们用这个userId是用来记住用户登录状态的。原理如下:

在页面初始化之前,通过userId来调用一个新的接口这个接口返回和login一样的用户数据,这样我们就能把登陆后的数据渲染到页面上了。

  constructor(
    // 。。。
    private storageService: StorageService,
  ) {
    // 从localStorage中获取信息
    const userId = this.storageService.getStorage('wyUserId');
    if (userId) {
      this.store$.dispatch(SetUserId({userId}));
      this.memberService.getUserDetail(userId).subscribe(user => this.user = user);
    }
}

这里也展示一下StorageService把,他主要是用来封装SessionStorage和LocalStorage两个操作的(原理是用[]+字符串访问元素):

@Injectable({
  providedIn: ServicesModule
})
export class StorageService {

  constructor(@Inject(WINDOW) private win: Window) {

  }

  getStorage(key: string, type = 'local'): string {
    return this.win[type + 'Storage'].getItem(key);
  }

  setStorage(params: AnyJson | AnyJson[], type = 'local') {
    const kv = Array.isArray(params) ? params : [params];
    for (const { key, value } of kv) {
      this.win[type + 'Storage'].setItem(key, value.toString());
    }
  }

  removeStorage(params: string | string[], type = 'local') {
    const kv = Array.isArray(params) ? params : [params];
    for (const key of kv) {
      this.win[type + 'Storage'].removeItem(key);
    }
  }
}

 

六. 保存用户登录用户名和密码

我们之前的登录选项中有记住用户名和密码这个选项,当我们勾选了这个选项之后,下次点击登录按钮就会从localStorage中取出保存的用户名和密码。

不过,这种做法相当不安全,这里我们也是通过了base64的加密算法来进行加密,不过依然不够安全:

    // 这段代码是从上面摘抄来的。。。     
     if (loginParam.remember) {
        this.storageService.setStorage({
          key: 'wyRememberLogin',
          value: JSON.stringify(codeJson(loginParam))
        });
      } else {
        this.storageService.removeStorage('wyRememberLogin');
      }

一个比较推荐的做法是,不用记住密码这种选项,而让浏览器自动记住你的登录信息

 

七. 登出做什么

  • 相关变量,全部设置为null。
  • 利用messageService提示用户已经退出成功。
  • 删除localStorage中的相关信息。
  onLogout() {
    this.memberService.logout().subscribe(() => {
      this.alertMessage('success', '退出成功!');
      this.user = null;
      this.store$.dispatch(SetUserId({userId: null}));
      this.storageService.removeStorage('wyUserId');
    }, this.handleErrorMessage);
  }

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值