Angular
谷歌开源的JavaScript库,性能比之前最先的版本提升很多,采用Typescript进行,以及新的模式进行开发,所以从第二版本开始,突然大幅下降人数。
技术:typescript的基本使用,es6
Typescript是JavaScript超集,JavaScript内容和es6都可以直接在typescript中使用。
Typescript的代码依然要编译成纯JavaScript,typescript编译工具可以运行在任何的服务器和任何系统上。Typescript开源
Typescript:
1、非常包容,JavaScript
2、定义简单到复杂的一切类型。
3、typescript如果报错,依然可以生成JavaScript文件
4、typescript拥有活跃的社区。
Typscript缺点:
- 学习成本高
- 短期项目,简单的项目,效率低下。
- 跟其他库的兼容性不高。
安装typescript
cnpm install -g typescript
创建后缀为。ts的文件,最后需要通过编译转成js
将TS文件编译成js
tsc index.ts
安装Angular-cli
cnpm install -g @angular/cli
创建项目
ng new angularApp --skip-install
安装模块
npm install
启动开发服务命令
Npm start
启动时候打开浏览器
ng serve --open
创建组件命令
ng g component component/news
组件使用:
<app-news></app-news>
Typescript数据类型
原始数据类型:布尔,数值,字符串,null,undefined;
布尔类型定义:注意直接调用Boolean是可以的
let isDone:boolean = true;
isDone = false;
let isCheck:boolean = Boolean(1)
错误方式:因为new Boolean返回的是1个对象类型
let isCheck:boolean = new Boolean(1)
数值类型定义
let num:number = 1;
let num16:number = 0xf00a;
let notNum:number = NaN;
let infNum:number = Infinity;
字符串类型定义
let username:string = 'laochen';
let age:number = 30;
let juzi:string = `我的年龄是${age}`;
空值:大部分情况下用于定义函数没有返回值,一般不用于定义变量,因为一旦变量定义为void类型,那么就只能赋值undfined,null;
//空值
function hello():void{
console.log('helloworld')
}
let unable:void = undefined;
unable = null;
Null和undefined定义
//null和undefined类型,意义不大
let a:undefined = undefined;
a = null;
let b:null = null;
b = undefined;
注意:null和undefined是所有类型的子类型,void类型是不能赋值给其他类型的,虽然它也只有undefined和null,例如
let username:string = 'laochen';
username = undefined;
let abc:undefined = undefined;
let cba:void = undefined;
username = abc;
//username = cba;//这是不允许,因为cba是void类型
let age:number = 30;
任意值类型
任意值类型(any)用来表示允许任意值类型赋值。
//任意值类型
let admin:any = 'adbc'
admin = 123;
//任意值类型允许访问他任意的属性
let anything:any = 'hello';
console.log(anything.myname);
未申明类型的变量
let something1;
//something1在声明时候未定义类型,也没有赋值,就会被定义为任意值类型
something1 = 'abc';
something1 = 123;
类型推断
//something2在声明的时候虽然未定义类型,但是由于赋值为字符串,typescript有类型推断规则,会将赋值的类型定义成变量的类型
let something2 = 'abc';
//something2 = 123;//会报错
联合类型:表示取值可以取多个类型。
let cat:string|number;
cat = '小猫咪';
cat = 1;
let cat1:string|number = '小猫咪';
Typescript接口
什么是接口?行为的抽象。具体行动需要由类去实现(接口抽象出来的内容)。
相当于定义类的描述。
定义:
interface Person{
name:string;
age:number;
}
let tom:Person = {
name:"tom",
age:16,
}
//约束TOM这个对象,必须和接口一致属性。
//一般接口的首字母大写。
//用接口定义的对象,属性不能多写,也不能少写
接口可选属性:该属性可写,可不写,但不能多写属性,加?即可
//接口属性可选
interface Student{
name:string;
age?:number;
}
let s1:Student = {
name:"小明",
age:16,
}
接口可定义任意的属性
//任意属性
interface Teacher{
name:string;
age?:number;
[propName:string]:any;
}
let t1:Teacher={
name:"老王",
age:35,
school:'清华'
}
只读属性,readonly
interface Cat{
//只读属性,只能进行1次赋值,后面不可以在修改,但是可以获取
readonly name:string;
color:string;
age:number;
}
let c1:Cat = {
name:'波斯猫',
color:"白色",
age:10
}
//c1.name = '中华猫'; //会报错,因为c1.name是只读属性
Typescript函数
typescript会对函数的输入和输出有约束,
输入和输出会定义类型,参数传入(参数定义类型),返回值(定义类型)
//typescript会对函数的输入和输出有约束,
//输入和输出会定义类型,参数传入(参数定义类型),返回值(定义类型)
function sum1(x:number,y:number) :number{
return x+y;
}
let result:number = sum1(3,4);
函数表达式写法
//函数表达式写法
let sum2 = function(x:number,y:number) :number{
return x+y;
}
//这种写法,实质上仅将右侧匿名函数进行了类型的定义。对左边sum2这个变量没有实质的定义;
let sum3:(x:number,y:number) => number = function(x:number,y:number) :number{
return x+y;
}
可选参数,加?
//可选参数
function sumName(firstname:string,lastname?:string):string{
return firstname+lastname;
}
sumName('lao',"chen");
sumName('lao');
//注意:可选参数必须放置到必须参数的后面,可选参数后面不允许放置必须要的参数
//不允许
// function sumName2(lastname?:string,firstname:string,):string{
// return firstname+lastname;
// }
参数默认值
//默认参数
function sumName3(firstname:string='chen',lastname:string='guan'):string{
return firstname+lastname;
}
剩余参数
arguments
来访问所有传入的参数。
//剩余参数
//ES6正常模式
function fnpush(...items){
}
function fnpush1(...items:any[]):string{
return 'abc';
}
重载
//重载
//重载允许1个函数,接收不同数量或者类型的参数
function setAbc(x:number|string):number|string{
return x;
}
function fnAbc(x:boolean):boolean;
function fnAbc(x:number):number;
function fnAbc(x:string):string;
function fnAbc(x:any):any{
if(typeof x==='number'){
return x;
}else{
return x
}
}
fnAbc('1')
fnAbc(false)
类
//定义静态方法
class Animal{
static isAnimal(a){
return a instanceof Animal;
}
}
let dog = new Animal();
Animal.isAnimal(dog);
//typescript静态属性
class Dog{
static age =20;
}
console.log(Dog.age)
//类属性
class Abc{
a = 1;
b = 3;
private c = 4;
constructor(){
this.a = 1;
this.b = 2;
this.c = 3;
}
}
//属性可以有3种访问的修饰符进行修饰,public/private/protect
//public,任何地方都可以访问,默认所有的属性都是public
//private,私有的,只能在类的内容访问,不能类外面访问
//protected,受保护的,protect在子类里可以访问,private是不允许的
class Banana{
private name;
public constructor(name){
this.name = name;
}
}
let aa = new Banana('海南香蕉');
//console.log(aa.name)//不能获取,name是私有属性
//aa.name = '巴黎香蕉' //不能访问,name是私有属性
class Apple{
protected name;
public constructor(name){
this.name = name
}
}
//protect在子类中允许访问。
class BigApple extends Apple{
private constructor(name){
super(name)
console.log(this.name)
}
}
//如果构造函数式私有的将不能实例化
//let bb = new BigApple('aaaa');
//抽象类,抽象类不允许实例化,一般用于继承实现。
abstract class PinkPig{
public name;
constructor(name){
this.name = name
}
//只定义,不实现
public abstract sayHi();
}
class SmallPinkPig extends PinkPig{
sayHi(){
console.log("helloworld")
}
}
Angular_css
注意:Componet.css仅用于组件,src根目录下的style.css全局的样式
Angular模板语法
MVVM设计模式起源于MVC。
- >model:数据(模型)/状态
V->view:视图
C->控制器:交互修改数据的方式
MVVC
M->model
V->view
VM -> 对数据和视图的双向绑定,只要修改数据,vm(框架)就会自动改变视图,视图的交互改变了数据。
Angular插值
语法:{{ ...变量和表达式}}
应用:应用于html内容里,也可以应用于property里
<h1 [innerHtml]="htmlStr"></h1>
Angular 样式
定义的class三种方式,跟VUE一致,会自动的将变量和原有的HTML的CLASS相加
<!-- 定义变量的方式 -->
<h1 class="abc" class="{{classStr}}">class1</h1>
<h1 class="abc" [class]="classStr">class2</h1>
<h1 [attr.class]="classStr">class3</h1>
Class变量类型
<!-- 变量类型 -->
<!-- 字符串模式 -->
<h1 [class]="classStr">class4</h1>
<h1 class="a" [class]="'str abc'">class4</h1>
<!-- 对象模式 -->
<h1 [class]="classObj">class5</h1>
<h1 [class]="{bgBlue:isShow}">class6</h1>
<!-- 数组模式 -->
<h1 [class]="['bgBlue','active','abc']">class7</h1>
<h1 [class]="classArr"></h1>
Style
<!-- style -->
<!-- style表达式类型 -->
<!-- 字符串变量 -->
<h1 [style]="styleStr"></h1>
<!-- 对象模式 -->
<h1 [style]="styleObj"></h1>
<!-- 橙色 -->
<h1 [style.height]="styleObj.width">h</h1>
<h1 [style.width]="colorAbc"></h1>
<h1 [style.width.px]="widthNum"></h1>
事件
<!--
绑定事件:
由等号左侧写小括号加上事件名称,右边写调用的事件函数
-->
<button (click)="changeColor()">改变颜色</button>
<button (click)="changeButton($event)">改变颜色</button>
上面用到的练习代码
isShow = true;
classArr = ['abc','cba','aaa'];
styleStr = "background:skyblue;width:400px;height:400px;";
styleObj = {
background:"pink",
width:'100px',
height:"100px"
};
colorAbc='100px';
widthNum = 200;
changeColor(){
this.styleObj = {
background:"purple",
width:'200px',
height:"300px"
};
}
changeButton(event){
console.log(event)
event.target.style.background = 'green'
}
条件渲染
<!-- 条件渲染 -->
<!-- person如果是广东人,就显示广东人的疫情信息 -->
<div *ngIf="person=='广东人'">
广东:1000人
</div>
<div *ngIf="person=='湖北人'">
湖北:40000人
</div>
<button (click)="togglePerson()">切换身份</button>
<h1>style的条件渲染</h1>
<!--
这里使用的条件渲染,会移除出Document,添加进DOM都会消耗性能。
-->
<!-- 使用STYLE进行条件渲染,如果需要频繁切换内容,那么需要style完成条件渲染-->
<div [style.display]="person=='广东人'?'block':'none'">
广东:1000人
</div>
<div [style.display]="person=='湖北人'?'block':'none'">
湖北:40000人
</div>
<!-- 条件渲染,匹配多种情况 -->
<div [ngSwitch]="homeState">
<div *ngSwitchCase="'睡觉'">卧室</div>
<div *ngSwitchCase="'看电视'">客厅</div>
<div *ngSwitchCase="'吃饭'">餐厅</div>
<div *ngSwitchCase="'发呆'">阳台</div>
<div *ngSwitchDefault>厕所</div>
</div>
<!-- 条件渲染,匹配多种情况 -->
<div [ngSwitch]="orderState">
<div *ngSwitchCase="1">待付款</div>
<div *ngSwitchCase="2">已付款</div>
<div *ngSwitchCase="3">发货</div>
<div *ngSwitchCase="4">已收货</div>
<div *ngSwitchDefault>丢失</div>
</div>
循环渲染
<!-- 列表循环 -->
<ul>
<li *ngFor="let item of arr">{{item}}</li>
</ul>
<!-- 列表循环获取索引值 -->
<ul>
<li *ngFor="let item of arr;let i=index">索引值:{{i}};内容:{{item}}</li>
</ul>
<!-- 将列表的内容传入事件 -->
<ul>
<li *ngFor="let item of arr;let i=index" (click)="choosePerson(item,i)">索引值:{{i}};内容:{{item}}</li>
</ul>
<!-- 循环复杂数组 -->
<ul>
<li *ngFor="let item of students;let key=index">{{key}}-{{item.name}}的爱好是{{item.hobby}}</li>
</ul>
Angular数据双向绑定
导入模块至app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
//导入form模块
import { FormsModule} from '@angular/forms'
import { AppComponent } from './app.component';
import { NewsComponent } from './views/news/news.component';
@NgModule({
declarations: [
AppComponent,
NewsComponent
],
//导入
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
表单的数据绑定
<input type="text" [(ngModel)]="username">
设置临时变量绑定DOM对象
<!-- 表单数据获取 -->
<div>
<label for="">用户名</label>
<input #input1 type="text" >
</div>
<div>
<label for="">密码</label>
<input #input2 type="text" >
</div>
<button (click)="getContent(input1.value,input2.value)">获取账号密码</button>
NgForm获取整个表单的数据,NgForm只能用在表单内
<form action="" #formobj>
<input type="text" [(ngModel)]="username" name="username" >
<input type="text" [(ngModel)]="password" name="password" >
<button (click)='eventFn(formobj)'>按钮</button>
</form>
管道(跟vue过滤器类似)
将对象转换成json格式显示
<h1>{{student | json}}</h1>
将时间转化yy/MM/dd格式
<h1>显示时间:{{time | date:"yy/MM/dd"}}</h1>
转全大写
<h1>用户名: {{username | uppercase}} </h1>
//管道可以转换多次
<h1>{{'abcdefg'|uppercase|lowercase}}</h1>
自定义管道
创建自定义的管道文件
ng g pipe filter/lcUppercase
import { Pipe, PipeTransform } from '@angular/core';
// @Pipe({
// name: 'lcUppercase'
// })
// export class LcUppercasePipe implements PipeTransform {
// transform(value:string,...args:string[]):string {
// if(value=="老陈"){
// return "大帅哥老陈"
// }
// return value ;
// }
// }
@Pipe({
name: 'lcUppercase'
})
export class LcUppercasePipe implements PipeTransform {
transform(value:string,...args:string[]):string {
console.log(args)
return '¥'+value+args[0];
}
}
模板
注意:msg是模板变量,lcUppercase是转换函数,:后面是参数
<h1>{{msg|lcUppercase:'元'}}</h1>
//msg=100
//根据上面的自定义管道得到的结果是 ¥100元
父组件传值给子组件
//在父组件(html)中传参
<app-child [item]="sendchildMsg"></app-child>
//父组件中的(ts)值
sendchildMsg ="这是给子元素的数据,希望在子组件中显示";
//子组件导入Input
import {Input} from '@angular/core';
export class ChildComponent implements OnInit {
//@input的作用是定义模块输入,是用来让父级组件向子组件传递内容。
@Input() item;
}
//子组件(html)
<h1>{{item}}</h1>
//显示的结果是:这是给子元素的数据,希望在子组件中显示
子组件传数据给父组件
//子组件模板(.html)
<button (click)="sendMsg()">发送消息给父组件</button>
//子组件逻辑(.ts)
//Output用于把一个类字段标记为输出属性,并提供配置元数据。 凡是绑定到输出属性上的 DOM 属性,Angular 在变更检测期间都会自动进行更新。
//angular提供了EventEmitter用来触发自定义事件。子指令创建一个 EventEmitter 实例,并将其作为输出属性导出。子指令调用已创建的 EventEmitter 实例中的 emit(payload)方法来触发一个事件,父指令通过事件绑定(eventName)的方式监听该事件,并通过 $event 对象来获取payload对象。
//导入
import {Output,EventEmitter } from '@angular/core';
export class ChildComponent implements OnInit {
//实例化
@Output() childMsg=new EventEmitter()
//自定义事件
sendMsg(){
this.childMsg.emit({msg:"我子组件,这是我发给父组件的消息"})
}
}
//父组件模板
<h1>子组件发来信息:{{getChildMsg}}</h1>
//监听事件,childMsg自定义
<app-child (childMsg)="getEvent($event)"></app-child>
//父组件逻辑
export class AppComponent {
getChildMsg="";
getEvent(event){
console.log(event)
this.getChildMsg = event.msg
}
}
生命周期函数
constructor(){
console.log("组件构造函数调用")
}
ngOnChanges(){
console.log("数据发生变化之时就会调用此函数ngOnChanges")
}
ngOnInit(){
console.log("第一次显示数据绑定和指令输入属性之后,就会调用,只调用一次")
}
ngDoCheck(){
console.log('在ngOnChanges和ngOnInit发生之后,会进行一次检测')
}
ngAfterContentInit(){
console.log('数据内容渲染到视图上之后')
}
ngAfterContentChecked(){
console.log('数据内容渲染到视图检测之后')
}
ngAfterViewInit(){
console.log('完成组件和子组件初始化')
}
ngAfterViewChecked(){
console.log('完成组件和子组件初始化检测后')
}
ngOnDestory(){
console.log("销毁组件")
}
自定义指令
创建指令文件:
ng g directive directive/lcstyle
设置指令内容和功能
//父组件
<h1 [appLcstyle]="'abc'"></h1>
//ElementRef 顾名思义是元素参阅。
其实在实际应用中就是获取视图层的dom元素,借助Augular提供的依赖注入机制,轻松的访问到dom元素。
//子组件Directive
import { Directive,Input,ElementRef } from '@angular/core';
//自定义指令
@Directive({
selector: '[appLcstyle]'
})
export class LcstyleDirective {
@Input() appLcstyle;
constructor(public ref:ElementRef) {
console.log('ref')
//会输出dom元素
}
//生命周期函数
ngOnChanges(){
//console.log(this.appLcstyle)
//console.log(this.ref)
this.ref.nativeElement.className = this.appLcstyle;
this.ref.nativeElement.innerHTML = this.appLcstyle;
this.ref.nativeElement.addEventListener("click",()=>{
this.ref.nativeElement.style.background="pink"
})
}
}
自定义服务:很多组件需要共用的功能抽象出来(类和函数)
1、创建服务文件
ng g service service/stocklist
服务
import { Injectable } from '@angular/core';
import axios from 'axios'
@Injectable({
providedIn: 'root'
})
export class StockListService {
constructor() { }
async getAllStocks(){
let httpUrl = 'http://localhost:8080/stocklist';
let result = await axios.get(httpUrl);
return result.data.data;
}
async superStocks(){
let httpUrl = 'http://localhost:8080/superStock';
let result = await axios.get(httpUrl);
return result.data.data;
}
}
2、导入服务到项目中
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { LcstyleDirective } from './directive/lcstyle.directive';
//将服务导入到项目中
import { StockListService } from './service/stock-list.service'
@NgModule({
declarations: [
AppComponent,
LcstyleDirective
],
imports: [
BrowserModule
],
//导入服务
providers: [
StockListService
],
bootstrap: [AppComponent]
})
export class AppModule { }
使用服务获取数据
import { Component } from '@angular/core';
import axios from 'axios';
import {StockListService} from './service/stock-list.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angularapp';
stockList = []
superStocks = []
//注入服务
constructor( public stockSer:StockListService ){
// let httpUrl = 'http://localhost:8080/stocklist';
// axios.get(httpUrl).then((res)=>{
// console.log(res.data)
// this.stockList = res.data.data.slice(0,10)
// console.log(this.stockList)
// })
stockSer.superStocks().then((res)=>{
this.superStocks = res.slice(0,10)
})
}
async stockFn(){
console.log(123)
let allStocks = await this.stockSer.getAllStocks()
this.stockList = allStocks.slice(0,10)
}
}
页面
<button (click)="stockFn()">获取10股票列表</button>
<h1>预估今日涨停的十只股票</h1>
<div *ngFor="let item of superStocks">
<h1>股票名称:{{item.stockName}}</h1>
<h2>股票代码:{{item.stockCode}}</h2>
<div class="img">
<img src="{{item.dailyChart}}" alt="">
<img src="{{item.hourlyChart}}" alt="">
</div>
</div>
<h1>普通股票</h1>
<div *ngFor="let item of stockList">
<h1>股票名称:{{item.stockName}}</h1>
<h2>股票代码:{{item.stockCode}}</h2>
<div class="img">
<img src="{{item.dailyChart}}" alt="">
<img src="{{item.hourlyChart}}" alt="">
</div>
</div>
路由配置
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {IndexComponent} from './view/index/index.component';
import {AboutComponent} from './view/about/about.component';
import {NewsComponent} from './view/news/news.component'
//配置路由对象
const routes: Routes = [
{
//不需要加/
path:"",
component:IndexComponent
},
{
path:'about',
component:AboutComponent
},
{
path:"news",
component:NewsComponent
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
HTML模板
<!-- 根据路径的变化,显示不同组件 -->
<style>
div>span{
margin: 20px;
padding: 5px 10px;
background-color: skyblue;
}
</style>
<div>
<span [routerLink]="['/']" routerLinkActive="router-link-active" >首页</span>
<span [routerLink]="['/about']" routerLinkActive="router-link-active" >about</span>
<span [routerLink]="['/news']" routerLinkActive="router-link-active" >news</span>
</div>
<router-outlet></router-outlet>
动态路由
{
//动态路由
path:"news/:id",
component:NewsComponent
},
获取动态路由的参数
import { Component, OnInit } from '@angular/core';
//导入route
import {ActivatedRoute} from '@angular/router'
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.less']
})
export class NewsComponent implements OnInit {
title = ""
//注入route
constructor(public route:ActivatedRoute) {
//http://localhost:4200/news/123?search=meinv#abc
//console.log(this)
}
ngOnInit(): void {
//console.log(this)
//let params = this.route.params;
//console.log(params.value.id);
}
goParams(){
//使用参数
this.route.params.subscribe((params)=>{
console.log(params)
this.title = params.id;
})
}
}
子路由
{
path:'admin',
component:AdminComponent,
children:[
{
path:'user',
component:UserComponent
},
{
path:'product',
component:ProductComponent
}
]
},
HTML模板设置
<p>admin works!</p>
<style>
.admin{
display: flex;
height: 600px;
}
.admin .left{
width: 200px;
background: skyblue;
}
.admin .main{
flex:1;
background: lavenderblush;
}
</style>
<div class="admin">
<div class="left">
这是侧边栏
<div [routerLink]="['/admin/user']" routerLinkActive="router-link-active" >user</div>
<div [routerLink]="['/admin/product']" routerLinkActive="router-link-active" >product</div>
</div>
<div class="main">
<router-outlet></router-outlet>
</div>
</div>
编程式导航,JS如何控制路由的跳转
1/依赖注入router
import { Component, OnInit } from '@angular/core';
//导入route,router
import {ActivatedRoute,Router} from '@angular/router'
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.less']
})
export class NewsComponent implements OnInit {
title = ""
//注入route,router
constructor(public route:ActivatedRoute,public router:Router) {
//http://localhost:4200/news/123?search=meinv#abc
console.log(this)
this.route.data.subscribe((data)=>{
console.log(data)
this.title = data.msg
})
}
ngOnInit(): void {
//console.log(this)
//let params = this.route.params;
//console.log(params.value.id);
}
goParams(){
this.route.params.subscribe((params)=>{
console.log(params)
this.title = params.id;
})
}
goHome(){
//第一个参数是传入数组(路径的数组)
this.router.navigate([''],{
queryParams:{
usernam:"admin"
},
fragment:"abc",
replaceUrl:true
})
}
}
//第一个参数是传入数组(路径的数组),自动拼接成完整的路径
this.router.navigate([''],{
//拼接参数
queryParams:{
usernam:"admin"
},
fragment:"abc",//传hash值
//是不是要替换当前的页面
replaceUrl:true
})