? 模板驱动表单:引入FormsModule
模块,表单的控制逻辑都是写在模板里的,每一个表单元素都是和一个负责管理内部表单模型的指令关联起来的。【相关教程推荐:《angular教程》】
import { Component, OnInit } from '@angular/core'; import { NgForm } from '@angular/forms'; @Component({ selector: 'app-module-form', template:` <form #moduleForm="ngForm" (submit)="OnSubmit(moduleForm)"> <label>用户名: <input type="text" name="username" ngModel required minlength="2" maxlength="6" #username="ngModel"><br> <ng-container *ngIf="username.touched&&username.errors"> <div *ngIf="username.errors.required">请输入用户名</div> <div *ngIf="username.errors.minlength">最少2个字符</div> <div *ngIf="username.errors.maxlength">最多6个字符</div> </ng-container> </label> <label> 密 码: <input style="margin-top: 10px;" type="text" name="password" ngModel required minlength="6" pattern="[a-zA-Z0-9]+" #password="ngModel"><br> <ng-container *ngIf="password.touched&&password.errors"> <div *ngIf="password.errors.required">请输入密码</div> <div *ngIf="password.errors.minlength">最少6个字符</div> <div *ngIf="password.errors.pattern">至少包含大小写、数字</div> </ng-container> </label> <button style="margin-top: 20px;" type="submit" [disabled]="!moduleForm.valid">提交</button> </form> `, styleUrls: ['./module-form.component.less'] }) export class ModuleFormComponent implements OnInit { constructor() { } ngOnInit() { } OnSubmit(moduleForm:NgForm){ console.log(moduleForm.value); } }1.2 响应式表单
? 响应式表单:需要引入ReactiveFormsModule
模块,在响应式表单中,视图中的每个表单元素都直接链接到一个表单模型。
- FormControl:是构成表单的基本单位。实例用于追踪
单个表单
控件的值和验证状态 - FormGroup:用于追踪一个
表单控件组
的值和状态。 - FormGroup和FormArray的区别:formgroup既可以代表表单一部分也可以代表整个表单;formarray有一个额外的长度属性,它的formControl是没有相关的key的,只能通过访问formarray中的元素。
<input [formControl]="username"> <!--不带表单的input-->
//原始的定义方法 export class ReactiveRegistComponent implements OnInit { formModel:FormGroup; constructor() { this.formModel=new FormGroup({ username:new FormControl(), mobile:new FormControl(), passwordsGroup: new FormGroup({ password:new FormControl(), pconfirm:new FormControl(), }) }); } } //使用formBuilder后的定义 constructor(fb:FormBuilder) { this.formModel=fb.group({ username:[''], mobile:[''], passwordsGroup: fb.group({ password:[''], pconfirm:[''], }) }); }
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms'; @Component({ selector: 'app-module-form', // templateUrl: './module-form.component.html', template:` <form [formGroup]="form"> <label>用户名: <input type="text" formControlName="username"><br> <ng-container *ngIf="form.controls['username'].errors?.['required']"><div style="color: red;">请输入用户名</div></ng-container> <ng-container *ngIf="form.controls['username'].errors?.['maxlength']"><div style="color: red;">最多只能有6个字符</div></ng-container> </label> <label> 密 码: <input style="margin-top: 10px;" formControlName="password"><br> <ng-container *ngIf="form.controls['password'].errors?.['required']"><div style="color: red;">请输入密码</div></ng-container> </label> </form> `, styleUrls: ['./module-form.component.less'] }) export class ModuleFormComponent implements OnInit { form!: FormGroup; constructor(private fb: FormBuilder) { } ngOnInit() { this.form = this.fb.group({ username:[null,[Validators.required,Validators.maxLength(6)]], password:[null,[Validators.required]], }) } }
- 自定义验证器:
export function whiteSpaceValidator(): ValidatorFn { // 不能全输入空格,验证 return (control: FormControl): { [s: string]: boolean } => { const reg = new RegExp(/\s/g); if (reg.test(control.value)) { return { required: true }; } }; }2 Angular管道
- 管道:把数据作为输入,然后转换它并给出输出。
? Angular的一些内置管道:如date、uppercase、lowercase、currency、percent等,不用引入,直接在任何组件中使用。注意:用date转化的数据不能为字符串,必须为Date类数据。
<!-- curDate = new Date(); --> <!-- 当前时间日期为Apr 20, 2022 --> <p>当前时间日期为{{curDate|date}}</p> <!-- 管道参数化:当前时间日期为2022/04/20 --> <p>当前时间日期为{{curDate|date:"yyyy/MM/dd"}}</p> <!-- title = "Hello World!"; --> <!-- 转大写HELLO WORLD! --> <p>转大写{{title|uppercase}}</p> <!-- 转小写hello world! --> <p>转小写{{title|lowercase}}</p> <!-- 转换金额字符串2.2 链式管道.26 --> <p>转换金额字符串{{0.259|currency}}</p> <!-- 转换金额百分比26% --> <p>转换金额百分比{{0.259|percent}}</p>链式管道
?
<!-- 当前时间日期为wednesday, april 20, 2022 --> <p>当前时间日期为{{curDate|date:'fullDate'|lowercase}}</p>:管道以多个条件指定格式输出。2.3 自定义管道自定义管道
?
<!-- 600000毫秒真实时间:00天00时10分00秒 --> <p>600000毫秒真实时间:{{600000|TimeFormater}}</p>:在app.module文件的declarations中进行声明就可以在任何一个组件中使用了。
import { PipeTransform, Pipe } from '@angular/core'; @Pipe({ name: 'TimeFormater', }) export class TimeFormaterPipe implements PipeTransform { // // 传入的是一个一毫秒为单位的时间数 transform(value) { if (!value || value <= 0) { return '00天00时00分00秒'; } const time = Math.abs(value); const transecond = Math.round(time / 1000); // 转化为秒 const day = Math.floor(transecond / 3600 / 24); // 获取天数 const hour = Math.floor((transecond / 3600) % 24); // 获取小时,取余代表不满一天那部分 const minute = Math.floor((transecond / 60) % 60); // 获取分,取余代表不满小时那部分 const second = transecond % 60; return `${this.formatTime(day)}天${this.formatTime(hour)}时${this.formatTime(minute)}分${this.formatTime(second)}秒`; } formatTime(t) { return t < 10 ? '0' + t : '' + t; } }3 Angular的三大绑定3.1属性绑定属性绑定
?
<!--属性绑定图片路径,动态获取--> <img [src]="imgUrl">的属性指的是元素、组件、指令的属性。属性的绑定是单向绑定,从组件的属性流动到目标元素的属性。3.2 attribute、class和style绑定attribute绑定
?特性:并非所有属性都有可供属性绑定。是HTML标签上的attr.特性名
,它的值只能够是字符串。通过Property(属性,DOM中的属性,是JavaScript里的对象)
绑定。而比如标签中的id、src等这些属于
<table [border]="1"> <tr><td id="attr" [attr.colspan]="1+1">One-Two</td></tr> <tr><td>Five</td><td>Six</td></tr> </table>,这些可以直接绑定就可。而attribute绑定如下:class的绑定
?
.test1{ width: 100px; height: 100px; border: 1px solid; } .test2{ width: 100px; height: 100px; background-color: yellowgreen; }:静态绑定、单一属性动态绑定方式、多属性动态绑定方式、ngCLass指令绑定。
<div class="test1"></div> <div class="test1 test2"></div> <!--多个样式会重叠-->
(1)静态绑定:可以是一个,也可以是多个,多个的class就会融合起来。
<div [class]="'test1'"></div> <div [class]="'test1 test2'"></div>
class.样式的class名
(2)单一属性动态绑定方式:取在css文件中定义好的样式进行绑定,
<div [class.test1]="true"></div> <div [class.test2]="true"></div>。
<div [class]="moreClass"></div> <div [ngClass]="moreClass"></div>
(3)多属性动态绑定方式:class的绑定
moreClass:object = { 'test1':true, 'test2':true }style的绑定
?
<div [style.width]="'200px'"></div>:单一样式绑定、带单位的单一样式绑定、多个样式绑定。 (1)单一样式的绑定
<div [style.width.px]="'200'"></div>
(2)带单位的单一样式绑定
`<div [style]="moreStyle">绑定多个形式的style</p>`
(3)多个样式绑定
moreStyle:string = 'width: 100px;height: 200px';3.3 事件绑定
()
?事件绑定:带
<button (click)="save()"></button>的特性就是事件绑定。括号内代表的是目标事件。而下面例子的事件绑定就是点击事件的绑定。
//html <input [value]="currentUser.name" (input)="getValue($event)"> //js currentUser={ 'name':'' } getValue(ev:any){ this.currentUser.name = ev.target.value; console.log(this.currentUser.name); }
(1)目标事件是原生DOM元素事件,input是DOM元素input的原生事件。
ngClick(2)目标事件是指令:比如ngDblclick、(子组件的EventEmitter实例变量)="父组件的方法(子组件数据)"等。
(3)目标事件是自定义事件。目标事件 4 Angular的三大指令 下文的父子组件通信已经有详解?。
4.1 属性型指令属性型指令?
- ngStyle指令:可以同时设置多个内联样式。
- ngModel指令:可以双向绑定到HTML表单中的元素。
- 创建属性型指令:在app.module文件中的declarations数组中进行声明就可以在任何组件的标签元素中调用,如下:
<p appHighlight>看一下指令</p>
// Directive:在declarations import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(el:ElementRef) { el.nativeElement.style.backgroundColor = 'yellow'; } }
import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor(private el:ElementRef) { } // 监听鼠标悬浮背景颜色设置 @HostListener('mouseenter') onMouseEnter(){ this.hightlight('yellow'); } // 监听鼠标离开背景颜色设置 @HostListener('mouseleave') onMouseLeave(){ this.hightlight(''); } private hightlight(color:string){ this.el.nativeElement.style.backgroundColor = color; } }4.2 结构型指令结构型指令
? 属性型指令:该指令通过添加或移除DOM元素来改变DOM布局。每一个宿主元素都只能有一个结构型指令。比如ngif和ngfor不能在同一元素出现。如ngif、ngfor、ngSwitch(本身是ngSwitchCase和ngSwitchDefault,它控制了两个结构型指令*符号
),结构型指令一般都是带**
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[appTagdisplay]' }) export class TagdisplayDirective { private hasView = false; // ViewContainerRef访问视图容器 constructor(private templateRef:TemplateRef<any>,private viewContainer:ViewContainerRef) { } @Input() set appTagdisplay(condition:boolean){ if(!condition&&!this.hasView){ // 视图容器创建一个内嵌的视图 this.viewContainer.createEmbeddedView(this.templateRef); this.hasView = true; }else if(condition&&this.hasView){ // 清除该容器,销毁该视图 this.viewContainer.clear(); this.hasView = false; } } }
<div> <h1>自定义结构指令</h1> <p *appTagdisplay="false"> 该段显示,因为appTagdisplay为false </p> <p *appTagdisplay="true"> 该段不显示,因为appTagdisplay为true </p> </div>4.3 组件组件
? 5 Angular的五大组件通信:也是一种指令,该指令拥有模板。
5.1 Input与Output实现父子组件通信Input和Output的 *** 作都在子组件中
通过下面的例子我们会发现父组件。父传子:在父组件中动态绑定属性,在子组件中Input获取父组件传来的属性。子传父:子组件创建一个实例化EventEmitter对象,EventEmitter 的核心就是事件触发与事件监听器功能的封装;父组件:通过事件绑定调用带参自定义函数接受子组件传来的数据(自定义函数的参数)。
?
<div class="chan"> <div class="father"> <ng-container *ngIf="!isClear"> <div *ngFor="let item of currentData"> <span>{{item}}</span> </div> </ng-container> <div class="footer"> <input style="width:178px" [(ngModel)]="fatherData"/> <button (click)="send()">发送</button> <button (click)="isClear=true">清空记录</button> </div> </div> <app-son [dataSource]="currentData" (sonSend)="getSonInfo($event)"></app-son> </div>:双向绑定fatherData也就是当前输入框输入的信息,点击发送事件触发传给子组件的currentData添加数据并清空当前输入框的信息。
import { Component, OnInit } from '@angular/core'; export class FatherComponent implements OnInit { isClear:boolean=false; fatherData:any; currentData:Array<any>=[]; constructor() { } ngOnInit() { this.fatherData = this.currentData; } send(){ this.currentData.push("我是父组件的数据:"+this.fatherData); this.fatherData=''; } getSonInfo(event:any){ this.currentData.push("我是子组件的数据:"+event); } }子组件
?
<div class="son"> <ng-container *ngIf="!isClear"> <div *ngFor="let item of currentData"> <span>{{item}}</span> </div> </ng-container> <div class="footer"> <input style="width:178px" [(ngModel)]="sonData"/> <button (click)="send()">发送</button> <button (click)="isClear=true">清空记录</button> </div> </div>:输入框输入sonData,点击发送事件触发子组件事件发射数据,然后父组件就可以通过子组件绑定的事件发射从父组件通过事件方法获取当前子组件发送的数据。
import { OnInit } from '@angular/core'; export class SonComponent implements OnInit{ @Input() dataSource:any=0; @Output() sonSend = new EventEmitter<any>(); isClear:boolean=false; sonData:any; currentData:Array<any>=[]; constructor() { } ngOnInit(): void { this.currentData = this.dataSource; } send(){ this.sonSend.emit(this.sonData); // this.currentData.push("我是子组件的数据:"+this.sonData); this.sonData=''; } }5.2 通过本地变量实现父子组件的通信
<div>我是父组件</div> <div>子组件:{{sonTpl.myName}}</div> <app-son #sonTpl></app-son>
在父组件的模板中创建一个代表子组件的本地变量,通过调用这个变量就可以调用子组件中的属性和方法。
5.3 通过@ViewChild实现父子组件的通信@ViewChild(SonComponent) childTpl: any;
父组件的js中定义
<div>我是父组件</div> <button (click)="childTpl.getName()">子组件按钮</button> <app-son></app-son>,注意在html必须要调用子组件元素,不然会直接报错,且不能直接调用childTpl.myName获取子组件中的变量。
// 子组件中的getName getName(){ console.log('我是子组件的getName方法'); }5.4 通过共享服务Service实现非父子组件通信
书籍《Angular企业级应用开发实战》
栗子来自Service服务-p143:
? providers
:主要控制准备开始、和确认按钮的动作进行消息的传递。注意这个服务的定义一定是共享的,不要在各个组件下独自注入
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class MissionService { // 源 private missionAnnounceSource = new Subject<string>(); private misssionConfirmedSource = new Subject<string>(); // 流 // asObservable:可观察的序列,隐藏源序列的身份。 missionAnnounce$ = this.missionAnnounceSource.asObservable(); missionConfirm$ = this.misssionConfirmedSource.asObservable(); constructor() {} announceMission(mission:string){ this.missionAnnounceSource.next(mission); } confirmMission(astronaut:string){ this.misssionConfirmedSource.next(astronaut); } }中,因为单独引入service只是在当前组件有效,每个组件调用一次都是独立的,互不影响,这就不是组件通信需要的。MissioncontrolComponent
? astronaut组件:这是一个主要界面的组件,在界面中调用了
import { Component, OnInit } from '@angular/core'; import { MissionService } from 'src/app/service/mission.service'; @Component({ selector: 'app-missioncontrol', template: `<h2>导d控制器</h2> <button (click)="announce()">准备开始</button> <app-astronaut *ngFor="let item of astronauts" [astronaut]="item"> </app-astronaut> <h3>日志</h3> <ul> <li *ngFor="let event of history">{{event}}</li> </ul> `, }) export class MissioncontrolComponent implements OnInit { astronauts = [' *** 作员1',' *** 作员2',' *** 作员3']; history:string[] = []; missions = ['发射导d']; nextMession = 0; constructor(private misssionSvc:MissionService) { // 获取子组件保存的信息,获取是哪一个 *** 作员点击确认了 misssionSvc.missionConfirm$.subscribe((astronaut)=>{ this.history.push(`${astronaut}已经确认`); }) } announce(){ let mission = this.missions[this.nextMession++]; this.misssionSvc.announceMission(mission); this.history.push(`任务"${mission}"进入准备`); if(this.nextMession>=this.missions.length){ this.nextMession = 0; } } ngOnInit(): void { } }。当前组件就是父组件,而astronaut组件就是一个子组件。AstronautComponent
?
import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; import { MissionService } from 'src/app/service/mission.service'; @Component({ selector: 'app-astronaut', template: ` <p> {{astronaut}}:<strong>{{mission}}</strong><button (click)="confirm()" [disabled]="!announced||confirmed">确认</button> </p> `, }) export class AstronautComponent implements OnInit,OnDestroy{ @Input() astronaut:string=''; mission = '<没有任务>'; confirmed = false; announced = false; subscription:Subscription; constructor(private missionSvc:MissionService) { // 获取父组件的数据 this.subscription = missionSvc.missionAnnounce$.subscribe((mission)=>{ this.mission = mission;// 发射导d this.announced = true;// 激活确认按钮 this.confirmed = false; }) } confirm(){ // 禁用按钮 this.confirmed = true; // 点击确认,保存当前的 *** 作员数据 this.missionSvc.confirmMission(this.astronaut); } ngOnDestroy(): void { // 防止内存泄漏 this.subscription.unsubscribe(); } ngOnInit() { } }:点击确认,向父组件传递确认的 *** 作员信息。5.5 路由传值
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'app-father', template:`<button (click)="linkSon()">按钮跳到儿子组件中</button>` }) export class FatherComponent implements OnInit { obj:any=[ '书悟空', '唐僧', ]; constructor(private router:Router) {} ngOnInit() {} linkSon(){ this.router.navigate(['/son',{ name:this.obj,id:10010}]); //http://localhost:4209/son;name=书悟空,唐僧;id=10010 } }
? 按钮点击跳转:路由传参数由分号隔开。
// private route:ActivatedRoute this.route.params.subscribe((route)=>{ console.log(route); })
<a [routerLink]="['/son']" [queryParams]="{id:10010,name:this.obj}">链接跳到儿子组件中</a> // http://localhost:4209/son?id=10010&name=书悟空&name=唐僧
? 链接点击跳转:路由传参通过queryParams属性控制,由?、&符号分隔开。
// private route:ActivatedRoute this.route.queryParams.subscribe((route)=>{ console.log(route); })
{ path: 'son/:id', component: SonComponent },
? 链接点击跳转:直接是用/分割路由传参。
<a [routerLink]="['/son',10010]">链接跳到儿子组件中</a> // http://localhost:4209/son/10010
// private route:ActivatedRoute this.route.params.subscribe((route)=>{ console.log(route); })6 Angular的八大生命周期
还有其他通信方式:浏览器本地传值(localStorge、SessionStorge)、cookie
6.1 ngOnChanges()组件(或指令)
当angular检测到发生在
重新设置数据绑定输入属性时响应。在被绑定的输入属性的值发生变化时调用,首次调用一定会ngOnInit()之前
OnChanges(),每一次改变绑定数据就调用一次这个钩子。ngOnChanges() 对应的函数 当前值currentValue
,该函数获取了一个对象,对象把每个发生变化的属性名映射在SimpleChange对象中,对象有属性前一个值previousValue
和父组件。
?
<!--父组件html--> <div> <span>我是父组件:</span> <input [(ngModel)]="dataSource"/> <span style="margin-left: 20px;">{{dataSource}}</span> </div> <div style="height: 20px;"></div> <app-two [data]="dataSource"></app-two>:子组件
?
<!--子组件TwoComponent html--> <div> <span>我是子组件:</span> <button (click)="increment()">+</button> <span style="margin: 0 20px;">{{data}}</span> <button (click)="decrement()">-</button> </div>:
import { Component, Input, OnInit, SimpleChanges, OnChanges } from '@angular/core'; // 子组件js export class TwoComponent implements OnInit,OnChanges { @Input() data:any=0; constructor() { } ngOnInit(): void { console.log('ngOnInit',this.data); } increment(){ this.data++; } decrement(){ this.data--; } ngOnChanges(changes: SimpleChanges): void { console.log("previousValue:",changes['data'].previousValue); console.log("currentValue:",changes['data'].currentValue); } }注意地,在子组件中 *** 作是不能触发Onchanges钩子函数地,它是控制组件上属性的改变而触发
6.2 ngOnInit()。
ngOnChanges()完成之后在Angular第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件,在第一轮只调用一次
调用,6.3 ngDoCheck()。
ngOnChanges
检测变化,在每一个Angular变更检测周期(变化监测)中调用,在执行ngOnInit()
和
<div> <input [(ngModel)]="dataSource"/> <span style="margin-left: 20px;">{{dataSource}}</span> </div>方法之后调用。不管是数据绑定还是鼠标的点击事件,只要触发了都会触发这个钩子的调用。
import { DoCheck,OnInit } from '@angular/core'; export class SonComponent implements OnInit,DoCheck { dataSource:any; constructor() { } ngOnInit(): void { console.log("ngOnInit!"); } ngDoCheck(): void { console.log("DoCheck:",this.dataSource); } }6.4 ngAfterContentInit()
只调用一次
把内容投影进组件之后调用,在第一次执行ngDoCheck方法之后调用,
import { AfterContentChecked, AfterContentInit,DoCheck, Input, OnInit } from '@angular/core'; export class SonComponent implements OnInit,DoCheck,AfterContentInit{ @Input() data:any=0; dataSource:any; constructor() { } ngOnInit(): void { console.log("ngOnInit!"); } ngAfterContentInit(): void { console.log("ngAfterContentInit!",this.dataSource); } ngDoCheck(): void { console.log("DoCheck:",this.dataSource); } }。6.5 ngAfterContentChecked()
ngAfterContentInit()
在每次完成被投影组件内容的变更检测之后调用。在执行ngDoCheck()
和
import { AfterContentChecked, AfterContentInit, DoCheck, Input, OnInit } from '@angular/core'; export class SonComponent implements OnInit,DoCheck,AfterContentInit,AfterContentChecked{ @Input() data:any=0; dataSource:any; constructor() { } ngOnInit(): void { console.log("ngOnInit!"); } ngAfterContentInit(): void { console.log("ngAfterContentInit!",this.dataSource); } ngAfterContentChecked(): void { console.log("ngAfterContentChecked!",this.dataSource); } ngDoCheck(): void { console.log("DoCheck:",this.dataSource); } }方法之后调用。6.6 ngAfterViewInit()
只调用一次
在初始化完组件视图及其子视图之后调用,在第一次执行ngAfterContentChecked()方法之后调用,
export class SonComponent implements OnInit,DoCheck,AfterContentInit,AfterContentChecked,AfterViewInit{ dataSource:any; constructor() { } ngOnInit(): void { console.log("ngOnInit!"); } ngAfterContentInit(): void { console.log("ngAfterContentInit!",this.dataSource); } ngAfterContentChecked(): void { console.log("ngAfterContentChecked!",this.dataSource); } ngAfterViewInit(): void { console.log("ngAfterViewInit!"); } ngDoCheck(): void { console.log("DoCheck:",this.dataSource); } }。6.7 ngAfterViewChecked()
ngAfterViewInit()
在每次完成组件视图和子视图的变更检测之后调用。在执行ngAfterContentChecked()
和切换组件或从一个组件跳转到另一个组件
方法之后调用。
在Angular每次销毁指令/组件之前调用并清扫,在这里反订阅可观察对象和分离事件处理器,以防内存泄漏。什么时候会调用这个生命周期呢?也就是平时我们的时候,这时候就会触发组件的销毁。
更多编程相关知识,请访问:编程视频!!
以上就是Angular知识点分享:聊聊表单、管道、绑定、指令、通信和周期的详细内容,
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)