# 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