Angular 组件是一个由模板组成的元素,通过组件来渲染我们的应用。

一个简单组件

Angular提供了@Component装饰器来,我们需要使用该装饰器来定义一个组件。

@Component内置了一些参数:

  • providers : 用来声明一些资源,这些资源可以在构造函数中通过DI注入。
  • selector : 在html中适应的查询选择器,Angular会使用定义的组件替换html中的该选择器
  • styles : 定义一组内联样式,数组类型
  • styleUrls :一组样式文件
  • template :内联模板
  • templateUrl :模板文件

例子:

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'app-required',
  4. styleUrls: ['requried.component.scss'],
  5. templateUrl: 'required.component.html'
  6. })
  7. export class RequiredComponent { }

模板 & 样式

模板是html文件,里面可以包含一些逻辑。

我们可以通过两种方式来指定组件的模板:

  1. 通过文件路径来指定模板
  1. @Component({
  2. templateUrl: 'hero.component.html'
  3. })
  1. 通过使用内联方式指定模板
  1. @Component({
  2. template: '<div>This is a template.</div>'
  3. })

组件中定义的模板可以包含样式,我们可以在@Component中定义当前模板的样式。在组件中定义的样式和应用的style.css中定义是有区别的。组件中定义的任何样式,作用域都被限制在此组件内。
例如,我们在组件中添加样式:

  1. div {background: red;}

组件模板内的所有的div背景都会渲染成红色,但是其他组件中的div不会受到此样式的影响。
编译后的代码类似如下这样:

  1. <style>div[_ngcontent-c1] {background:red;}</style>

我们可以通过两种方式为组件的模板定义样式:

  1. 通过文件的方式
  1. @Component({
  2. styleUrls: ['hero.component.css']
  3. })
  1. 通过内联的方式
  1. styles: [`div {background: red;}`]

如何选择

不论模版还是样式,组件都提供来两种方式来声明它们。理论上我们可以随心所欲,自由组合。但实际的开发过程中我们还是需要有自己的原则:根据实际内容的多少来选择声明方式,内容较多就选择文件方式,这样可以使代码结构更加清晰,整洁。

组件测试

hero.component.html

  1. <form (ngSubmit)="submit($event)" [formGroup]="form" novalidate>
  2. <input type="text" formControlName="name"/>
  3. <button type="submit"> Show hero name</button>
  4. </form>

hero.component.ts

  1. import { FromControl, FormGroup, Validators } from '@angular/forms';
  2. import { Component } from '@angular/core';
  3. @Component({
  4. slector: 'app-hero',
  5. templateUrl: 'hero.component.html'
  6. })
  7. export class HeroComponent {
  8. public form = new FormGroup({
  9. name: new FormControl('', Validators.required)
  10. });
  11. submit(event) {
  12. console.log(event);
  13. console.log(this.form.controls.name.value);
  14. }
  15. }

hero.component.spec.ts

  1. import { ComponentFixture, TestBed, async } from '@angular/core/testing';
  2. import { HeroComponent } from 'hero.component';
  3. import { ReactiveFormsModule } from '@angular/forms';
  4. describe('HeroComponent', () => {
  5. let component: HeroComponent;
  6. let fixture: ComponentFixture<HeroComponent>;
  7. beforeEach(async(() => {
  8. TestBed.configureTestingModule({
  9. declarations: [HeroComponent],
  10. imports: [ReactiveFormsModule]
  11. }).compileComponents();
  12. fixtrue = TestBed.createComponent(HeroComponent);
  13. component = fixtrue.componentInstance;
  14. fixture.detectChanges();
  15. }));
  16. it('should be created', () => {
  17. expect(component).toBetruthy();
  18. });
  19. it('should log hero name in the console when user submit form', async(() => {
  20. const heroName = 'Saitama';
  21. const element = <HTMLFormElement>fixture.debugElement.nativeElement.querySelector('form');
  22. spyOn(console, 'log').and.callThrough();
  23. component.form.controls['name'].setValue(heroName);
  24. element.querySelector('button').click();
  25. fixture.whenStable().then(() => {
  26. fixture.detectChanges();
  27. expect(console.log).toHaveBeenCalledWith(heroName);
  28. });
  29. }));
  30. it('should validate name field as required', () => {
  31. component.form.controls['name'].setValue('');
  32. expect(component.form.invalid).toBeTruthy();
  33. });
  34. })

嵌套组件

组件是通过selector来渲染的,所以我们就可以通过嵌套的方式来使用所有的组件。

  1. import { Component, Input } from '@angular/core';
  2. @Component({
  3. selector: 'app-required',
  4. template: `{{name}} is required.`
  5. })
  6. export class RequiredComponent {
  7. @Input()
  8. public name: string = '';
  9. }

我们就可以在其他的组件中,通过使用app-required标签来嵌套我们的组件。

  1. import { Component, Input } from '@angular/core';
  2. @Component({
  3. selector: 'app-sample',
  4. template: `
  5. <input type="text" name="heroName" />
  6. <app-required name="Hero Name"></app-required>
  7. `
  8. })
  9. export class SampleComponent {
  10. @Input()
  11. public name = '';
  12. }