服务
创建服务之后,我们在 StorageService.service.ts 中定义一个测试方法:
export class StorageService { constructor() { } test() { alert('调用服务方法'); } } 复制代码
我们重新建立一个 home 组件,来测试服务调用。
<p>home works!</p> <button (click)="getService()">获取服务</button> 复制代码
import { Component, OnInit } from '@angular/core'; import {StorageService} from '../../services/storage.service'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { constructor(public storage: StorageService) { } ngOnInit(): void { } getService() { this.storage.test(); } } 复制代码
测试:
数据持久化到localStroage
之前的 search 和 todoList 测试案例当页面刷新数据都会消失,接下来我们在 service 里构建数据持久化方法(实际是存放到浏览器的 localStroage 中)。
首先在 StorageService.service.ts 定义三个方法:
set(key: string, value: string[]) { // @ts-ignore localStorage.setItem(key, JSON.stringify(value)); } get(key: string) { return JSON.parse(localStorage.getItem(key)); } remove(key: string) { localStorage.removeItem(key); } 复制代码
在组件中调用服务里定义的方法:
import {Component, OnInit} from '@angular/core'; import {StorageService} from '../../services/storage.service'; @Component({ selector: 'app-search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { keyWord: any = ''; keyWordsOld: any[] = []; product: any = ''; products: any[] = []; constructor(public storage: StorageService) { } // 在初始化方法中查询localStorage中是否有数据 ngOnInit(): void { // tslint:disable-next-line:prefer-const let serchList = this.storage.get('serchList'); // tslint:disable-next-line:prefer-const let todoList = this.storage.get('todoList'); if (serchList) { // @ts-ignore this.keyWordsOld = serchList; } if (todoList) { this.products = todoList; } } //输入内容后回车触发该方法 keyup(e) { if (e.keyCode === 13){ //首先判断搜索内容是否重复,若为新则加入 if (this.keyWordsOld.indexOf(this.keyWord) === -1) { this.keyWordsOld.push(this.keyWord); this.storage.set('serchList', this.keyWordsOld); } this.keyWord = ''; } } //搜索按钮绑定的方法 search() { if (this.keyWordsOld.indexOf(this.keyWord) == -1) { this.keyWordsOld.push(this.keyWord); this.storage.set('serchList', this.keyWordsOld); } this.keyWord = ''; } //删除按钮绑定的方法 delete(key) { this.keyWordsOld.splice(key, 1); this.storage.set('serchList', this.keyWordsOld); } //回车事件,触发该方法 add(e) { // tslint:disable-next-line:triple-equals if (e.keyCode == 13) { if (!this.equalProduct(this.products, this.product)) { this.products.push({ title: this.product, status: 0 }); this.product = ''; this.storage.set('todoList', this.products); } else { alert('数据已存在'); this.product = ''; } } } deleteWay(key) { this.products.splice(key, 1); this.storage.set('todoList', this.products); } //新增数据前与已有记录进行比对 equalProduct(products: any[], value: any) { if (!value || value === '') { return false; } // tslint:disable-next-line:prefer-for-of for (let i = 0; i < products.length; i++) { // tslint:disable-next-line:triple-equals if (products[i].title == value) { return true; } } return false; } //change事件用来更新缓存中的内容 change() { this.storage.set('todoList', this.products); } } 复制代码
html 文件基本不变,测试效果:
注意观察下方 localStorage 中的内容,当页面刷新之后数据仍然存在。
生命周期
指令和组件的实例有一个生命周期:当 Angular 新建、更新和销毁它们时触发。 通过实现一个或多个 Angular core
库里定义的生命周期钩子接口,开发者可以介入该生命周期中的这些关键时刻。
每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng
前缀构成的。比如,OnInit
接口的钩子方法叫做 ngOnInit
, Angular 在创建组件后立刻调用它,:
export class PeekABoo implements OnInit { constructor(private logger: LoggerService) { } // implement OnInit's `ngOnInit` method ngOnInit() { this.logIt(`OnInit`); } logIt(msg: string) { this.logger.log(`#${nextId++} ${msg}`); } } 复制代码
生命周期的顺序:
这里我们重点学习 ngOnInit() 和 ngAfterViewInit()这两个方法。
首先新建 home 组件,其 html 内容为:
<h2>这里一个home组件---DOM操作演示</h2> <div id="box"> 这是一个div标签 </div> <br> <div id="box2" *ngIf="flag" > 这是第二个div标签 </div> 复制代码
home.component.ts 文件内容为:
export class HomeComponent implements OnInit { flag = true; constructor() { } /*生命周期函数*/ ngOnInit(): void { /*//组件和指令初始化完成,并不是真正的dom加载完成*/ let oBox = document.getElementById('box'); console.log(oBox.innerHTML); oBox.style.color = 'red'; // 此处获取不到dom节点,所以最好不要在ngOnInit方法中获取dom节点 /*let oBox2: any = document.getElementById('box2'); console.log(oBox2.innerHTML); oBox2.style.color = 'blue';*/ } // 视图加载完成后触发的方法,dom加载完成 // tslint:disable-next-line: use-lifecycle-interface ngAfterViewInit(): void { let oBox2 = document.getElementById('box2'); console.log(oBox2.innerHTML); oBox2.style.color = 'blue'; } } 复制代码
通过操作 dom 节点来区分 ngOnInit 和 ngAfterViewInit 方法,前者用来初始化指令/组件,后者是当 Angular 初始化完组件视图及其子视图之后调用。所以后者才知道 box2 是否渲染完毕,然后才能获取到该 dom 节点并加以修改。
页面运行效果如下:
父子组件之间通信传值
新建四个组件:news,home,header,footer。其中 home 和 header 是父子组件关
系,news 和 footer 是父子组件关系 。
父组件通过@Input给子组件传值
1、home.component.html 用来传递变量和方法,也可以将 home 组件中所有信息传递过去。
<app-header [msg]="msg" [title]="title" [run]="run" [home]="this"></app-header> <br> <hr> <br> <h2>我是home组件</h2> 复制代码
2、home.component.ts
export class HomeComponent implements OnInit { msg: any = '我是home组件的msg'; title: any = 'home组件的title'; constructor() { } ngOnInit(): void { } run() { return '执行home组件的run方法'; } } 复制代码
3、header.component.html
<h2>我是header组件</h2> <br> <p>{{title}}-----{{msg}}</p> <br> <button (click)="getParentWay()">执行home组件的方法</button> 复制代码
4、header.component.ts,子组件中需要引入 Input,用来接收父组件传过来的数据。
import { Component, OnInit, Input } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'] }) export class HeaderComponent implements OnInit { @Input() msg: any; @Input() title: any; @Input() run: any; /*//获取home组件所有内容*/ @Input() home: any; constructor() { } ngOnInit(): void { } getParentWay() { // alert(this.run()); alert(this.home.run()); } } 复制代码
5、网页运行效果如下:
6、小结
父组件给子组件传值,即子组件获取父组件的属性和方法,大致分为以下几步:
- 父组件页面嵌套子组件页面,将属性和方法存放在子组件声明中;
- 子组件引入 Input 模块;
- 子组件通过@Input 获取子组件声明中存放的属性和方法;
子组件通过ViewChild给父组件传值
1、news.component.html,
<app-footer #footerComponent></app-footer> <br> <hr> <br> <h2>我是news组件</h2> <button (click)="getChildData()">获取子组件的数据</button> <br> <br> <br> <button (click)="getChildWay()">执行子组件的方法</button> 复制代码
2、news.component.ts
import {Component, OnInit, ViewChild} from '@angular/core'; @Component({ selector: 'app-news', templateUrl: './news.component.html', styleUrls: ['./news.component.css'] }) export class NewsComponent implements OnInit { @ViewChild('footerComponent') footer; constructor() { } ngOnInit(): void { } getChildData() { alert(this.footer.msg); } getChildWay() { this.footer.run(); } } 复制代码
3、footer.component.html
<h2>我是footer组件</h2> 复制代码
4、footer.component.ts
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-footer', templateUrl: './footer.component.html', styleUrls: ['./footer.component.css'] }) export class FooterComponent implements OnInit { msg: any = '我是footer组件的msg'; constructor() { } ngOnInit(): void { } run() { alert('执行footer组件的run方法'); } } 复制代码
5、网页运行效果如下:
6、小结
子组件给父组件传值,即父组件主动获取子组件的属性和方法,大致分为以下几步:
- 父组件页面嵌套子组件页面,子组件声明时需要定义一个名称来代指子组件;
- 父组件引入 ViewChild 模块;
- 父组件通过@ViewChild 获取子组件的全部内容;