angular优点

1.1 Angular最大程度减少了页面上的DOM操作;
1.2 让JavaScript中专注业务逻辑的代码
1.3 通过自定义指令实现组件化编程
1.4 代码结构更合理
1.5 维护成本更低

1. 组件

1.1 HTML模板
1.2 typescript定义行为
1.3 css组,可以引入多个css文件,因此可以在一个组件里面定义多个css文件。

  1. //从angular主模块中引入Component(组件装饰器或组件注解)
  2. import { Component } from '@angular/core';
  3. //装饰器中以json的形式声明元数据
  4. @Component({
  5. //它指定了一个叫 <app-root> 的元素。 该元素是 index.html 文件里的一个占位符
  6. //为什么这个组件跟入口index建立了联系呢?因为入口main.ts中绑定了主模块为appModule
  7. selector: 'app-root', //在模板中找对应的标签,找到后创建并插入该组件实例
  8. templateUrl: './app.component.html', // html模板
  9. styleUrls: ['./app.component.css'], // css样式,可以引入多个css文件
  10. // 这个属性(内联模板)和templateUrl(外联模板)二选一,template后面可以直接跟html字符串
  11. // 注意在模板语法(反引号)中是使用插值表达式,不能使用${}插入值
  12. template: `<h1>{{title}}</h1>`
  13. })
  14. //组件控制器,写逻辑代码的地方
  15. export class AppComponent {
  16. title = 'myAngular';
  17. //构造函数可以用来进行属性的声明和初始化语句
  18. //在angular里面有个特别重要的点要记住:只能通过构造函数注入依赖
  19. constructor() {}
  20. }

2. 指令和渲染

2.1 渲染文本用双花括号 {{}} // 和vue一样
2.2 属性绑定用方括号 [] // 将属性或属性绑定到组件类中的值
2.3 绑定事件用单括号 ()
2.4 ngIf和ngFor
2.5 [()] 双向绑定

3. 依赖注入

@Injectable();

4. 生命周期

  1. @Directive()
  2. export class PeekComponent implements OnInit, OnChanges, DoCheck,
  3. AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy{
  4. name: string = '';
  5. constructor() {
  6. this.string = '';
  7. console.log('00构造函数执行了---除了使用简单的值对局部变量进行初始化之外,什么都不应该做')
  8. }
  9. ngOnChanges() {
  10. console.log('01ngOnChages执行了---当被绑定的输入属性的值发生变化时调用(父子组件传值的时候会触发)');
  11. }
  12. ngOnInit() {
  13. console.log('02ngOnInit执行了--- 请求数据一般放在这个里面');
  14. }
  15. ngDoCheck() {
  16. console.log('03ngDoCheck执行了---检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应');
  17. }
  18. ngAfterContentInit() {
  19. console.log('04ngAfterContentInit执行了---当把内容投影进组件之后调用');
  20. }
  21. ngAfterContentChecked() {
  22. console.log('05ngAfterContentChecked执行了---每次完成被投影组件内容的变更检测之后调用');
  23. }
  24. ngAfterViewInit() : void {
  25. console.log('06 ngAfterViewInit执行了----初始化完组件视图及其子视图之后调用(dom操作放在这个里面)');
  26. }
  27. ngAfterViewChecked() {
  28. console.log('07ngAfterViewChecked执行了----每次做完组件视图和子视图的变更检测之后调用');
  29. }
  30. ngOnDestroy() {
  31. console.log('08ngOnDestroy执行了····');
  32. }
  33. //自定义方法
  34. changeMsg() {
  35. this.msg = "数据改变了";
  36. }
  37. }

4.1 生命周期执行顺序

  1. ngOnChanges - 当数据绑定输入属性的值发生变化时调用(父子传值时会触发)
  2. ngOnInit - 在第一次 ngOnChanges 后调用, 初始化只调用一次,请求数据
  3. ngDoCheck - 自定义的方法,用于检测和处理值的改变
  4. ngAfterContentInit - 在组件内容初始化之后调用。只调用一次
  5. ngAfterContentChecked - 组件每次检查内容时调用
  6. ngAfterViewInit - 组件相应的视图初始化之后调用
  7. ngAfterViewChecked - 组件每次检查视图时调用。每次做完组件视图和子视图的变更检测之后调用。
  8. ngOnDestroy - 指令销毁前调用。在这儿反订阅可观察对象和分离事件处理器,以防内存泄 漏。在 Angular 销毁指令/组件之前调用。比如:移除事件监听、清除定时器、退订 Observable 等。
  9. 初始化调用constructor,ngOnChanges,ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked,ngAfterViewInit,ngAfterViewChecked
  10. 卸载 ngOnDestroy 3-5-7

image.png

5. 组件传值

5.1 装饰器
5.1.1 1). @Input(); 2). @Output(); 是Angular2专门用来实现跨组件通讯,双向绑定等操作的。
@Input 父组件向子组件 ,@Output 子组件向父组件,并且需要EventEmitter,变量不能加‘on’,可以使用。

  1. // tslint:disable-next-line:no-output-on-prefix
  2. @Output() onRemoveElement = new EventEmitter<Element>();

1.
Angular学习 - 图2

  1. child_component.ts内有students,并且是被@Input标记的,那么这个属性就作为输入属性
  2. 在parent_component.html内直接使用了students,那是因为在parent.module.ts内将child组件import进来了
  3. [students]这种形式叫属性绑定,绑定的值为school.schoolStudents属性
  4. Angular会把schoolStudents的值赋值给students,然后影响到子组件的显示。

所以我们可以总结,child_component中有数据要显示,但是这个数据的来源是通过parent_component.html中通过属性绑定的形式作为child组件的输入,要想child组件内的students属性能够成功赋值,那么必须使用@Input。
可以使用ngChanges监听@Input的变化。

2.
Angular学习 - 图3
child组件内有一个Output customClick的事件,事件的数据类型是number
child组件内有一个onClicked方法,这个是应用在html中button控件的click事件中,通过(click)=”onClicked()”进行方法绑定
parent组件内有一个public的属性showMsg,Angular的ts类默认不写关键字就是public。
parent组件内有一个onCustomClicked方法,这个也是要用在html中的,是和child组件内的output标记的customClick事件进行绑定的
步骤为child的html的button按钮被点击->onClicked方法被调用->emit(99)触发customClick->Angular通过Output数据流识别出发生变化并通知parent的html中(customClick)->onCustomClicked(event)被调用,event)被调用,event为数据99->改变了showMsg属性值->影响到了parent的html中的显示由1变为99。

  1. // 子组件
  2. import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
  3. @Component({
  4. selector: 'selector-aaa',
  5. templateUrl: './aaa.component.html'
  6. })
  7. export class AaaComponent implements OnInit {
  8. @Input() mes: any;
  9. @Output() childClick = new EventEmitter<any>();
  10. child = '发给父组件的值'
  11. constructor() { }
  12. ngOnInit() { }
  13. aaa() {
  14. this.childClick.emit(this.child);
  15. }
  16. }
  17. // ---html
  18. <div style="width: 40px;height: 40px;" (click)="aaa()">我是子组件--{{ mes }}</div>
  1. // 关联页面
  2. @NgModule({
  3. imports: [
  4. SharedModule,
  5. DragDropModule,
  6. RouterModule.forChild(ROUTES)
  7. ],
  8. exports: [RouterModule],
  9. entryComponents: COMPONENTS,
  10. declarations: [COMPONENTS],
  11. schemas: [CUSTOM_ELEMENTS_SCHEMA],
  12. providers: [...SERVICES]
  13. })
  1. // 父组件
  2. prventMes = '父组件发的';
  3. parentClick(e) {
  4. console.log(e);
  5. }
  6. // html
  7. <selector-aaa [mes]="prventMes" (childClick)="parentClick($event)"></selector-aaa>

5.2 组件通过服务来通讯(发布订阅模式);

  1. // 1.创建一个service服务
  2. import { Observable, Subject } from 'rxjs';
  3. export class MessService {
  4. private subject = new Subject<any>();
  5. send(mes: any) {
  6. this.subject.next(mes);
  7. }
  8. get(): Observable<any> {
  9. return this.subject.asObservable();
  10. }
  11. }
  1. // 2.关联service
  2. @NgModule({
  3. imports: [
  4. SharedModule,
  5. DragDropModule,
  6. RouterModule.forChild(ROUTES)
  7. ],
  8. exports: [RouterModule],
  9. entryComponents: COMPONENTS,
  10. declarations: [COMPONENTS],
  11. schemas: [CUSTOM_ELEMENTS_SCHEMA],
  12. providers: [...SERVICES]
  13. })
  1. // 3. 发布组件
  2. import { MessService } from './../../../states';
  3. name = '发布啦';
  4. constructor(
  5. private Util: UtilService,
  6. private Dialog: DialogService,
  7. private fb: FormBuilder,
  8. // private service: TempHumiSettingStatusService
  9. private sev: MessService,
  10. ) { }
  11. add() {
  12. this.sev.send(this.name);
  13. });
  1. // 4.接受组件
  2. import { MessService } from './../../../states';
  3. message = '';
  4. constructor(
  5. private dialogService: DialogService,
  6. private fb: FormBuilder,
  7. private util: UtilService,
  8. private srv: MessService
  9. ) { }
  10. ngOnInit() {
  11. this.srv.get().subscribe((result) => {
  12. console.log('111111111111111111');
  13. this.message = result;
  14. console.log(this.message);
  15. });
  16. }

5.3 本地缓存
5.4 路由传参

6. 动态组件

添加组件之前,先定义一个锚点 告诉Angular把组件插到什么地方。

组件的模板不会永远是固定的。应用可能会需要在运行期间按需加载一些新的组件。 通过下面的例子可以了解动态组件的基本使用。

7. 模板

7.1 插值 {{ }}
7.2 别名 #

8. 装饰器

8.1 简介
8.1.1 装饰器就是一个函数;
8.1.2 它是一个返回函数的函数;
8.1.3 它不是Angular的特性,它是ts的特性;
8.1.4 函数的入参分别为 target,name和descriptor;
8.1.5 执行该函数后,可能返回description对象,用于配置target对象。
8.2 TypeSctipt装饰器的分类;
8.2.1 类装饰器(Class decorators);
8.2.2 方法装饰器(Method decorators);
8.2.3 属性装饰器(Property decorators);
8.2.4 参数装饰器(Paramter decorators);
8.2.5 访问器装饰器();
8.3 实现一个@Emoji()的装饰器,能够让传入的字符串两边加上标签符合。

  1. export function Emoji() {
  2. return (target: Object, key: string) {
  3. let val = target[key];
  4. const getter = () => {
  5. return this.val;
  6. };
  7. const setter = () => {
  8. val = `表情${value}表情`
  9. };
  10. Object.defineProperty(target, key, {
  11. get: getter,
  12. set: setter,
  13. enumerable: true,
  14. configurable: true
  15. })
  16. }
  17. }
  18. ## 如何使用
  19. + @Emoji() Result = 'hello';
  20. + {{ result }} 页面显示为:表情hello表情

8.4 实现一个@Confirmable()装饰器,弹窗确认对话框;

  1. export function Confirmable(message: string) {
  2. return (target: Object, key: string, descriptor: PropertyDescriptor) {
  3. const origin = descriptor.value;
  4. descriptor.value = function(...args:any) {
  5. const allow = window.confirm(message);
  6. if (allow) {
  7. const result = origin.apply(this, args);
  8. return result;
  9. }
  10. return null;
  11. };
  12. return descriptor;
  13. }
  14. }
  15. ## 在方法前加上这个装饰器
  16. @Confirmable('确定要点击吗')
  17. handlerClick() {
  18. console.log('click');
  19. }
  20. ## 效果
  21. 触发handlerClick前会出现一个弹窗,点击确认后才会继续输出click

8.4 Angular中的装饰器的种类,19个内置装饰器。

装饰器类型 内置装饰器
类装饰器 5个 @Component、@NgModule、@Pipe、@Injectabl、@Directive
属性装饰器 6个 @Input、@Output、@ContentChild、@ContentChildren、@ViewChild、@ViewChildren
方法装饰器 2个 @HostListener、@HostBinding
参数装饰器 6个 @Attribute、@Inject、@Optional、@Self、@SkipSelf、@Host

1. 使用 [@Directive] 自定义指令

1.1 创建属性指令

  1. import { Directive, HostListener, ElementRef, Renderer2, HostBinding } from '@angular/core';
  2. @Directive({
  3. selector: '[appBackgroundExe]'
  4. })
  5. export class BackgroundExeDirective {
  6. @Input('appBackgroundExe') highLightColor: string;
  7. constructor(private elementRef: ElementRef, private renderer: Renderer2) {
  8. // 这种写法比较丑陋
  9. // this.elementRef.nativeElement.style.background = 'yellow';
  10. // 推荐这种写法, Renderer
  11. this.renderer.setStyle(this.elementRef.nativeElement, 'background', 'yellow');
  12. }
  13. @HostBinding('class.pressed') isPressed: boolean;
  14. @HostListener('mouseenter')
  15. onMouseEnter(): void {
  16. this.highLight(this.highLightColor);
  17. }
  18. @HostListener('mouseleave')
  19. onMouseLeave(): void {
  20. this.highLight(null);
  21. }
  22. @HostListener('mousedown')
  23. onMouseDown(): void {
  24. this.isPressed = true;
  25. }
  26. @HostListener('mouseup')
  27. onMouseUp(): void {
  28. this.isPressed = false;
  29. }
  30. private highLight(color: string): void {
  31. // this.elementRef.nativeElement.style.background = color;
  32. this.renderer.setStyle(this.elementRef.nativeElement, 'background', color);
  33. }
  34. }
  35. 其中,selector: '[appBackgroundExe]' 是指令关联的属性名称,
  36. 以便 Angular 在编译时,能从模板中找到与此指令关联的 HTML 代码。
  37. 构造函数中,注入了 ElementRef Renderer2 模块的实例。
  38. 通过 ElementRef 我们可以引用指令标识的 DOM 元素,并对其进行相关的操作;
  39. 并且可以利用 Renderer2 提供的 API 对元素进行相关的渲染操作。
  40. @HostListener @HostBinding 是属性装饰器。
  41. @HostListener 是用来为宿主元素添加事件监听;而指令标记的元素,就是宿主元素。
  42. @HostBinding 是用来动态设置宿主元素的属性值。
  1. 在模板中使用
  2. .pressed {
  3. font-size: 30px;
  4. }
  5. <div class="panel panel-primary">
  6. <div [appBackgroundExe]="'red'">鼠标移进,元素变成红色。鼠标移出,元素红色消失</div>
  7. </div>

1.2 创建结构型指令

  1. import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
  2. @Directive({
  3. selector: '[appIf]'
  4. })
  5. export class IfDirective {
  6. constructor(
  7. private templateRef: TemplateRef<any>,
  8. private viewContainerRef: ViewContainerRef
  9. ) { }
  10. @Input('ifCreat')
  11. set condition(condition: boolean) {
  12. if (condition) {
  13. this.viewContainerRef.createEmbeddedView(this.templateRef);
  14. } else {
  15. this.viewContainerRef.clear();
  16. }
  17. }
  18. }
  1. <div class="panel panel-primary">
  2. <div *ifCreate="'true'">hello</div>
  3. </div>

  1. import { Directive, Type, ViewContainerRef, Input, ComponentFactoryResolver, AfterViewInit } from '@angular/core';
  2. @Directive({
  3. selector: '[add-comp]',
  4. })
  5. export class AddCompDirective implements AfterViewInit {
  6. @Input() comp: Type<any>;
  7. constructor(public viewContainerRef: ViewContainerRef, public componentFactoryResolver: ComponentFactoryResolver) {
  8. }
  9. ngAfterViewInit(){
  10. console.log('comp', this.comp);
  11. if (this.comp){
  12. const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.comp);
  13. this.viewContainerRef.clear();
  14. this.viewContainerRef.createComponent(componentFactory);
  15. }
  16. }
  17. }
  18. <ng-container add-comp [comp]="tab?.Comp"></ng-container>

9. Angular操作DOM

9.1 @ViewChild();
在探索 DOM 抽象类前,先了解下如何在组件/指令中获取这些抽象类。Angular 提供了一种叫做 DOM Query 的技术,主要来源于 @ViewChild@ViewChildren 装饰器(decorators)。两者基本功能相同,唯一区别是 @ViewChild 返回单个引用,@ViewChildren 返回由 QueryList 对象包装好的多个引用。
1 ,一般@ViewChild和ViewChildren 与 模板引用变量(template reference variable)一起使用。
2 ,模板引用变量仅仅是对模板(template)内DOM元素的命名式引用(a named reference),类似于HTML中的 id 属性。
3 ,你可以使用模板引用来标记一个DOM元素,并在组件/指令中使用@ViewChild装饰器查询(query)它。

  1. @Component({
  2. selector: 'sample',
  3. template: `
  4. <span #tref>I am span</span>
  5. `
  6. })
  7. export class SampleComponent implements AfterViewInit {
  8. @ViewChild("tref", {read: ElementRef}) tref: ElementRef;
  9. constructor(
  10. private el:ElementRef,
  11. private renderer2: Renderer2
  12. ){}
  13. ngAfterViewInit(): void {
  14. // outputs `I am span`
  15. console.log(this.tref.nativeElement.textContent);
  16. }
  17. /** 通过 ElementRef 我们就可以封装不同平台下视图层中的 native 元素
  18. (在浏览器环境中,native 元素通常是指 DOM 元素),
  19. 最后借助于 Angular 2 提供的强大的依赖注入特性,我们就可以轻松地访问到 native 元素。*/
  20. Oninit() {
  21. console.log(this.el.nativeElement);
  22. // 获取元素DOM后nativeElement下可以操作原生属性
  23. this.el.nativeElement.querySelector('.btn1').style.height = '300px';
  24. this.renderer2.setStyle(this.el.nativeElement.querySelector('.btn1'),'background','green')
  25. }
  26. }

@ViewChild([reference from template], {read: [reference type]}) 模板引用名称;

第二个参数 read 是可选的,因为 Angular 会根据 DOM 元素的类型推断出该引用类型。例如,如果它(#tref)挂载的是类似 span 的简单 html 元素,Angular 返回 ElementRef;如果它挂载的是 template 元素,Angular 返回 TemplateRef。一些引用类型如 ViewContainerRef 就不可以被 Angular 推断出来,所以必须在 read 参数中显式申明。其他的如 ViewRef 不可以挂载在 DOM 元素中,所以必须手动在构造函数中编码构造出来

9.2 ElementRef
最基本的抽象类,只包含所挂载的元素对象,对访问原生元素很有用。(Angular不推荐)

  1. console.log(this.tref.nativeElement.textContent);

9.2.1 使用@ViewChild()装饰器的DOM元素会返回ElementRef, 但是由于所有组件挂载在自定义的DOM元素,所有指令都作用于DOM元素,所以组件和指令都可以通过DI(Dependency Injection)获取宿主元素ElemntRef对象。

  1. @Component({
  2. selector: 'sample',
  3. ...
  4. export class SampleComponent{
  5. constructor(private hostElement: ElementRef) {
  6. //outputs <sample>...</sample>
  7. console.log(this.hostElement.nativeElement.outerHTML);
  8. }
  9. ...
  1. private el: ElementRef,
  2. // 表格增加滚动条锁定最后
  3. ngAfterViewChecked(): void {
  4. if (this.flag) {
  5. const editorhtml = this.el.nativeElement.querySelector('.editorhtml');
  6. editorhtml.scrollTop = editorhtml.scrollHeight - editorhtml.clientHeight;
  7. this.flag = false;
  8. }
  9. }
  10. // 滚动不刷新滚动条位置
  11. scroll(e) {
  12. this.flag = false;
  13. }

9.3 TemplateRef

9.3.1 模板:跨程序视图内一堆DOM元素的组合。在HTML5引入template标签前,浏览器通过在script标签内设置type来引入模板。

  1. <script id="tpl" type="text/template">
  2. <span>I am span in template</span>
  3. </script>

9.3.2 缺点: 1. 这种方式不仅有语义缺陷,还需要手动创建DOM模型。
2.通过template标签,浏览器可以解析HTML并创建DOM树,但不会渲染它,该DOM树可以通过content属性访问。

  1. <script>
  2. let tpl = document.querySelector('#tpl');
  3. let container = document.querySelector('.insert-after-me');
  4. insertAfter(container, tpl.content); // content
  5. </script>
  6. <div class="insert-after-me"></div>
  7. <ng-template id="tpl">
  8. <span>I am span in template</span>
  9. </ng-template>

9.3.3 ng-template : Angular采用template标签,实现了Template抽象类template标签一起合作。

  1. @Component({
  2. selector: 'sample',
  3. template: `
  4. <ng-template #tpl>
  5. <span>I am span in template</span>
  6. </ng-template>
  7. `
  8. })
  9. export class SampleComponent implements AfterViewInit {
  10. @ViewChild("tpl") tpl: TemplateRef<any>;
  11. ngAfterViewInit() {
  12. let elementRef = this.tpl.elementRef;
  13. // outputs `template bindings={}`
  14. console.log(elementRef.nativeElement.textContent);
  15. }
  16. }

Angular从DOM中移除template元素,并在其位置插入注释.

TemplateRef 是一个结构简单的抽象类,它的 elementRef 属性是对其宿主元素的引用,还有一个 createEmbeddedView 方法。然而 createEmbeddedView 方法很有用,因为它可以创建一个视图(view)并返回该视图的引用对象 ViewRef

autocomplete=”new-password”

https://blog.csdn.net/zwj_jyzl/article/details/90348405
https://blog.csdn.net/qq_29532651/article/details/103729603
https://blog.csdn.net/wjyyhhxit/article/details/91973582?utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link

https://segmentfault.com/a/1190000012252368

https://juejin.cn/post/6910943445569765384#heading-38
https://juejin.cn/post/6910943445569765384#heading-25

https://www.jianshu.com/p/2aaf65840310