一、Angular 介紹
Angualr
是一款來自谷歌的開源的web
前端框架,誕生於2009
年,由Misko Hevery
等 人創建,後爲JS
框架,已經被用於
- 根據項目數統計
angular(1.x 、2.x 、4.x、5.x、6.x、7.x)
是現在網上使用量最大的框架 Angualr
基於TypeScript
和react
、vue
相比,Angular
更適合中大型企業級項目。
目前 2018 年 11 月 25 日
angular
最新版本angular7.x
。根據官方介紹,Angular
每過幾個月 就會更新一個版本。此教程同樣適用於後期更新的Angular8.x
、Angular9.x
image.png
學習 Angular 必備基礎
必備基礎:
html
、css
、js
、es6
、Typescript
二、Angular 環境搭建及創建項目
2.1 環境搭建
1. 安裝 nodejs
安裝
angular
的計算機上面必須安裝最新的nodejs
--注意安裝nodejs
穩定版本
2. 安裝 cnpm
npm 可能安裝失敗建議先用
npm
安裝一下cnpm
用淘寶鏡像安裝https://npm.taobao.org
npm install -g cnpm --registry=https://registry.npm.taobao.org
3. 使用 npm/cnpm 命令安裝 angular/cli
npm install -g @angular/cli
# 或者
cnpm install -g @angular/cli
ng v
查看版本信息
angular cli
4. 安裝插件
image.png
5. 安裝chrome擴展
用
augury
查看component
結構,更方便調試
image.png
2.2 創建項目
# 創建項目
ng new my-app
cd my-app
# 運行項目
ng serve --open
2.3 目錄結構分析
image.png
app目錄(重點)
app
目錄是我們要編寫的代碼目錄。我們寫的代碼都是放在這個目錄。
一個Angular
程序至少需要一個模塊和一個組件。在我們新建項目的時候命令行已經默認生成出來了
image.png
app.component.ts
:這個文件表示組件,- 組件是
Angular
應用的基本構建模塊,可以理解爲一段帶有業務邏輯和數據的Html
我們來看看app.component.ts
中的代碼,並解釋下代碼的意義
app.component.ts
/*這裏是從Angular核心模塊裏面引入了component裝飾器*/
import {Component} from '@angular/core';
/*用裝飾器定義了一個組件以及組件的元數據 所有的組件都必須使用這個裝飾器來註解*/
@Component({
/*組件元數據 Angular會通過這裏面的屬性來渲染組件並執行邏輯
* selector就是css選擇器,表示這個組件可以通過app-root來調用,index.html中有個<app-root>Loading...</app-root>標籤,這個標籤用來展示該組件的內容
*templateUrl 組件的模板,定義了組件的佈局和內容
*styleUrls 該模板引用那個css樣式
* */
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
/*AppComponent本來就是一個普通的typescript類,但是上面的組件元數據裝飾器告訴Angular,AppComponent是一個組件,需要把一些元數據附加到這個類上,Angular就會把AppComponent當組件來處理*/
export class AppComponent {
/*這個類實際上就是該組件的控制器,我們的業務邏輯就是在這個類中編寫*/
title = '學習Angular';
}
組件相關的概念
- 組件元數據裝飾器(
@Component
)
簡稱組件裝飾器,用來告知Angular
框架如何處理一個TypeScript
類.Component
裝飾器包含多個屬性,這些屬性的值叫做元數據,Angular
會根據這些元數據的值來渲染組件並執行組件的邏輯 - 模板(
Template
)
我們可以通過組件自帶的模板來定義組件的外觀,模板以html
的形式存在,告訴Angular
如何來渲染組件,一般來說,模板看起來很像html
,但是我們可以在模板中使用Angular
的數據綁定語法,來呈現控制器中的數據。 - 控制器(
controller
)
控制器就是一個普通的typescript
類,他會被@Component
來裝飾,控制器會包含組件所有的屬性和方法,絕大多數的業務邏輯都是寫在控制器裏的。控制器通過數據綁定與模板來通訊,模板展現控制器的數據,控制器處理模板上發生的事件。
裝飾器,模板和控制器是組件的必備要素。還有一些可選的元素,比如:
- 輸入屬性(
@inputs
):是用來接收外部傳入的數據的,Angular
的程序結構就是一個組件樹,輸入屬性允許在組件樹種傳遞數據
提供器(providers
):這個是用來做依賴注入的 - 生命週期鉤子(
LifeCycle Hooks
):一個組件從創建到銷燬的過程中會有多個鉤子會被觸發,類似於Android中的Activity
的生命週期 - 樣式表:組件可以關聯一些樣式表
- 動畫(
Animations
):Angular
提供了一個動畫包來幫助我們方便的創建一些跟組件相關的動畫效果,比如淡入淡出等 - 輸出屬性(
@Outputs
):用來定義一些其他組件可能需要的事件或者用來在組件之間共享數據
組件的中關係就如下圖所示
image.png
下面我們來看看模塊文件
app.module.ts
:這個文件表示模塊- 與
AppComponent
類似,模塊也需要裝飾器來裝飾
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
@NgModule({
declarations: [
// 聲明模塊裏有什麼東西 只能聲明:組件/指令/管道
AppComponent,
HeroesComponent
],
// 聲明該模塊所依賴的模塊
imports: [
BrowserModule,
AppRoutingModule
],
// 默認情況下是空的
providers: [],
// 聲明模塊的主組件是什麼
bootstrap: [AppComponent]
})
export class AppModule { }
2.4 Angular cli
通過
ng g
列出當前命令
ng g
1. 創建新組件 ng generate component component-name
ng g component components/header
指定生成到哪個目錄
該命令會把生成的組件,添加到 src/app/app.module.ts
文件中 @NgModule
的 declarations
列表中聲明
image.png
2. 使用 Angular CLI 創建一個名叫 hero 的服務
ng generate service hero
該命令會在
src/app/hero.service.ts
中生成HeroService
類的骨架。HeroService
類的代碼如下:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class HeroService {
constructor() { }
}
3. 添加 AppRoutingModule
ng generate module app-routing --flat --module=app
--flat
把這個文件放進了src/app
中,而不是單獨的目錄中。--module=app
告訴CLI
把它註冊到AppModule
的imports
數組中。
生成的文件是這樣的:
src/app/app-routing.module.ts (generated)
content_copy
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
修改後
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
三、angular組件及組件裏的模板
3.1 創建angualr組件
1. 創建組件
ng g component components/header
2. 使用組件
<app-header></app-header>
3.2 Angular 綁定數據
1. 數據文本綁定
定義數據幾種方式
定義數據幾種方式
<h1>{{title}}</h1>
2. 綁定HTML
this.h="<h2>這是一個 h2 用[innerHTML]來解析</h2>"
<div [innerHTML]="h"></div>
3.3 聲明屬性的幾種方式
public
共有(默認) 可以在類裏外使用protected
保護類型 只能在當前類和子類中使用private
私有類型 只能在當期類使用
3.4 綁定屬性
用
[]
包裹
<div [id]="id" [title]="msg">調試工具看看我的屬性</div>
image.png
3.5 數據循環 *ngFor
*1. ngFor 普通循環
export class HomeComponent implements OnInit {
arr = [{ name: 'poetries', age: 22 }, { name: 'jing' , age: 31}];
constructor() { }
ngOnInit() {
}
}
<ul *ngIf="arr.length>0">
<li *ngFor="let item of arr">{{item.name}}- {{item.age}}</li>
</ul>
2. 循環的時候設置 key
<ul>
<li *ngFor="let item of list;let i = index;"> <!-- 把索引index賦給i -->
{{item}} --{{i}}
</li> </ul>
3. template 循環數據
<ul>
<li template="ngFor let item of list">
{{item}}
</li> </ul>
3.6 條件判斷 *ngIf
<p *ngIf="list.length > 3">這是 ngIF 判斷是否顯示</p>
<p template="ngIf list.length > 3">這是 ngIF 判斷是否顯示</p>
3.7 *ngSwitch
<ul [ngSwitch]="score">
<li *ngSwitchCase="1">已支付</li>
<li *ngSwitchCase="2">訂單已經確認</li> <li *ngSwitchCase="3">已發貨</li>
<li *ngSwitchDefault>無效</li>
</ul>
3.8 執行事件 (click)=”getData()”
<button class="button" (click)="getData()"> 點擊按鈕觸發事件
</button>
<button class="button" (click)="setData()"> 點擊按鈕設置數據
</button>
getData(){ /*自定義方法獲取數據*/ //獲取
alert(this.msg);
}
setData(){
//設置值
this.msg='這是設置的值';
}
3.9 表單事件
<input
type="text"
(keyup)="keyUpFn($event)"/>
<input type="text" (keyup)="keyUpFn($event)"/>
keyUpFn(e){
console.log(e)
}
3.10 雙向數據綁定
<input [(ngModel)]="inputVal">
注意引入:
FormsModule
import {FormsModule} from '@angular/forms'
NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
NewsComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
<!--使用-->
<input type="text" [(ngModel)]="inputValue"/> {{inputValue}}
3. 11 [ngClass]、[ngStyle]
1. [ngClass]:
<div [ngClass]="{'red': true, 'blue': false}">
這是一個 div
</div>
public flag=false;
<div [ngClass]="{'red': flag, 'blue': !flag}">
這是一個 div </div>
public arr = [1, 3, 4, 5, 6];
<ul>
<li *ngFor="let item of arr, let i = index"> <span [ngClass]="{'red': i==0}">{{item}}</span>
</li> </ul>
2. [ngStyle]:
<div [ngStyle]="{'background-color':'green'}">你好 ngStyle</div>
public attr='red';
<div [ngStyle]="{'background-color':attr}">你好 ngStyle</div>
3.12 管道
public today=new Date();
<p>{{today | date:'yyyy-MM-dd HH:mm:ss' }}</p>
其他管道
angular
中的管道(pipe
)是用來對輸入的數據進行處理,如大小寫轉換、數值和日期格式化等
angular
中的管道(pipe
) 以及自定義管道適用於angular4 angualr5 angualr6 angular7
常用的管道(pipe
)有
1. 大小寫轉換
<!--轉換成大寫-->
<p>{{str | uppercase}}</p>
<!--轉換成小寫-->
<p>{{str | lowercase}}</p>
2. 日期格式轉換
<p>
{{today | date:'yyyy-MM-dd HH:mm:ss' }}
</p>
3. 小數位數
接收的參數格式爲
{最少整數位數}.{最少小數位數}-{最多小數位數}
<!--保留2~4位小數-->
<p>{{p | number:'1.2-4'}}</p>
4. JavaScript 對象序列化
<p>
{{ { name: 'semlinker' } | json }}
</p>
<!-- Output: { "name": "semlinker" } -->
5. slice
<p>{{ 'semlinker' | slice:0:3 }}</p>
<!-- Output: sem -->
6. 管道鏈
<p>
{{ 'semlinker' | slice:0:3 | uppercase }}
</p>
<!-- Output: SEM -->
7. 自定義管道
自定義管道的步驟:
- 使用
@Pipe
裝飾器定義Pipe
的metadata
信息,如Pipe
的名稱 - 即name
屬性 - 實現
PipeTransform
接口中定義的transform
方法
7.1 WelcomePipe 定義
import { Pipe, PipeTransform } from '@angular/core';
[@Pipe](/user/Pipe)({ name: 'welcome' })
export class WelcomePipe implements PipeTransform {
transform(value: string): string {
if(!value) return value;
if(typeof value !== 'string') {
throw new Error('Invalid pipe argument for WelcomePipe');
}
return "Welcome to " + value;
}
}
7.2 WelcomePipe 使用
<div>
<p ngNonBindable>{{ 'semlinker' | welcome }}</p>
<p>{{ 'semlinker' | welcome }}</p> <!-- Output: Welcome to semlinker -->
</div>
7.3 RepeatPipe 定義
import {Pipe, PipeTransform} from '@angular/core';
[@Pipe](/user/Pipe)({name: 'repeat'})
export class RepeatPipe implements PipeTransform {
transform(value: any, times: number) {
return value.repeat(times);
}
}
7.4 RepeatPipe 使用
<div>
<p ngNonBindable>
{{ 'lo' | repeat:3 }}
</p>
<p>
{{ 'lo' | repeat:3 }}
</p>
<!-- Output: lololo -->
</div>
3.13 實現一個人員登記表單-案例
image.png
# 創建組件
ng g component components/form
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
public peopleInfo:any = {
username: '',
sex: '2',
cityList: ['北京', '上海', '深圳'],
city: '上海',
hobby:[{
title: '喫飯',
checked:false
},{
title:'睡覺',
checked:false
},{
title:'敲代碼',
checked:true
}],
mark:''
}
constructor() { }
ngOnInit() {
}
doSubmit(){
/*
jquery dom操作
<input type="text" id="username" />
let usernameDom:any=document.getElementById('username');
console.log(usernameDom.value);
*/
console.log(this.peopleInfo);
}
}
<h2>人員登記系統</h2>
<div class="people_list">
<ul>
<li>姓 名:<input type="text" id="username" [(ngModel)]="peopleInfo.username" value="fonm_input" /></li>
<li>性 別:
<input type="radio" value="1" name="sex" id="sex1" [(ngModel)]="peopleInfo.sex"> <label for="sex1">男 </label>
<input type="radio" value="2" name="sex" id="sex2" [(ngModel)]="peopleInfo.sex"> <label for="sex2">女 </label>
</li>
<li>
城 市:
<select name="city" id="city" [(ngModel)]="peopleInfo.city">
<option [value]="item" *ngFor="let item of peopleInfo.cityList">{{item}}</option>
</select>
</li>
<li>
愛 好:
<span *ngFor="let item of peopleInfo.hobby;let key=index;">
<input type="checkbox" [id]="'check'+key" [(ngModel)]="item.checked"/> <label [for]="'check'+key"> {{item.title}}</label>
</span>
</li>
<li>
備 注:
<textarea name="mark" id="mark" cols="30" rows="10" [(ngModel)]="peopleInfo.mark"></textarea>
</li>
</ul>
<button (click)="doSubmit()" class="submit">獲取表單的內容</button>
<br>
<br>
<br>
<br>
<pre>
{{peopleInfo | json}}
</pre>
</div>
h2{
text-align: center;
}
.people_list{
width: 400px;
margin: 40px auto;
padding:20px;
border:1px solid #eee;
li{
height: 50px;
line-height: 50px;
.fonm_input{
width: 300px;
height: 28px;
}
}
.submit{
width: 100px;
height: 30px;
float: right;
margin-right: 50px;
margin-top:120px;
}
}
3.14 實現一個完整的ToDo-案例
image.png
基礎版
# 創建組件
ng g component components/todo
<h2>todoList</h2>
<div class="todolist">
<input class="form_input" type="text" [(ngModel)]="keyword" (keyup)="doAdd($event)" />
<hr>
<h3>待辦事項</h3>
<ul>
<li *ngFor="let item of todolist;let key=index;" [hidden]="item.status==1">
<input type="checkbox" [(ngModel)]="item.status" /> {{item.title}} ------ <button (click)="deleteData(key)">X</button>
</li>
</ul>
<h3>已完成事項</h3>
<ul>
<li *ngFor="let item of todolist;let key=index;" [hidden]="item.status==0">
<input type="checkbox" [(ngModel)]="item.status" /> {{item.title}} ------ <button (click)="deleteData(key)">X</button>
</li>
</ul>
</div>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.scss']
})
export class TodoComponent implements OnInit {
public keyword: string;
public todolist: any[] = [];
constructor() { }
ngOnInit() {
}
doAdd(e){
if(e.keyCode == 13){
if(!this.todolistHasKeyword(this.todolist, this.keyword)){
this.todolist.push({
title: this.keyword,
status: 0 //0表示代辦事項 1表示已完成事項
});
this.keyword='';
}else{
alert('數據已經存在');
this.keyword='';
}
}
}
deleteData(key){
this.todolist.splice(key,1);
}
//如果數組裏面有keyword返回true 否則返回false
todolistHasKeyword(todolist:any, keyword:any){
//異步 會存在問題
// todolist.forEach(value => {
// if(value.title==keyword){
// return true;
// }
// });
if(!keyword) return false;
for(var i=0; i<todolist.length; i++){
if(todolist[i].title==keyword){
return true;
}
}
return false;
}
}
h2{
text-align: center;
}
.todolist{
width: 400px;
margin: 20px auto;
.form_input{
margin-bottom: 20px;
width: 300px;
height: 32px;
}
li{
line-height: 60px;
}
}
3.15 搜索緩存數據-案例
基礎版
# 創建組件
ng g component components/search
<div class="search">
<input type="text" [(ngModel)]="keyword" /> <button (click)="doSearch()">搜索</button>
<hr>
<ul>
<li *ngFor="let item of historyList;let key=index;">{{item}} ------ <button (click)="deleteHistroy(key)">X</button></li>
</ul>
</div>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
public keyword: string;
public historyList: any[] = [];
constructor() { }
ngOnInit() {
}
doSearch(){
if(this.historyList.indexOf(this.keyword)==-1){
this.historyList.push(this.keyword);
}
this.keyword = '';
}
deleteHistroy(key){
alert(key);
this.historyList.splice(key,1);
}
}
.search{
width: 400px;
margin: 20px auto;
input{
margin-bottom: 20px;
width: 300px;
height: 32px;
}
button{
height: 32px;
width: 80px;
}
}
四、Angular 中的服務
4.1 服務
定義公共的方法,使得方法在組件之間共享調用
image.png
1. 創建服務命令
ng g service my-new-service
# 創建到指定目錄下面
ng g service services/storage
2. app.module.ts 裏面引入創建的服務
// app.module.ts 裏面引入創建的服務
import { StorageService } from './services/storage.service';
// NgModule 裏面的 providers 裏面依賴注入服務
NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
NewsComponent,
TodolistComponent
], imports: [
BrowserModule,
FormsModule
],
providers: [StorageService],
bootstrap: [AppComponent]
})
export class AppModule { }
3. 使用的頁面引入服務,註冊服務
import { StorageService } from '../../services/storage.service';
constructor(private storage: StorageService) {
}
// 使用
addData(){
// alert(this.username);
this.list.push(this.username);
this.storage.set('todolist',this.list);
}
removerData(key){
console.log(key);
this.list.splice(key,1);
this.storage.set('todolist',this.list);
}
4.2 改造上面的Todo、searchList
searchList
import { Component, OnInit } from '@angular/core';
// 引入服務
import { StorageService } from '../../services/storage.service';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
public keyword: string;
public historyList: any[] = [];
constructor(public storage: StorageService) {
console.log(this.storage.get());
}
ngOnInit() {
// 修改的地方
var searchlist:any=this.storage.get('searchlist');
if(searchlist){
this.historyList=searchlist;
}
}
doSearch(){
if(this.historyList.indexOf(this.keyword)==-1){
this.historyList.push(this.keyword);
// 修改的地方
this.storage.set('searchlist',this.historyList);
}
this.keyword = '';
}
deleteHistroy(key){
alert(key);
this.historyList.splice(key,1);
}
}
TODOLIST
ngOnInit() {
// 修改的地方
var todolist:any=this.storage.get('todolist');
if(todolist){
this.todolist=todolist;
}
}
doAdd(e){
if(e.keyCode==13){
if(!this.todolistHasKeyword(this.todolist,this.keyword)){
this.todolist.push({
title:this.keyword,
status:0 //0表示代辦事項 1表示已完成事項
});
this.keyword='';
// 修改的地方
this.storage.set('todolist',this.todolist); //用到this一定要注意this指向
}else{
alert('數據已經存在');
this.keyword='';
}
}
}
// 修改的地方
checkboxChange(){
console.log('事件觸發了');
this.storage.set('todolist',this.todolist);
}
<h2>todoList</h2>
<div class="todolist">
<input class="form_input" type="text" [(ngModel)]="keyword" (keyup)="doAdd($event)" />
<hr>
<h3>待辦事項</h3>
<ul>
<li *ngFor="let item of todolist;let key=index;" [hidden]="item.status==1">
<!-- add checkboxChange-->
<input type="checkbox" [(ngModel)]="item.status" (change)="checkboxChange()"/> {{item.title}} ------ <button (click)="deleteData(key)">X</button>
</li>
</ul>
<h3>已完成事項</h3>
<ul>
<li *ngFor="let item of todolist;let key=index;" [hidden]="item.status==0">
<!-- add checkboxChange-->
<input type="checkbox" [(ngModel)]="item.status" (change)="checkboxChange()" /> {{item.title}} ------ <button (click)="deleteData(key)">X</button>
</li>
</ul>
</div>
五、Dom 操作以及@ViewChild、 執行 css3 動畫
1. Angular 中的 dom 操作(原生 js)
ngAfterViewInit(){
var boxDom:any=document.getElementById('box'); boxDom.style.color='red';
}
2. Angular 中的 dom 操作(ViewChild)
import { Component ,ViewChild,ElementRef} from '@angular/core';
@ViewChild('myattr') myattr: ElementRef;
<div #myattr></div>
ngAfterViewInit(){
let attrEl = this.myattr.nativeElement;
}
@viewChild
3. 父子組件中通過 ViewChild 調用子組件 的方法
調用子組件給子組件定義一個名稱
<app-footer #footerChild></app-footer>
引入
ViewChild
import { Component, OnInit ,ViewChild} from '@angular/core';
ViewChild
和剛纔的子組件關聯起來
@ViewChild('footerChild') footer
在父組件中調用子組件方法
run(){
this.footer.footerRun();
}
六、Angular 父子組件以及組件之間通訊
父子組件通訊
6.1 父組件給子組件傳值-@input
父組件不僅可以給子組件傳遞簡單的數據,還可把自己的方法以及整個父組件傳給子組件
1. 父組件調用子組件的時候傳入數據
<app-header [msg]="msg"></app-header>
2. 子組件引入 Input 模塊
import { Component, OnInit ,Input } from '@angular/core';
3. 子組件中 @Input 接收父組件傳過來的數據
export class HeaderComponent implements OnInit {
@Input() msg:string
constructor() { }
ngOnInit() {
}
}
4. 子組件中使用父組件的數據
<p>
child works!
{{msg}}
</p>
5. 把整個父組件傳給子組件
通過
this
傳遞整個組件實例
<app-header [home]="this"></app-header>
export class HeaderComponent implements OnInit {
@Input() home:any
constructor() { }
ngOnInit() {
}
}
執行父組件方法
this.home.xxx()
6.2 子組件通過@Output 觸發父組件的方法(瞭解)
1. 子組件引入 Output 和 EventEmitter
import { Component, OnInit ,Input,Output,EventEmitter} from '@angular/core';
2. 子組件中實例化 EventEmitter
@Output() private outer=new EventEmitter<string>(); /*用EventEmitter和output裝飾器配合使用 <string>指定類型變量*/
3. 子組件通過 EventEmitter 對象 outer 實例廣播數據
sendParent(){
// alert('zhixing');
this.outer.emit('msg from child')
}
4. 父組件調用子組件的時候,定義接收事件 , outer 就是子組件的 EventEmitter 對象 outer
<!--$event就是子組件emit傳遞的數據-->
<app-header (outer)="runParent($event)"></app-header>
5. 父組件接收到數據會調用自己的 runParent 方法,這個時候就能拿到子組件的數據
//接收子組件傳遞過來的數據
runParent(msg:string){
alert(msg);
}
6.3 父組件通過@ViewChild 主動獲取子組 件的數據和方法
1. 調用子組件給子組件定義一個名稱
<app-footer #footerChild></app-footer>
2. 引入 ViewChild
import { Component, OnInit ,ViewChild} from '@angular/core';
3. ViewChild 和剛纔的子組件關聯起來
@ViewChild('footerChild') footer;
4. 調用子組件
run(){ this.footer.footerRun();
}
6.4 非父子組件通訊
- 公共的服務
Localstorage
(推薦)Cookie
七、Angular 中的生命週期函數
7.1 Angular中的生命週期函數
- 生命週期函數通俗的講就是組件創建、組件更新、組件銷燬的時候會觸發的一系列的方法。
- 當
Angular
使用構造函數新建一個組件或指令後,就會按下面的順序在特定時刻調用這些 生命週期鉤子方法。 - 每個接口都有唯一的一個鉤子方法,它們的名字是由接口名再加上
ng
前綴構成的,比如OnInit
接口的鉤子方法叫做ngOnInit
.
1. 生命週期鉤子分類
基於指令與組件的區別來分類
指令與組件共有的鉤子
ngOnChanges
ngOnInit
ngDoCheck
ngOnDestroy
組件特有的鉤子
ngAfterContentInit
ngAfterContentChecked
ngAfterViewInit
ngAfterViewChecked
生命週期鉤子
2. 生命週期鉤子的作用及調用順序
1、ngOnChanges
- 當數據綁定輸入屬性的值發生變化時調用
2、ngOnInit
- 在第一次 ngOnChanges
後調用
3、ngDoCheck
- 自定義的方法,用於檢測和處理值的改變
4、ngAfterContentInit
- 在組件內容初始化之後調用
5、ngAfterContentChecked
- 組件每次檢查內容時調用
6、ngAfterViewInit
- 組件相應的視圖初始化之後調用
7、ngAfterViewChecked
- 組件每次檢查視圖時調用
8、ngOnDestroy
- 指令銷燬前調用
3. 首次加載生命週期順序
export class LifecircleComponent {
constructor() {
console.log('00構造函數執行了---除了使用簡單的值對局部變量進行初始化之外,什麼都不應該做')
}
ngOnChanges() {
console.log('01ngOnChages執行了---當被綁定的輸入屬性的值發生變化時調用(父子組件傳值的時候會觸發)');
}
ngOnInit() {
console.log('02ngOnInit執行了--- 請求數據一般放在這個裏面');
}
ngDoCheck() {
console.log('03ngDoCheck執行了---檢測,並在發生 Angular 無法或不願意自己檢測的變化時作出反應');
}
ngAfterContentInit() {
console.log('04ngAfterContentInit執行了---當把內容投影進組件之後調用');
}
ngAfterContentChecked() {
console.log('05ngAfterContentChecked執行了---每次完成被投影組件內容的變更檢測之後調用');
}
ngAfterViewInit() : void {
console.log('06 ngAfterViewInit執行了----初始化完組件視圖及其子視圖之後調用(dom操作放在這個裏面)');
}
ngAfterViewChecked() {
console.log('07ngAfterViewChecked執行了----每次做完組件視圖和子視圖的變更檢測之後調用');
}
ngOnDestroy() {
console.log('08ngOnDestroy執行了····');
}
//自定義方法
changeMsg() {
this.msg = "數據改變了";
}
}
生命週期調用順序
帶
check
的可以對數據做響應操作
<button (click)="changeMsg()">數據改變了</button>
<input type='text' [(ngModel)]="userInfo" />
點擊按鈕/雙向數據綁定此時觸發了以下生命週期。只要數據改變
cheked
可以在
check
做一些操作
ngDoCheck() {
//寫一些自定義的操作
console.log('03ngDoCheck執行了---檢測,並在發生 Angular 無法或不願意自己檢測的變化時作出反應');
if(this.userinfo!==this.oldUserinfo){
console.log(`你從${this.oldUserinfo}改成${this.userinfo}`);
this.oldUserinfo = this.userinfo;
}else{
console.log("數據沒有變化");
}
}
7.2 生命週期鉤子詳解
7.2.1 constructor-掌握
constructor
,來初始化類。Angular
中的組件就是基於class
類實現的,在Angular
中,constructor
用於注入依賴。組件的構造函數會在所有的生命週期鉤子之前被調用,它主要用於依賴注入或執行簡單的數據初始化操作。
import { Component, ElementRef } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h1>Welcome to Angular World</h1>
<p>Hello {{name}}</p>
`,
})
export class AppComponent {
name: string = '';
constructor(public elementRef: ElementRef) {//使用構造注入的方式注入依賴對象
// 執行初始化操作
this.name = 'Semlinker';
}
}
7.2.2 ngOnChanges()
當
Angular
(重新)設置數據綁定輸入屬性時響應。該 方法接受當前和上一屬性值的SimpleChanges
對象 當被綁定的輸入屬性的值發生變化時調用,首次調用一 定會發生在ngOnInit()
之前。
<!-- 父組件中 傳遞title屬性給header子組件 -->
<app-header [title]="title"></app-header>
此時改變
title
會觸發ngOnChanges
生命週期,並且也會觸發
cheked
7.2.3 ngOnInit()--掌握
在
Angular
第一次顯示數據綁定和設置指令/組件的輸入屬性之後,初始化指令/組件。在第一輪ngOnChanges()
完成之後調用,只調用一次。可以請求數據
- 使用 ngOnInit() 有兩個原因:
- 在構造函數之後馬上執行復雜的初始化邏輯
- 在
Angular
設置完輸入屬性之後,對該組件進行準備。有經驗的開發者會認同組件的構建應該很便宜和安全
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'exe-child',
template: `
<p>父組件的名稱:{{pname}} </p>
`
})
export class ChildComponent implements OnInit {
@Input()
pname: string; // 父組件的名稱
constructor() {
console.log('ChildComponent constructor', this.pname);
// Output:undefined
}
ngOnInit() {
console.log('ChildComponent ngOnInit', this.pname);
// output: 輸入的pname值
}
}
7.2.4 ngDoCheck()
檢測,並在發生
Angular
無法或不願意自己檢測的變 化時作出反應。在每個Angular
變更檢測週期中調用,ngOnChanges()
和ngOnInit()
之後。
7.2.5 ngAfterContentInit()
當把內容投影進組件之後調用。第一次
ngDoCheck()
之後調用,只調用一次
7.2.6 ngAfterContentChecked()
每次完成被投影組件內容的變更檢測之後調用。
ngAfterContentInit()
和每次ngDoCheck()
之後調
7.2.7 ngAfterViewInit()--掌握
初始化完組件視圖及其子視圖之後調用。第一 次
ngAfterContentChecked()
之後調用,只調用一次。在這裏可以操作DOM
7.2.8 ngAfterViewChecked()
每次做完組件視圖和子視圖的變更檢測之後調用。
ngAfterViewInit()
和每次ngAfterContentChecked()
之後 調用。
7.2.9 ngOnDestroy()--掌握
當
Angular
每次銷燬指令/組件之前調用並清掃。在這兒反訂閱可觀察對象和分離事件處理器,以防內存泄 漏。在Angular
銷燬指令/組件之前調用。比如:移除事件監聽、清除定時器、退訂Observable
等。
@Directive({
selector: '[destroyDirective]'
})
export class OnDestroyDirective implements OnDestroy {
sayHello: number;
constructor() {
this.sayHiya = window.setInterval(() => console.log('hello'), 1000);
}
ngOnDestroy() {
window.clearInterval(this.sayHiya);
}
}
八、Rxjs 異步數據流編程
8.1 Rxjs介紹
RxJS
是ReactiveX
編程理念的JavaScript
版本。ReactiveX
來自微軟,它是一種針對異步數據 流的編程。簡單來說,它將一切數據,包括HTTP
請求,DOM
事件或者普通數據等包裝成流的形式,然後用強大豐富的操作符對流進行處理,使你能以同步編程的方式處理異步數據,並組合不同的操作符來輕鬆優雅的實現你所需要的功能。
RxJS
是一種針對異步數據流編程工具,或者叫響應式擴展編程;可不管如何解釋 RxJS 其目 標就是異步編程,Angular
引入RxJS
爲了就是讓異步可控、更簡單。RxJS
裏面提供了很多模塊。這裏我們主要給大家講RxJS
裏面最常用的Observable
和 fromEvent
目前常見的異步編程的幾種方法:
- 回調函數
- 事件監聽/發佈訂閱
Promise
Rxjs
8.2 Promise和RxJS處理異步對比
新建一個
services
ng g service services/rxjs
在
services/rxjs.service.ts
中寫以下方法
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class RxjsService {
constructor() { }
// Promise 處理異步
getPromiseData() {
return new Promise(resolve = >{
setTimeout(() = >{
resolve('---promise timeout---');
},
2000);
});
// RxJS 處理異步:
getRxjsData() {
return new Observable(observer = >{
setTimeout(() = >{
observer.next('observable timeout');
},
2000);
});
}
}
// 在其他組件使用服務
import { Component, OnInit } from '@angular/core';
import { RxjsService } from '../../services/rxjs.service';
@Component({
selector: 'app-rxjs',
templateUrl: './rxjs.component.html',
styleUrls: ['./rxjs.component.scss']
})
export class RxjsComponent implements OnInit {
// 注入服務
constructor(public request: RxjsService) {
}
ngOnInit() {
// 調用方法
this.request.getRxjsData().subscribe(data=>{
console.log(data)
})
}
}
- 從上面列子可以看到
RxJS
和Promise
的基本用法非常類似,除了一些關鍵詞不同。Promise
裏面用的是then()
和resolve()
,而RxJS
裏面用的是next()
和subscribe()
Rxjs
相比Promise
要強大很多。 比如Rxjs
中可以中途撤回、Rxjs
可以發射多個值、Rxjs
提供了多種工具函數等等
8.3 Rxjs unsubscribe 取消訂閱
Promise
的創建之後,動作是無法撤回的。Observable
不一樣,動作可以通過unsbscribe()
方法中途撤回,而且Observable
在內部做了智能的處理.
Promise 創建之後動作無法撤回
let promise = new Promise(resolve = >{
setTimeout(() = >{
resolve('---promise timeout---');
},
2000);
});
promise.then(value = >console.log(value));
Rxjs 可以通過 unsubscribe() 可以撤回 subscribe 的動作
let stream = new Observable(observer = >{
let timeout = setTimeout(() = >{
clearTimeout(timeout);
observer.next('observable timeout');
},
2000);
});
let disposable = stream.subscribe(value = >console.log(value));
setTimeout(() = >{
//取消執行 disposable.unsubscribe();
},
1000);
8.4 Rxjs 訂閱後多次執行
- 如果我們想讓異步裏面的方法多次執行,比如下面代碼。
這一點
Promise
是做不到的,對於Promise
來說,最終結果要麼resole
(兌現)、要麼reject
(拒絕),而且都只能觸發一次。如果在同一個Promise
對象上多次調用resolve
方法, 則會拋異常。而Observable
不一樣,它可以不斷地觸發下一個值,就像next()
這個方法的 名字所暗示的那樣。
let promise = new Promise(resolve = >{
setInterval(() = >{
resolve('---promise setInterval---');
},
2000);
});
promise.then(value = >console.log(value));
Rxjs
let stream = new Observable < number > (observer = >{
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.subscribe(value = >console.log("Observable>" + value));
8.5 Angualr6.x之前使用Rxjs的工具函數 map filter
注意:
Angular6
以後使用以前的rxjs
方法,必須安裝rxjs-compat
模塊纔可以使用map
、filter
方法。
angular6
後官方使用的是RXJS6
的新特性,所以官方給出了一個可以暫時延緩我們不需要修 改rsjx
代碼的辦法
npm install rxjs-compat
import {Observable} from 'rxjs'; import 'rxjs/Rx';
let stream = new Observable < any > (observer = >{
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.filter(val = >val % 2 == 0).subscribe(value = >console.log("filter>" + value));
stream.map(value = >{
return value * value
}).subscribe(value = >console.log("map>" + value));
8.6 Angualr6.x 以後 Rxjs6.x 的變化以及 使用
8.6.1 Rxjs 的變化參考
從
Angular5
升級到Angular6
,angular6
相比較於angular5
總體變化不大,但是在RXJS
上面卻有一些變動,下面給大家講講關於Angular6
版本升級和RXJS6
新特性的講解
1. angular6 Angular7中使用以前的rxjs
對於寫了半年多的項目,模塊已經很多了,所以不可能在升級到
angular6
後馬上更新所有代碼關於RXJS6
的新特性,所以官方給出了一個可以暫時延緩我們不需要修改rsjx
代碼的辦法。
npm install --save rxjs-compat
- 優點: 暫時不用改代碼,可以一點點地改,直到改完後吧這個包卸掉
- 缺點: 對於
rxjs6
的rename
的operator
無效,所以,如果有用到rename
的API
,必須手動修改
2. Angular6 以後 RXJS6的變化
RXJS6
改變了包的結構,主要變化在import
方式和operator
上面以及使用pipe()
2.1 Imports 方式改變
image.png
從
rxjs
中類似像導入observable
subject
等的不再進一步導入,而是止於rxjs
,rxjs6
在包的結構上進行了改變
2.2 operator的改變
image.png
總而言之: 類似於創建之類的用的
API
都是從rxjs
引入的,類似於map
之類的操作都是從rxjs/operators
引入的
image.png
2.3 pipeable observable
image.png
2.4 被重新命名的API
image.png
RXJS6
改變了包的結構,主要變化在import
方式和operator
上面以及使用pipe()
import {Observable} from 'rxjs';
import {map,filter} from 'rxjs/operators';
let stream= new Observable<any>(observer => {
let count = 0;
setInterval(() = >{
observer.next(count++);
},
1000);
});
stream.pipe(filter(val = >val % 2 == 0))
.subscribe(value = >console.log("filter>" + value));
stream
.pipe(
filter(val = >val % 2 == 0),
map(value = >{
return value * value
}))
.subscribe(value = >console.log("map>" + value));
8.7 Rxjs 延遲執行
import {
Observable,
fromEvent
}
from 'rxjs';
import {
map,
filter,
throttleTime
}
from 'rxjs/operators';
var button = document.querySelector('button');
fromEvent(button, 'click')
.pipe(throttleTime(1000))
.subscribe(() = >console.log(`Clicked`));
九、Angular 中的數據交互(get jsonp post)
9.1 Angular get 請求數據
Angular5.x
以後get
、post
和和服務器交互使用的是HttpClientModule
模塊。
1. 在 app.module.ts 中引入 HttpClientModule 並注入
import {HttpClientModule} from '@angular/common/http';
imports: [
BrowserModule,
HttpClientModule
]
2. 在用到的地方引入 HttpClient 並在構造函數聲明
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
3. get 請求數據
var api = "http://a.itying.com/api/productlist";
this.http.get(api).subscribe(response => {
console.log(response); });
9.2 Angular post 提交數據
Angular5.x
以後get
、post
和和服務器交互使用的是HttpClientModule
模塊。
1. 在 app.module.ts 中引入 HttpClientModule 並注入
import {HttpClientModule} from '@angular/common/http';
imports: [
BrowserModule,
HttpClientModule
]
2. 在用到的地方引入 HttpClient、HttpHeaders 並在構造函數聲明 HttpClient
import {HttpClient,HttpHeaders} from "@angular/common/http";
constructor(public http:HttpClient) { }
3. post 提交數據
用
express
搭建一個server
// package.json
{
"dependencies": {
"ejs": "^2.5.6",
"express": "^4.15.3",
"socket.io": "^2.0.3",
"body-parser": "~1.17.1"
}
}
// app.js 代碼
var express = require('express');
var app=express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
/*express允許跨域*/
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
if(req.method=="OPTIONS") res.send(200);
else next();
});
//app.use(express.static(path.join(__dirname, 'public')));
app.get('/',function(req,res){
res.send('首頁');
})
app.post('/dologin',function(req,res){
console.log(req.body);
res.json({"msg":'post成功'});
})
app.get('/news',function(req,res){
//console.log(req.body);
res.jsonp({"msg":'這是新聞數據'});
})
app.listen(3000,'127.0.0.1',function(){
console.log('項目啓動在3000端口')
});
// angular代碼
doLogin() {
// 手動設置請求類型
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
var api = "http://127.0.0.1:3000/doLogin";
this.http.post(api, {
username: '張三',
age: '20'
},
httpOptions).subscribe(response = >{
console.log(response);
});
}
9.3 Angular Jsonp 請求數據
1. 在 app.module.ts 中引入 HttpClientModule、HttpClientJsonpModule 並注入
import {HttpClientModule,HttpClientJsonpModule} from '@angular/common/http';
imports: [
BrowserModule,
HttpClientModule,
HttpClientJsonpModule
]
3. 在用到的地方引入 HttpClient 並在構造函數聲明
import {HttpClient} from "@angular/common/http";
constructor(public http:HttpClient) { }
3. jsonp 請求數據
// 接口支持jsonp請求
var api = "http://a.itying.com/api/productlist";
this.http.jsonp(api,'callback').subscribe(response => {
console.log(response); });
9.4 Angular 中使用第三方模塊 axios 請求數據
1. 安裝 axios
cnpm install axios --save
2. 用到的地方引入 axios
import axios from 'axios';
3. 看文檔使用
axios.get('/user?ID=12345').then(function(response) {
// handle success
console.log(response);
}).
catch(function(error) {
// handle error console.log(error);
}).then(function() {
// always executed
});
十、Angular 中的路由
10.1 Angular 創建一個默認帶路由的項目
1. 命令創建項目
ng new angualrdemo08 --skip-install
2. 創建需要的組件
ng g component home
ng g component news
ng g component newscontent
3. 找到 app-routing.module.ts 配置路由
// 引入組件
import { HomeComponent } from './home/home.component';
import { NewsComponent } from './news/news.component';
import { NewscontentComponent } from './newscontent/newscontent.component';
// 配置路由
const routes: Routes = [
{path: 'home', component: HomeComponent},
{path: 'news', component: NewsComponent},
{path: 'newscontent/:id', component: NewscontentComponent},
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
} ];
4. 找到 app.component.html 根組件模板,配置 router-outlet 顯示動態加載的路由
<h1>
<a routerLink="/home">首頁</a> <a routerLink="/news">新聞</a>
</h1>
<router-outlet></router-outlet>
10.2 routerLink 跳轉頁面 默認路由
<a routerLink="/home">首頁</a>
<a routerLink="/news">新聞</a>
//匹配不到路由的時候加載的組件 或者跳轉的路由
{
path: '**', /*任意的路由*/
// component:HomeComponent
redirectTo:'home'
}
10.3 routerLinkActive 設置routerLink 默認選中路由
<h1>
<a routerLink="/home" routerLinkActive="active">首頁</a> <a routerLink="/news" routerLinkActive="active">新聞</a>
</h1>
<h1>
<a [routerLink]="[ '/home' ]" routerLinkActive="active">首頁</a> <a [routerLink]="[ '/news' ]" routerLinkActive="active">新聞</a>
</h1>
.active{
color:red;
}
10.4 routerLink Get傳遞參數
1. 跳轉
<li *ngFor="let item of list;let key=index;">
<!-- <a href="/news-detail?aid=123">{{key}}--{{item}}</a> -->
<a [routerLink]="['/news-detail']" [queryParams]="{aid:key}">{{key}}--{{item}}</a>
</li>
2. 接收參數
import { ActivatedRoute } from '@angular/router';
constructor(public route:ActivatedRoute) { }
this.route.queryParams.subscribe((data)=>{
console.log(data);
})
10.5 動態路由
1.配置動態路由
const routes: Routes = [
{path: 'home', component: HomeComponent},
{path: 'news', component: NewsComponent},
{path: 'newscontent/:id', component: NewscontentComponent},
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
} ];
2. 跳轉傳值
<a [routerLink]="[ '/newscontent/',aid]">跳轉到詳情</a>
<a routerLink="/newscontent/{{aid}}">跳轉到詳情</a>
3. 獲取動態路由的值
import { ActivatedRoute} from '@angular/router';
constructor( private route: ActivatedRoute) { }
ngOnInit() {
console.log(this.route.params);
this.route.params.subscribe(data=>this.id=data.id);
}
10.6 動態路由的 js 跳轉
// 引入
import { Router } from '@angular/router';
// 初始化
export class HomeComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {}
goNews(){
// this.router.navigate(['/news', hero.id]);
this.router.navigate(['/news']);
}
}
// 路由跳轉
this.router.navigate(['/news', hero.id]);
10.7 路由 get 傳值 js 跳轉
1. 引入 NavigationExtras
import { Router ,NavigationExtras} from '@angular/router';
2. 定義一個 goNewsContent 方法執行跳轉,用 NavigationExtras 配置傳參。
goNewsContent() {
let navigationExtras: NavigationExtras = {
queryParams: {
'session_id': '123'
},
fragment: 'anchor'
};
this.router.navigate(['/news'], navigationExtras);
}
3. 獲取 get 傳值
constructor(private route: ActivatedRoute) {
console.log(this.route.queryParams);
}
10.8 父子路由
1. 創建組件引入組件
import { NewsaddComponent } from './components/newsadd/newsadd.component';
import { NewslistComponent } from './components/newslist/newslist.component';
2. 配置路由
{
path: 'news',
component: NewsComponent,
children: [{
path: 'newslist',
component: NewslistComponent
},
{
path: 'newsadd',
component: NewsaddComponent
}]
}
3. 父組件中定義 router-outlet
<router-outlet></router-outlet>