angualr - 模块与组件

模块

@NgModule 的设计意图

静态的元数据 (declarations)

运行时的元数据 (providers)

组合与分组 (imports 和 exports)

《angular从零到一》说的简单些

NgModule是一个装饰器函数,它接收一个用来描述模块属性的元数据对象。其中最重要的属性是:
❑declarations:声明本模块中拥有的视图类。Angular有三种视图类:组件、指令和管道。
❑exports: declarations的子集,可用于其他模块的组件模板。
❑imports:本模块声明的组件模板需要的类所在的其他模块。
❑providers:服务的创建者,并加入到全局服务列表中,可用于应用任何部分。
❑bootstrap:指定应用的主视图(称为根组件),它是所有其他视图的宿主。只有根模块才能设置bootstrap属性

插值表达式

插值是指将表达式嵌入到被标记的文本中。默认情况下,插值使用双花括号 {{ 和 }} 作为定界符。

<p>footer works! {{year}} - {{year + 10}}</p>

组件

组件是Angular应用中最基本的U构造块
组件必须从属于某个 NgModule 才能被其它组件或应用使用
组件在 @NgModule 元数据的 declarations 字段中引用

@Component 元数据

@Component 装饰器会指出紧随其后的那个类是个组件类,并为其指定元数据。

在下面的范例代码中,你可以看到 HeroListComponent 只是一个普通类,完全没有 Angular 特有的标记或语法。 直到给它加上了 @Component 装饰器,它才变成了组件。

组件的元数据告诉 Angular 到哪里获取它需要的主要构造块,以创建和展示这个组件及其视图。具体来说,它把一个模板(无论是直接内联在代码中还是引用的外部文件)和该组件关联起来。该组件及其模板,共同描述了一个视图

除了包含或指向模板之外,@Component 的元数据还会配置要如何在 HTML 中引用该组件,以及该组件需要哪些服务等等。

下面的例子中就是 HeroListComponent 的基础元数据:

@Component({
  selector:    'app-hero-list',
  templateUrl: './hero-list.component.html',
  providers:  [ HeroService ]
})
export class HeroListComponent implements OnInit {
  /* . . . */
}
配置选项 详情
selector 一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。比如,如果应用的 HTML 中包含 <app-hero-list></app-hero-list>,``Angular 就会在这些标签中插入一个 HeroListComponent 实例的视图。
templateUrl 该组件的 HTML 模板文件相对于这个组件文件的地址。``另外,你还可以用 template 属性的值来提供内联的 HTML 模板。这个模板定义了该组件的宿主视图
providers 当前组件所需的服务提供者的一个数组。``在这个例子中,它告诉 Angular 该如何提供一个 HeroService 实例,以获取要显示的英雄列表。

在组件的 @Component 装饰器中,添加一个名叫 animations: 的元数据属性。你可以把用来定义动画的触发器放进 animations 元数据属性中。

src/app/app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
  animations: [
    // animation triggers go here
  ]
})

组件生命周期

ngOnChanges 组件输入属性改变

子组件

scroll-lable-tab.component.html

<ul [ngStyle]="{'background-color': backColor}">
  export class ScrollLableTabComponent implements OnInit,OnChanges{
  selectIndex = -1;
  @Input() menus: TopMenu[] = [];
  @Input() backColor = "#fff";
  @Output() tabSelected = new EventEmitter<TopMenu>();
  handleSelected(index: number){
    this.selectIndex = index;
    this.tabSelected.emit(this.menus[this.selectIndex]);
  }
  ngOnInit(): void {
    console.log("组件初始化");

  }
  ngOnChanges(changes: SimpleChanges): void {
    console.log('组件输入属性改变',changes);
  }
}

父级

app.component.html

  <app-scroll-lable-tab [menus]="topMenus" [backColor]="backColor" (tabSelected)="handleSelectedTab($event)"></app-scroll-lable-tab>

app.component.ts

  backColor = "red";
  handleSelectedTab(topmenu: TopMenu){
    const colors =['red','blue','black'];
    const idx = Math.floor(Math.random()*3);
    this.backColor = colors[idx];
    console.log(topmenu);
  }

方法1:@Output()装饰器和EventEmitter来通知父组件

方法2:父组件和子组件之间服务通信

在Angular中,除了使用@Output()装饰器和EventEmitter来通知父组件外,还有其他几种方式可以实现子组件与父组件之间的通信。

ngAfterContentInit 组件内容投影完成时

内容投影

内容投影 是从组件外部导入 HTML 内容,并把它插入在组件模板中指定位置上的一种途径。可以在目标中通过查找下列结构来认出内容投影。

  • 元素标签中间的 HTML
  • 组件模板中的 <ng-content> 标签

<ng-content> 标签是外来内容的占位符。它告诉 Angular 在哪里插入这些外来内容。在这里,被投影进去的内容就是来自父组件的 <app-child> 标签。

Name Description
select="selector" Only select elements from the projected content that match the given CSS selector.``仅从投影内容中选择与给定CSS “selector” 匹配的元素。
Angular supportsselectors for any combination of tag name, attribute, CSS class, and the :not pseudo-class.``Angular支持对标签名、属性、CSS类和“:not”伪类的任意组合使用[selectors]。
<ng-content select="样式类/HTML标签/指令"></ng-content>
//仅从投影内容中选择与给定 CSS 匹配的元素selector

例子,在 app-scoll-lable-tab 里面加入 div ,这里是不显示的

必须在这个组件加上 <ng-content>

生命周期钩子

1 挂载:

ngOnInit() 、ngAfterContentInit()、 ngAfterViewInit()

2 更新:

ngOnChanges()、ngDoCheck()(不推荐)、ngAfterContentChecked()、ngAfterViewChecked()

3 卸载:

ngOnDestroy()

钩子方法 用途 时机
ngOnChanges() 多次 当Angular设置或重设数据绑定的输入属性时做出响应。该方法接收一个包含当前和之前属性值的SimpleChanges 对象。


NOTE:
这种情况发生得非常频繁,所以你在这里进行的任何操作都会对性能产生重大影响。

See details in Using change detection hooks in this document.当 Angular 设置或重新设置数据绑定的输入属性时响应。``该方法接受当前和上一属性值的 SimpleChanges 对象


注意
这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。
引用类型属性值的改变,检测不到!!!!如果要更改引用类型,但可以给引用类型重新赋值(例如给对象赋值)
欲知详情,参阅本文档的使用变更检测钩子
ngOnInit() 之前调用(如果组件有绑定的输入),并且在一个或多个数据绑定的输入属性发生变化时调用。


NOTE:
如果你的组件没有输入或者你在使用它时没有提供任何输入,框架将不会调用 ngOnChanges().

如果组件绑定过输入属性,那么在 ngOnInit() 之前以及所绑定``的一个或多个输入属性的值发生变化时都会调用。


注意
如果你的组件没有输入属性,或者你使用它时没有提供任何输入属性,那么框架就不会调用 ngOnChanges()
ngOnInit() 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。欲知详情,参阅本文档中的初始化组件或指令 在第一轮 ngOnChanges() 完成之后调用,只调用一次。而且即使没有调用过 ngOnChanges(),也仍然会调用 ngOnInit()(比如当模板中没有绑定任何输入属性时)。
ngDoCheck()``多次,慎用 检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。``欲知详情和范例,参阅本文档中的自定义变更检测 紧跟在每次执行变更检测时的 ngOnChanges() 和首次执行变更检测时的 ngOnInit() 后调用。
ngAfterContentInit() 当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。<br>欲知详情和范例,参阅本文档中的响应内容中的变更 第一次 ngDoCheck() 之后调用,只调用一次。
ngAfterContentChecked()``多次 每当 Angular 检查完被投影到组件或指令中的内容之后调用。<br>欲知详情和范例,参阅本文档中的响应被投影内容的变更 ngAfterContentInit() 和每次 ngDoCheck() 之后调用。
ngAfterViewInit() 当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。<br>欲知详情和范例,参阅本文档中的响应视图变更 第一次 ngAfterContentChecked() 之后调用,只调用一次。
ngAfterViewChecked()``多次 每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。 ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。

组件的输入输出

把数据发送到子组件

父组件 @Input 属性绑定

把数据发送到父组件

@Output() 必须是 EventEmitter 类型,它是 @angular/core 中用来发出自定义事件的类。

装饰器部件 详情
@Output() 一个装饰器函数,它把该属性标记为数据从子组件进入父组件的一种途径。
newItemEvent 这个 @Output() 的名字。
EventEmitter<string> 这个 @Output() 的类型。
new EventEmitter<string>() 要求 Angular 创建一个新的事件发射器,它发出的数据是 string 类型的。

关于 EventEmitter 的详细信息,请参阅 EventEmitter API 文档

emit(): 发出包含给定值的事件。

subscribe(): 注册此实例发出的事件的处理器。

事件绑定 (tabSelected)='handleSelectedTab($event)' 会把子组件中的 tabSelected 事件连接到父组件的 handleSelectedTab() 方法。

$event 中包含用户在子组件模板上的 <input> 中键入的数据。

获取DOM对象(组件模板):1.模板变量

模板变量可以帮助你在模板的另一部分使用这个部分的数据。使用模板变量,你可以执行某些任务,比如响应用户输入或微调应用的表单。

模板变量可以引用这些东西:

语法

在模板中,要使用井号 # 来声明一个模板变量。下列模板变量 #phone 声明了一个名为 phone 的变量,其值为此 <input> 元素

<input #phone placeholder="phone number" />

可以在组件模板中的任何地方引用某个模板变量。这里的 <button> 就引用了 phone 变量。

<input #phone placeholder="phone number" />

<!-- lots of other elements -->

<!-- phone refers to the input element; pass its `value` to an event handler -->
<button type="button" (click)="callPhone(phone.value)">Call</button>

获取DOM对象(组件类):2.ViewChild

模板在组件类中的引用 @ViewChild

属性装饰器,用于配置一个视图查询。 变更检测器会在视图的 DOM 中查找能匹配上该选择器的第一个元素或指令。 如果视图的 DOM 发生了变化,出现了匹配该选择器的新的子节点,该属性就会被更新

在调用 NgAfterViewInit 回调函数之前就会设置这些视图查询。

元数据属性

  • selector - 用于查询的指令类型或名字。
  • read - 从查询到的元素中读取另一个令牌。
  • static - 如果为 true,则在变更检测运行之前解析查询结果,如果为 false,则在变更检测之后解析。默认为 false。

1 HTML 中引用(更改dom)

<!-- #后面是给模板或者dom元素起的引用名字,以便可以在组件类或模板中进行引用 -->
<div class="container" #imageSlider>
  <div class="image-slider">
      <img *ngFor="let slider of sliders" [src]="slider.imgUrl" [alt]="slider.caption">
  </div>
  <div class="nav-selection">
      <span *ngFor="let _ of sliders; let idx=index" class="slide-button"></span>
  </div>
</div>

ts

export class ImageSliderComponent implements OnInit{
/** 
   *@ViewChild 是一个选择器,用来查找要引用的 dom元素或者组件
   * ElementRef 是 dom 元素的一个包装类,因为 dom元素不是angular中的类,所以需要一个
   * 包装类以便在 angular中使用和标识其类型
   */
  //  @ViewChild('container') containerRef: ElementRef;
   @ViewChild('imageSlider', { static: true }) imgSlider!: ElementRef;
   @Input() sliders: ImageSlider[] = [];
  ngOnInit(): void {
    console.log(this.imgSlider.nativeElement.innerHTML = '<span>aaa</span>')
  }

}

2  angular组件中引用

效果如下

获取DOM对象(一组组件类):3.ViewChildren

用于配置视图查询的参数装饰器

用于从视图 DOM 中获取元素或指令的 QueryList。每当添加、删除或移动子元素时,此查询列表都将更新,并且其可观察对象 changes 将发出新值。

在调用 ngAfterViewInit 前设置的视图查询。

元数据属性

  • selector - 要查询的指令类型或名称。

  • read - 用于从查询元素中读取不同的标记。

  • emitDistinctChangesOnly -仅当 QueryList 结果发生更改时, QueryList#changes observable 才会发出新值。当 false 时,即使 QueryList 没有 changes ,observable 也可能会发出更改。注意:此配置选项已弃用*,它将被永久设置为 true 并在未来版本的 Angular 中删除。

    支持以下选择器。

    • 任何带有 @Component 或 @Directive 装饰器的类
    • 作为字符串的模板引用变量(例如,使用 @ViewChildren('cmp') 查询 <my-component #cmp></my-component> )
    • 在当前组件的子组件树中定义的任何提供者(例如 @ViewChildren(SomeService) someService!: SomeService )
    • 通过字符串标记定义的任何提供程序(例如 @ViewChildren('someToken') someTokenVal!: any
    • TemplateRef (例如使用 @ViewChildren(TemplateRef) template; 查询 <ng-template></ng-template> ;)

此外,多个字符串选择器可以用逗号分隔(例如 @ViewChildren('cmp1,cmp2')

read 支持以下值:

  • 任何带有 @Component 或 @Directive 装饰器的类
  • 在此查询的 selector 匹配的组件注入器上定义的任何提供程序
  • 通过字符串标记定义的任何提供程序(例如 {provide: 'token', useValue: 'val'}
  • TemplateRefElementRef 和 ViewContainerRef

Renderer2

扩展此基类以实现自定义渲染器。默认情况下,Angular 会把模板渲染成 DOM。 你可以使用自定义渲染器来拦截渲染类调用,或用于渲染一些非 DOM 的东西。

说明:

使用 RendererFactory2 创建你的自定义渲染器。

使用自定义渲染器可以绕过 Angular 的模板机制,并进行无法以声明式语法表达的自定义 UI 变更。 比如,如果你要设置无法静态得知名称的 Property 或 Attribute,可以使用 setProperty() 或 setAttribute() 方法。

使用高级样式特性

1 设置视图封装

ViewEncapsulation 枚举值

Emulated 默认值。angular 通过改写内容和样式添加属性。从而模拟影子DOM
ShadowDom 使用浏览器的影子 DOM 特性
None 未修改的 CSS 样式添加到HTML 文档的 head 节中。并且让浏览器设法使用正常的 CSS 优先级规则来应用样式
@Component({
  selector: 'app-product',
  templateUrl: 'template.component.html',
  styles: ['div {background-color: lightgreen;}'],
  encapsulation: ViewEncapsulation.Emulated,
})

2 使用影子 DOM CSS 选择器

Name Description
:host 匹配组件的宿主元素
:host-context(classSelector) 特定CSS类成员的宿主元素的祖先
/deep/或>>>
::ng-deep
父组件使用此类选择器来定义影响子组件模板中元素的样式。只有当 @Component 装饰器的 encapsulation 属性设置为 Emulated 时,才使用这个选择器
/deep/ 或 >>> 作废;::ng-deep,15后标记作废,但17还可使用
posted @ 2024-03-12 23:01  【唐】三三  阅读(21)  评论(0编辑  收藏  举报