Angular 组件是一个由模板组成的元素,通过组件来渲染我们的应用。
一个简单组件
Angular提供了@Component装饰器来,我们需要使用该装饰器来定义一个组件。
@Component内置了一些参数:
providers: 用来声明一些资源,这些资源可以在构造函数中通过DI注入。selector: 在html中适应的查询选择器,Angular会使用定义的组件替换html中的该选择器styles: 定义一组内联样式,数组类型styleUrls:一组样式文件template:内联模板templateUrl:模板文件
例子:
import { Component } from '@angular/core';@Component({selector: 'app-required',styleUrls: ['requried.component.scss'],templateUrl: 'required.component.html'})export class RequiredComponent { }
模板 & 样式
模板是html文件,里面可以包含一些逻辑。
我们可以通过两种方式来指定组件的模板:
- 通过文件路径来指定模板
@Component({templateUrl: 'hero.component.html'})
- 通过使用内联方式指定模板
@Component({template: '<div>This is a template.</div>'})
组件中定义的模板可以包含样式,我们可以在@Component中定义当前模板的样式。在组件中定义的样式和应用的style.css中定义是有区别的。组件中定义的任何样式,作用域都被限制在此组件内。
例如,我们在组件中添加样式:
div {background: red;}
组件模板内的所有的div背景都会渲染成红色,但是其他组件中的div不会受到此样式的影响。
编译后的代码类似如下这样:
<style>div[_ngcontent-c1] {background:red;}</style>
我们可以通过两种方式为组件的模板定义样式:
- 通过文件的方式
@Component({styleUrls: ['hero.component.css']})
- 通过内联的方式
styles: [`div {background: red;}`]
如何选择
不论模版还是样式,组件都提供来两种方式来声明它们。理论上我们可以随心所欲,自由组合。但实际的开发过程中我们还是需要有自己的原则:根据实际内容的多少来选择声明方式,内容较多就选择文件方式,这样可以使代码结构更加清晰,整洁。
组件测试
hero.component.html
<form (ngSubmit)="submit($event)" [formGroup]="form" novalidate><input type="text" formControlName="name"/><button type="submit"> Show hero name</button></form>
hero.component.ts
import { FromControl, FormGroup, Validators } from '@angular/forms';import { Component } from '@angular/core';@Component({slector: 'app-hero',templateUrl: 'hero.component.html'})export class HeroComponent {public form = new FormGroup({name: new FormControl('', Validators.required)});submit(event) {console.log(event);console.log(this.form.controls.name.value);}}
hero.component.spec.ts
import { ComponentFixture, TestBed, async } from '@angular/core/testing';import { HeroComponent } from 'hero.component';import { ReactiveFormsModule } from '@angular/forms';describe('HeroComponent', () => {let component: HeroComponent;let fixture: ComponentFixture<HeroComponent>;beforeEach(async(() => {TestBed.configureTestingModule({declarations: [HeroComponent],imports: [ReactiveFormsModule]}).compileComponents();fixtrue = TestBed.createComponent(HeroComponent);component = fixtrue.componentInstance;fixture.detectChanges();}));it('should be created', () => {expect(component).toBetruthy();});it('should log hero name in the console when user submit form', async(() => {const heroName = 'Saitama';const element = <HTMLFormElement>fixture.debugElement.nativeElement.querySelector('form');spyOn(console, 'log').and.callThrough();component.form.controls['name'].setValue(heroName);element.querySelector('button').click();fixture.whenStable().then(() => {fixture.detectChanges();expect(console.log).toHaveBeenCalledWith(heroName);});}));it('should validate name field as required', () => {component.form.controls['name'].setValue('');expect(component.form.invalid).toBeTruthy();});})
嵌套组件
组件是通过selector来渲染的,所以我们就可以通过嵌套的方式来使用所有的组件。
import { Component, Input } from '@angular/core';@Component({selector: 'app-required',template: `{{name}} is required.`})export class RequiredComponent {@Input()public name: string = '';}
我们就可以在其他的组件中,通过使用app-required标签来嵌套我们的组件。
import { Component, Input } from '@angular/core';@Component({selector: 'app-sample',template: `<input type="text" name="heroName" /><app-required name="Hero Name"></app-required>`})export class SampleComponent {@Input()public name = '';}
