Angular 2.x 从 0 到 1 (二)史上最简单的 Angular2 教程
第一节:Angular 2.0 从0到1 (一) 第二节:用Form表单做一个登录控件对于login组件的小改造在 import { Component,OnInit } from '@angular/core'; @Component({ selector: 'app-login',template: ` <div> <input type="text"> <button>Login</button> </div> `,styles: [] }) export class LoginComponent implements OnInit { constructor() { } ngOnInit() { } } 我们增加了一个文本输入框和一个按钮,保存后返回浏览器可以看到结果 onClick() { console.log('button was clicked'); } 返回浏览器,并按F12调出开发者工具。当你点击Login时,会发现Console窗口输出了我们期待的文字。 <div> <input #usernameRef type="text"> <button (click)="onClick(usernameRef.value)">Login</button> </div> 在Component内部的onClick方法也要随之改写成一个接受username的方法 onClick(username) { console.log(username); } 现在我们再看看结果是什么样子,在文本输入框中键入“hello”,点击Login按钮,观察Console窗口:hello被输出了。 import { Component,template: ` <div> <input #usernameRef type="text"> <input #passwordRef type="password"> <button (click)="onClick(usernameRef.value,passwordRef.value)">Login</button> </div> `,styles: [] }) export class LoginComponent implements OnInit { constructor() { } ngOnInit() { } onClick(username,password) { console.log('username:' + username + "nr" + "password:" + password); } } 看看结果吧,在浏览器中第一个输入框输入“wang”,第二个输入框输入“1234567”,观察Console窗口,Bingo! 建立一个服务去完成业务逻辑如果我们把登录的业务逻辑在onClick方法中完成,当然也可以,但是这样做的耦合性太强了。设想一下,如果我们增加了微信登录、微博登录等,业务逻辑会越来越复杂,显然我们需要把这个业务逻辑分离出去。那么我们接下来创建一个AuthService吧,首先我们在srcapp下建立一个core的子文件夹( 下面我们为这个service添加一个方法,你可能注意到这里我们为这个方法指定了返回类型和参数类型。这就是TypeScript带来的好处,有了类型约束,你在别处调用这个方法时,如果给出的参数类型或返回类型不正确,IDE就可以直接告诉你错了。 import { Injectable } from '@angular/core'; @Injectable() export class AuthService { constructor() { } loginWithCredentials(username: string,password: string): boolean { if(username === 'wangpeng') return true; return false; } } 等一下,这个service虽然被创建了,但仍然无法在Component中使用。当然你可以在Component中import这个服务,然后实例化后使用,但是这样做并不好,仍然时一个紧耦合的模式,Angular2提供了一种依赖性注入(Dependency Injection)的方法。 什么是依赖性注入?如果不使用DI(依赖性注入)的时候,我们自然的想法是这样的,在 import { Component,OnInit } from '@angular/core'; //引入AuthService import { AuthService } from '../core/auth.service'; @Component({ selector: 'app-login',styles: [] }) export class LoginComponent implements OnInit { //声明成员变量,其类型为AuthService service: AuthService; constructor() { this.service = new AuthService(); } ngOnInit() { } onClick(username,password) { //调用service的方法 console.log('auth result is: ' + this.service.loginWithCredentials(username,password)); } } 这么做呢也可以跑起来,但存在几个问题:
下面我们看看如果使用DI是什么样子的,首先我们需要在组件的修饰器中配置AuthService,然后在组件的构造函数中使用参数进行依赖注入。 import { Component,OnInit } from '@angular/core'; import { AuthService } from '../core/auth.service'; @Component({ selector: 'app-login',styles: [],//在providers中配置AuthService providers:[AuthService] }) export class LoginComponent implements OnInit { //在构造函数中将AuthService示例注入到成员变量service中 //而且我们不需要显式声明成员变量service了 constructor(private service: AuthService) { } ngOnInit() { } onClick(username,password) { console.log('auth result is: ' + this.service.loginWithCredentials(username,password)); } } 看到这里你会发现我们仍然需要import相关的服务,这是import是要将类型引入进来,而provider里面会配置这个类型的实例。当然即使这样还是不太爽,可不可以不引入AuthService呢?答案是可以。 我们看一下 providers: [ {provide: 'auth',useClass: AuthService} ] providers是一个数组,这个数组呢其实是把你想要注入到其他组件中的服务配置在这里。大家注意到我们这里的写法和上面优点区别,没有直接写成 providers:[AuthService] 而是给出了一个对象,里面有两个属性,provide和useClass,provide定义了这个服务的名称,有需要注入这个服务的就引用这个名称就好。useClass指明这个名称对应的服务是一个类,本例中就是AuthService了。这样定义好之后,我们就可以在任意组件中注入这个依赖了。下面我们改动一下 onstructor(@Inject('auth') private service) { } 我们去掉了service的类型声明,但加了一个修饰符 import { Component,OnInit,Inject } from '@angular/core'; @Component({ selector: 'app-login',styles: [] }) export class LoginComponent implements OnInit { constructor(@Inject('auth') private service) { } ngOnInit() { } onClick(username,password)); } } 双向数据绑定接下来的问题是我们是否只能通过这种方式进行表现层和逻辑之间的数据交换呢?如果我们希望在组件内对数据进行操作后再反馈到界面怎么处理呢?Angular2提供了一个双向数据绑定的机制。这个机制是这样的,在组件中提供成员数据变量,然后在模板中引用这个数据变量。我们来改造一下 username = ""; password = ""; 然后去掉 console.log('auth result is: ' + this.service.loginWithCredentials(this.username,this.password)); 去掉参数的原因是双向绑定后,我们通过数据成员变量就可以知道用户名和密码了,不需要在传递参数了。而成员变量的引用方式是 <div> <input type="text" [(ngModel)]="username" /> <input type="password" [(ngModel)]="password" /> <button (click)="onClick()">Login</button> </div>
//login.component.ts import { Component,template: ` <div> <input type="text" [(ngModel)]="username" /> <input type="password" [(ngModel)]="password" /> <button (click)="onClick()">Login</button> </div> `,styles: [] }) export class LoginComponent implements OnInit { username = ''; password = ''; constructor(@Inject('auth') private service) { } ngOnInit() { } onClick() { console.log('auth result is: ' + this.service.loginWithCredentials(this.username,this.password)); } } 表单数据的验证通常情况下,表单的数据是有一定的规则的,我们需要依照其规则对输入的数据做验证以及反馈验证结果。Angular2中对表单验证有非常完善的支持,我们继续上面的例子,在 <div> <input required type="text" [(ngModel)]="username" #usernameRef="ngModel" /> {{usernameRef.valid}} <input required type="password" [(ngModel)]="password" #passwordRef="ngModel" /> {{passwordRef.valid}} <button (click)="onClick()">Login</button> </div> 注意到我们只是为username和password两个控件加上了required这个属性,表明这两个控件为必填项。通过 我们是知道了验证的状态是什么,但是如果我们想知道验证失败的原因怎么办呢?我们只需要将 <input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />
<div> <input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" /> {{ usernameRef.errors | json }} <div *ngIf="usernameRef.errors?.required">this is required</div> <div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div> <input required type="password" [(ngModel)]="password" #passwordRef="ngModel" /> <div *ngIf="passwordRef.errors?.required">this is required</div> <button (click)="onClick()">Login</button> </div>
如果我们把用户名和密码整个看成一个表单的话,我们应该把它们放在一对 <div> <form #formRef="ngForm"> <input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" /> <div *ngIf="usernameRef.errors?.required">this is required</div> <div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div> <input type="password" [(ngModel)]="password" #passwordRef="ngModel" required /> <div *ngIf="passwordRef.errors?.required">this is required</div> <button (click)="onClick()">Login</button> </form> </div> 这时运行后会发现原本好用的代码出错了,这是由于如果在一个大的表单中,ngModel会注册成Form的一个子控件,注册子控件需要一个name,这要求我们显式的指定对应控件的name,因此我们需要为 <div> <form #formRef="ngForm"> <input type="text" name="username" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" /> <div *ngIf="usernameRef.errors?.required">this is required</div> <div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div> <input type="password" name="password" [(ngModel)]="password" #passwordRef="ngModel" required /> <div *ngIf="passwordRef.errors?.required">this is required</div> <button (click)="onClick()">Login</button> <button type="submit">Submit</button> </form> </div> 既然我们增加了一个 onSubmit(formValue) { console.log(formValue); } 你会发现 <div> <form #formRef="ngForm" (ngSubmit)="onSubmit(formRef.value)"> <fieldset ngModelGroup="login"> <input type="text" name="username" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" /> <div *ngIf="usernameRef.errors?.required">this is required</div> <div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div> <input type="password" name="password" [(ngModel)]="password" #passwordRef="ngModel" required /> <div *ngIf="passwordRef.errors?.required">this is required</div> <button (click)="onClick()">Login</button> <button type="submit">Submit</button> </fieldset> </form> </div>
onSubmit(formValue) { console.log('auth result is: ' + this.service.loginWithCredentials(formValue.login.username,formValue.login.password)); } 在浏览器中试验一下吧,所有功能正常工作。 验证结果的样式自定义如果我们在开发工具中查看网页源码,可以看到 <input name="username" class="ng-pristine ng-invalid ng-touched" required="" type="text" minlength="3" ng-reflect-minlength="3" ng-reflect-name="username"> 类似的可以实验一下,填入一些字符满足验证要求之后,看input的HTML是下面的样子:在验证结果为true时input的样式是 <input name="username" class="ng-touched ng-dirty ng-valid" required="" type="text" ng-reflect-model="ssdsds" minlength="3" ng-reflect-minlength="3" ng-reflect-name="username"> 知道这个后,我们可以自定义不同验证状态下的控件样式。在组件的修饰符中把styles数组改写一下: styles: [` .ng-invalid{ border: 3px solid red; } .ng-valid{ border: 3px solid green; } `] 保存一下,返回浏览器可以看到,验证不通过时 最后说一下,我们看到这样设置完样式后连form和fieldset都一起设置了,这是由于form和fieldset也在样式中应用了 styles: [` input.ng-invalid{ border: 3px solid red; } input.ng-valid{ border: 3px solid green; } `] 很多开发人员不太了解CSS,其实CSS还是比较简单的,我建议先从Selector开始看,Selector的概念弄懂后Angular2的开发CSS就会顺畅很多。具体可见W3School中对于CSS Selctor的参考和https://css-tricks.com/multip...。 本节代码: https://github.com/wpcfan/awe... 进一步的练习
第一节:Angular 2.0 从0到1 (一) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |