# Angular 筆記
###### tags: `angular`,`front-end`
## 鐵人賽
2022
[Angular Material完全攻略](https://ithelp.ithome.com.tw/users/20020617/ironman/1263)
[Angular TDD 測試從0到1](https://ithelp.ithome.com.tw/users/20151610/ironman/5335)
[今天我想來在 Angular 應用程式上加上測試保護](https://ithelp.ithome.com.tw/users/20109645/ironman/5708)
[angular專案開發指南](https://ithelp.ithome.com.tw/users/20151497/ironman/5797)
[Angular牙起來](https://ithelp.ithome.com.tw/users/20125192/ironman/5938)
2021
[Angular 學徒之路 feat. TypeScript](https://ithelp.ithome.com.tw/users/20140183/ironman/3996)
2018
[Angular 大師之路](https://ithelp.ithome.com.tw/users/20020617/ironman/1630)
[Angular 深入淺出三十天](https://ithelp.ithome.com.tw/users/20090728/ironman/1600)
---
## 環境設定與引用
### basics
https://angular.io/guide/setup-local
https://ithelp.ithome.com.tw/articles/10203185
安裝nodejs
https://nodejs.org/en/
(選用)安裝nvm
https://www.casper.tw/development/2022/01/10/install-nvm/
https://github.com/nvm-sh/nvm
安裝angular cli
```c!
npm install -g @angular/cli
```
檢查angular cli版本
```c!
ng version
```
建立新專案
```c!
ng new project_name
```
運行專案
```c!
ng serve --open
```
(選用)安裝vscode extension:
Angular Extension Pack
(在vscode搜尋angular will可找到保哥的版本)
(選用)安裝chrome extension:
Angular DevTools
https://chrome.google.com/webstore/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh
(適用angular9之前的版本)augury
https://augury.rangle.io/
(選用)啟用ESLint
https://github.com/angular-eslint/angular-eslint
https://blog.miniasp.com/post/2021/08/29/Angular-ESLint-with-so-much-details
#### 資料綁定
https://ithelp.ithome.com.tw/articles/10204430
### 重新命名專案
https://stackoverflow.com/questions/45891832/how-to-rename-an-angular-project
1.Modify Project name in angular.json file (Replace all occurrences of the old name in angular.json)
2.Rename Project folder name.
3.Delete node_modules folder from your project directory.
4.Run npm install command.
5.Finally run ng serve command.
### 在預設scss檔引用外部css
src/styles.scss
```css!
@import url('assets/css/style.css');
```
## pipe
https://angular.io/guide/pipes
### 網路教學
https://ithelp.ithome.com.tw/articles/10204799
### uppercase :將英文字母大寫
```htmlembedded!
<div>{{ name|uppercase }}</div>
```
### date :修改日期格式
```htmlembedded!
{{ item.UpdateTime | date: 'yyyy-MM-dd HH:mm:ss' }}
```
## Routing
### 基礎用法
https://ithelp.ithome.com.tw/articles/10241384
html
```htmlembedded=
<a [routerLink]="['/page1']">page1</a>
<a [routerLink]="[ '/page2']">page2</a>
<router-outlet></router-outlet>
```
src/app/app-routing.module.ts
```typescript=
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { Page1Component } from "./page1/page1.component";
import { Page2Component } from "./page2/page2.component";
const routes: Routes = [
{
path: "page1",
component: Page1Component,
},
{
path: "page2",
component: Page2Component,
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
```
## component
### basics
#### 建立與引用component
建立
```
ng g c ttt
```
執行完上述指令後會出現一個ttt資料夾.
找到ttt.component.ts,
```typescript!
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-ttt', //<----這行就是用來在其他component呼叫用的tag名稱
templateUrl: './ttt.component.html',
styleUrls: ['./ttt.component.scss']
})
export class TttComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
```
在其他component的html引用:
```htmlembedded!
<app-ttt></app-ttt>
```
### 傳遞純字串至component屬性
https://stackoverflow.com/questions/36220027/how-to-pass-a-string-value-to-a-component-in-angular2
```htmlembedded=
<component inputField="string"></component>
<component [inputField]="'string'"></component>
<component inputField="{{'string'}}"></component>
```
### attribute component
使用此方式建立的component,
render時不會多出一個tag
有些情況下,比如說table tr,
多出來的tag會破壞css版面.
https://codeburst.io/display-a-table-using-components-with-angular-4-f13f0971666d
app.component.html
```htmlembedded!
<table class="w-full">
<tr class="odd:bg-gray-300 w-full"
app-item
*ngFor="let todo of todos; let i=index;"
[todo]="todo" [i]="i"></tr>
</table>
```
item.component.ts
```typescript!
....
@Component({
//重點在這裡,只要加上中括號,
//這個component就是attribute component
selector: '[app-item]',
templateUrl: './item.component.html',
styleUrls: ['./item.component.scss']
})
export class ItemComponent implements OnInit {
editable = false;
@Input() todo!: any;
@Input() i!: any;
constructor() { }
ngOnInit(): void {
}
}
```
## animations
### basics
#### ex1
app.module.ts
```typescript!
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
...
@NgModule({
declarations: [
...
],
imports: [
...
BrowserAnimationsModule,
],
...
})
```
component ts
```typescript!
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-ttt',
templateUrl: './ttt.component.html',
styleUrls: ['./ttt.component.scss'],
animations: [
trigger('divState', [
state('shown', style({
opacity: 1,
height: '*'
})),
state('hidden', style({
opacity: 0,
height: 0
})),
transition('shown => hidden', [
animate('0.2s')
]),
transition('hidden => shown', [
animate('0.2s')
]),
])
]
})
export class TttComponent implements OnInit {
divState = 'hidden';
toggleShow(){
this.divState = this.divState === 'shown' ? 'hidden' : 'shown';
}
}
```
html
```htmlembedded!
<button (click)="toggleShow()">click me</button>
<div [@divState]="divState" class="my-div">some text here</div>
```
## directive
### ngIf
#### basics
```htmlembedded!
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<button (click)="toggleDiv()">Toggle Div</button>
<div *ngIf="showDiv">
This is a div that will be toggled when the button is clicked.
</div>
`
})
export class AppComponent {
showDiv = false;
toggleDiv() {
this.showDiv = !this.showDiv;
}
}
```
### ngModel簡例
src/app/app.component.html
```htmlembedded!
<input type="text" [(ngModel)]="newTodo">
```
src/app/app.component.ts
```javascript!
...
export class AppComponent {
newTodo:string;
}
...
```
src/app/app.module.ts
```javascript!
...
//在import那裏引用FormsModule後,
//vs code會自動加入這一行
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
//需加入以下這行
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
```
### ng-container搭配 *ngIf避免產生額外element
https://stackoverflow.com/questions/36376001/using-ngif-without-an-extra-element-in-angular-2
```htmlmixed=
<ng-container *ngIf="some expression">
```
### ngFor
#### 基本用法
```htmlembedded=
<app-todo *ngFor="let todo of todos;let i =index" [todo]="todo"></app-todo>
```
## 事件
### click帶入$event為參數
```htmlembedded!
<button (click)="ttt($event)">click me</button>
```
### 消去input內的值
https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning#add_items_to_the_list
html
```htmlembedded=
<input
#newItem
(keyup.enter)="urFnName(newItem.value); newItem.value = ''"
/>
```
### click基本範例
src/app/app.component.html
```htmlembedded!
<button (click)="fn1()">click me</button>
```
src/app/app.component.ts
```javascript!
...
export class AppComponent {
fn1(){
return console.log('button clicked');
}
}
```
## 套件
### bootstrap
#### 安裝
https://ithelp.ithome.com.tw/articles/10272766
https://hychen39.github.io/angular/2021/03/23/u18_bootstrap_modal_dialog.html
```c!
npm i bootstrap@4 jquery popper.js --save
```
angular.json
```jsonld!
...
"build": {
...
"options": {
...
"styles": [
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.scss"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/popper.js/dist/umd/popper.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]
},
```
app.component.ts
```typescript!
import { Component } from '@angular/core';
declare var $: any;//加上這行
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'app-1211';
//使用popover需加上以下這段
ngOnInit(): void {
// Init the popover everywhere
// See: https://getbootstrap.com/docs/4.6/components/popovers/#example-enable-popovers-everywhere
$(function () {
$('[data-toggle="popover"]').popover()
})
}
}
```
#### 還原bootstrap4動畫效果
https://hackmd.io/fFPUrmrbSIChs2Z46ijXSA
新建src/assets/css/force-bootstrap-transition.css
內容同上面連結
修改src/styles.scss
```css!
@import url('assets/css/force-bootstrap-transition.css');
```
### tailwind
#### 安裝
https://tailwindcss.com/docs/guides/angular
```clike!
$npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
```
tailwind.config.js
```javascript!
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: 'tw-', /*選擇性,如果跟bootstrap等其他套件一同使用可加上,避免class name相衝突*/
content: [
"./src/**/*.{html,ts}",
],
theme: {
extend: {},
},
plugins: [],
}
```
*如果同時使用scss:
src/assets/style.css
```css!
@tailwind base;
@tailwind components;
@tailwind utilities;
```
src/styles.scss
```sass!
@import url('assets/style.css');
```
### angular material
https://material.angular.io/
#### 安裝
```csharp
ng add @angular/material
```
#### dialog
##### 基本用法
https://www.youtube.com/watch?v=3ldV9EYyfv4
引用dialog元件
app.module.ts
```typescript=
import {MatDialogModule} from '@angular/material/dialog';
...
@NgModule({
declarations: [
...
],
imports: [
...
MatDialogModule,
],
...
```
新增兩個component
```css=
$ng g c dialog
$ng g c dialog-content
```
概要:
在dialogComponent寫按鈕開啟dialog,
在dialogContentComponent裡面寫dialog內容
dialog.component.html
```htmlembedded=
<button mat-raised-button (click)="openDialog()">
click me</button>
```
dialog.component.ts
```typescript=
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { DialogContentComponent } from './dialog-content/dialog-content.component';
@Component({
selector: '[app-dialog]',
templateUrl: './dialog.component.html',
styleUrls: ['./dialog.component.scss']
})
export class DialogComponent implements OnInit {
constructor(public dialog:MatDialog) { }
ngOnInit(): void {
}
openDialog(){
const dialogRef=this.dialog.open(DialogContentComponent,{
data:{
title:'Hello!',
}
});
dialogRef.afterClosed().subscribe(
result=>console.log('Dialog result',result)
)
}
}
```
dialog-content.component.html
```htmlembedded=
<h2 mat-dialog-data>{{ data?.title }}</h2>
<div mat-dialog-content>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Nostrum sequi ad commodi aliquid magnam illum! Iusto doloremque facere non? Ullam odio aperiam odit nihil dolorem praesentium assumenda fugit nesciunt illum!
</div>
<div mat-dialog-actions>
<button mat-button mat-dialog-close>Cancel</button>
<button mat-button [mat-dialog-close]="'dialog closed'">Close</button>
</div>
```
dialog-content.component.ts
```typescript=
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-dialog-content',
templateUrl: './dialog-content.component.html',
styleUrls: ['./dialog-content.component.scss']
})
export class DialogContentComponent implements OnInit {
constructor(@Inject(MAT_DIALOG_DATA) public data:any) { }
ngOnInit(): void {
}
}
```
## deploy
### github pages
https://github.com/angular/angular-cli/issues/3557
https://medium.com/swlh/how-to-deploy-an-angular-app-to-github-pages-without-using-any-libraries-step-by-step-guide-cfe96fb0c879
https://medium.com/%E9%80%B2%E6%93%8A%E7%9A%84-git-git-git/%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-%E7%94%A8github-pages-%E4%B8%8A%E5%82%B3%E9%9D%9C%E6%85%8B%E7%B6%B2%E7%AB%99-fa2ae83e6276
兩個重點:
1.
建構專案時,加上 base-href:
```
ng build --base-href ur_project_name/
```
或者是在<head></head>直接在build好的index.html
手動加上路徑:
```htmlembedded
<base href="/ur_project_name/">
```
加上這各地原因是避免js跟css路徑跑掉
2.
修改angular.json
```json=
...
"architect": {
...
"options": {
"outputPath": "docs",
...
```
在github設定中點settings,
找到pages,
把branches右邊那個root改成docs
## cmd
### 常用指令
啟動伺服器
ng serve --open
編譯檔案
ng build