Angular学习笔记

angular 的优点

  • 组织化前端结构
  • 强大和新颖
  • 完整的解决方案(路由、HTTP、RxJS等)
  • 构建强大单页应用
  • MVC设计模式
  • Tyepscript
  • 极好的cli工具

预备知识

  • Typescript
  • Classes
  • 高阶函数 - forEach、map、filter
  • 箭头函数
  • Promise
  • MVC设计模式

the angular way

  • 使用Typescript(变量、函数、参数)
  • 基于组件component based
  • 使用service来共享组件间的数据/功能
  • modules的概念(root module, forms modules, http module, etc)
  • 使用RxJS的observables来异步操作。(使用内置的HTTP模块发送请求,在组件中订阅返回的Observables)
  • 较陡的学习曲线

angular 基本用法

创建项目

ng new myapp

启动项目

ng serve

打包项目

ng build

创建组件

ng generate component todos

创建服务

ng generate service todo

创建模块

ng generate module <moduleName>

在Angular的大型应用中可以使用ngrxRedux等状态管理工具

项目准备

安装angular
sudo npm install -g @angular/cli
验证安装
ng --version
新建项目
ng new todolist
运行项目
ng serve --open

index.html

单页应用入口,可以在这个文件中引入CDN,<app-root>是组件的根标签

angular.json

项目配置文件,如打包目录(outputPath),静态资源目录(assets),样式目录(styles)

app.module.ts

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular中所有module必须导入后才能使用,如果使用cli创建模块,会自动进行导入

  • imports 使其他模块的导出声明在当前模块中可用

  • declarations 使当前模块中的指令(包括组件和管道)可用于当前模块中的其他指令

  • providers依赖注入知道servicesvalue,它们添加到root scope,并且注入到依赖它们的服务或指令

  • bootstrap数组声明哪些组件需要插入到index.html

将多个组件插入到index.html

index.html

<app-root></app-root>
<test-root></test-root>

test.component.ts

import { Component } from "@angular/core";

@Component({
  selector: "test-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class TestComponent {
  title = "test";
}

app.module.ts

// ...
import { TestComponent } from "./test.component";
@NgModule({
  declarations: [AppComponent, TestComponent],
  imports: [BrowserModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent, TestComponent],
})
export class AppModule {}

基础语法

创建组件
ng generate component components/Todos
ng g c components/TodoItem
// 创建服务
ng g s services/Todo

该命令会在components文件夹下创建新的组件

属性绑定

[属性名]="属性值"

app/components/todo-item/todo-item.component.html

<div [ngClass]="setClasses()">
  <p>
    <input
      (change)="onToggle(todo)"
      type="checkbox"
      [checked]="todo.completed"
    />
    {{ todo.title }}
    <button (click)="onDelete(todo)" class="del">x</button>
  </p>
</div>
*ngFor循环语句

app/components/todos/todos.component.html

<app-todo-item *ngFor="let todo of todos" [todo]="todo"> </app-todo-item>
向子组件传参

[参数名]="参数值"向子组件传值

app/components/todos/todos.component.html

<app-todo-item *ngFor="let todo of todos" [todo]="todo"> </app-todo-item>

[todo]="todo"app-todo-item组件传值

接收参数

app/components/todo-item/todo-item.component.ts

export class TodoItemComponent implements OnInit {
  @Input() todo: Todo;
}

在模板中使用参数

app/components/todo-item/todo-item.component.html

<div [ngClass]="setClasses()">
  <p>
    <input
      (change)="onToggle(todo)"
      type="checkbox"
      [checked]="todo.completed"
    />
    {{ todo.title }}
    <button (click)="onDelete(todo)" class="del">x</button>
  </p>
</div>
向父组件传参

app/components/todo-item/todo-item.component.ts

import { Component, OnInit, Input, EventEmitter, Output } from "@angular/core";
import { Todo } from "../../models/Todo";
import { TodoService } from "../../services/todo.service";
@Component({
  selector: "app-todo-item",
  templateUrl: "./todo-item.component.html",
  styleUrls: ["./todo-item.component.css"],
})
export class TodoItemComponent implements OnInit {
  // 从父组件接收参数
  @Input() todo: Todo;
  // 向父组件传递参数
  @Output() deleteTodo: EventEmitter<Todo> = new EventEmitter();
  constructor(private todoService: TodoService) {}

  ngOnInit(): void {}
  setClasses() {
    let classes = {
      todo: true,
      "is-completed": this.todo.completed,
    };
    return classes;
  }

  // onToggle
  onToggle(todo) {
    // Toggle on UI
    todo.completed = !todo.completed;
    // Toggle on server
    this.todoService.toggleCompleted(todo).subscribe((todo) => {
      console.log(todo);
    });
  }
  // 通过 emit 向父组件传参
  onDelete(todo) {
    this.deleteTodo.emit(todo);
  }
}

app/components/todos/todos.component.html

<app-todo-item
  *ngFor="let todo of todos"
  [todo]="todo"
  (deleteTodo)="deleteTodo($event)"
>
</app-todo-item>

(deleteTodo)监听这个事件的名称和子组件的EventEmitter对象名必须一致

ngClass动态添加类

在HTML元素上添加或者移除CSS类

app/components/todo-item/todo-item.component.html

<div [ngClass]="setClasses()">
  <p>
    <input type="checkbox" />
    <button class="del">x</button>
  </p>
</div>

其中setClasses()是脚本文件中的一个方法

app/components/todo-item/todo-item.component.ts

export class TodoItemComponent implements OnInit {
  @Input() todo: Todo;
  constructor() {}

  ngOnInit(): void {}
  setClasses() {
    let classes = {
      todo: true,
      "is-completed": this.todo.completed,
    };
    return classes;
  }
}
监听事件

app/components/todo-item/todo-item.component.html

<div [ngClass]="setClasses()">
  <p>
    <input (change)="onToggle(todo)" type="checkbox" />
    {{ todo.title }}
    <button (click)="onDelete(todo)" class="del">x</button>
  </p>
</div>

(change)监听输入框改变事件,(click)监听元素点击事件

网络请求

导入HTTP请求模块

app.module.ts

import { HttpClientModule } from "@angular/common/http";
@NgModule({
  declarations: [AppComponent, TodosComponent, TodoItemComponent],
  imports: [BrowserModule, AppRoutingModule, HttpClientModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

services发送请求

app/services/todo.service.ts

import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Todo } from "../models/Todo";
import { Observable } from "rxjs";
@Injectable({
  providedIn: "root",
})
export class TodoService {
  todoUrl: string = "https://jsonplaceholder.typicode.com/todos?_limit=5";
  constructor(private http: HttpClient) {}
  getTodos(): Observable<Todo[]> {
    return this.http.get<Todo[]>(this.todoUrl);
  }
}
表单

导入表单模块

app.module.ts

import { FormsModule } from "@angular/forms";

@NgModule({
  declarations: [
    AppComponent,
    TodosComponent,
    TodoItemComponent,
    HeaderComponent,
    AddTodoComponent,
  ],

  imports: [BrowserModule, AppRoutingModule, HttpClientModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

在表单元素中使用双向数据绑定

app/components/add-todo/add-todo.component.html

<form class="form" (ngSubmit)="onSubmit()">
  <input
    type="text"
    name="title"
    [(ngModel)]="title"
    placeholder="Add Todo..."
  />
  <input type="submit" value="submit" class="btn" />
  {{ title }}
</form>

[(ngModel)]用于双向数据绑定

(ngSubmit)监听表单提交事件

监听表单提交事件

app/components/add-todo/add-todo.component.ts

import { Component, OnInit, Output, EventEmitter } from "@angular/core";

@Component({
  selector: "app-add-todo",
  templateUrl: "./add-todo.component.html",
  styleUrls: ["./add-todo.component.css"],
})
export class AddTodoComponent implements OnInit {
  title: string;
  @Output() addTodo: EventEmitter<any> = new EventEmitter();

  constructor() {}

  ngOnInit(): void {}
  onSubmit() {
    const todo = {
      title: this.title,
      completed: false,
    };
    // 触发 addTodo 事件
    this.addTodo.emit(todo);
  }
}

表单提交事件处理

**app/components/todos/todos.component.html **

<app-add-todo (addTodo)="addTodo($event)"></app-add-todo>
<app-todo-item
  *ngFor="let todo of todos"
  [todo]="todo"
  (deleteTodo)="deleteTodo($event)"
>
</app-todo-item>

app/services/todo.service.ts

import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Todo } from "../models/Todo";
import { Observable } from "rxjs";
const httpOptions = {
  headers: new HttpHeaders({
    "Content-Type": "application/json",
  }),
};
@Injectable({
  providedIn: "root",
})
export class TodoService {
  todoUrl: string = "https://jsonplaceholder.typicode.com/todos";
  todoLimit = "?_limit=5";

  constructor(private http: HttpClient) {}
  getTodos(): Observable<Todo[]> {
    return this.http.get<Todo[]>(`${this.todoUrl}${this.todoLimit}`);
  }
  // Toggle Completed
  // 发送json类型数据必须带请求头
  toggleCompleted(todo: Todo): Observable<any> {
    const url = `${this.todoUrl}/${todo.id}`;
    return this.http.put(url, todo, httpOptions);
  }
  deleteTodo(todo: Todo): Observable<Todo> {
    const url = `${this.todoUrl}/${todo.id}`;
    return this.http.delete<Todo>(url, httpOptions);
  }
  addTodo(todo: Todo): Observable<Todo> {
    return this.http.post<Todo>(this.todoUrl, todo, httpOptions);
  }
}
路由

app/app-routing.module.ts

import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { TodosComponent } from "./components/todos/todos.component";
import { AboutComponent } from "./components/about/about.component";
const routes: Routes = [
  {
    path: "",
    component: TodosComponent,
  },
  {
    path: "about",
    component: AboutComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

app/app.component.html

<app-header></app-header>
<!-- <app-todos></app-todos> -->
<router-outlet></router-outlet>

路由跳转

app/components/layout/header/header.component.html

<header class="header">
  <h1>TODO</h1>
  <a routerLink="/">Home</a>
  <a routerLink="/about">About</a>
</header>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350