owned this note
owned this note
Published
Linked with GitHub
# ANGULAR
## Other
### npm
node_modules 砍掉重來
```shell
rm -rf node_modules
npm cache clean
npm install
```
### CSS
```css
:host {
color: white;
}
```
設定該個 component 的 style
### assets
`src/assets/css/myfile.css`
`<link rel="stylesheet" href="assets/css/myfile.css">`
### style guide
https://angular.io/docs/ts/latest/guide/style-guide.html
put all app code in folder `src`
Feature modules
Shared feature module ( 不要在這提供 service )
Core feature module ( 只在 app.module.ts import 他 )
### angular cli
https://github.com/angular/angular-cli
`ng new { project name }`
--style=scss 使用 scss 當作 default style file
ng build --prod --aot
### angular material
[doc](https://material.angular.io/)
[color](https://material.io/guidelines/style/color.html#color-color-palette)
### daemonite material
`npm install --save daemonite-material`
in `style.css` import the css file
`@import '../node_modules/daemonite-material/css/material.min.css';`
### jquery global $
`npm install --save @types/jquery`
in `tsconfig.json`
```
"compilerOptions": {
...
"types": [
"jquery"
]
}
```
If `src/tsconfig.app.json` or `src/tsconfig.spec.json` use extends and then override the types option, you should add it again in those file
### ngx-translate
https://github.com/ngx-translate
https://www.npmjs.com/package/@ngx-translate/core
`npm install @ngx-translate/core --save`
`npm install @ngx-translate/http-loader --save`
in `app.module.ts`
```typescript
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
export function httpLoaderFactory(http: Http) {
return new TranslateHttpLoader(http);
}
@NgModule({
...,
imports: [
...,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: httpLoaderFactory,
deps: [ Http ],
},
}),
]
})
```
in `app.component.ts`
```typescript
import { TranslateService } from '@ngx-translate/core';
export class ... {
constructor(private translate: TranslateService){
translate.setDefaultLang('chinese');
const nowLang = cookie.getCookie('language');
if (!nowLang) cookie.setCookie('language', 'chinese');
}
ngDoCheck() {
this.translate.use(this.cookie.getCookie('language'));
}
}
```
using translate in `some.component.ts`
```typescript
construtor( private translate: TranslateService ){}
this.translate.get( 'some' ).subscribe(
( message: string ) => { /* do something with message */ }
);
```
## API
### ngFor
`*ngFor="let some of somes; let i = index"`
如果 ngFor 要跟其他 api 合用,因為 [] 不能跟 {{}} 合用 =>
- 合成函式:`get self() { return this; }`
- 使用:`[(ngModel)]="self['answer'+i]"`
## Module
BrowserModule 包含 CommonModule 還有一些 app 啟動需要的東東 ( 只在 `app.module.ts` import 他 )
### Lazy Loading
in `app-routing.module`
`{ path: "some", loadChildren: "{path name}#{class name}" }`
in `some-routing.module`
path 變成 `/` 就好
```
{ path: "/", component: SomeComponent, children: [ ... ] }
```
## Service
Service is just a class
```typescript
export class SomeService { }
```
In where you want to inject
angular 在哪裡創立這個 instance
`providers: [ SomeService ]`
In where you want to use service
這會和 angular 要 SomeService 的 instance
`constructor( private someService: SomeService ){ }`
Hierarchical :
AppModule > AppComponent > Other-Component
加在 AppModule 整個 App 共用一個 Service instance
加在 AppComponent 所有 Component 共用一個 Service instance ( 不包括其他 Service )
加在 Other-Component 所有他的小孩共用一個 Service instance
如果要在 Service 中用另一個 Service 要加 @Injectable
```typescript
import { Injectable } from '@angular/core';
@Injectable
export class SomeService { }
```
## Routes
in `app-routing.module`
```typescript
const appRoutes: Routes = [
{ path: 'some', component: SomeComponent }
]
@NgModule({
imports: [ RouterModule.forRoot(appRoutes,{ preloadingStrategy: PreloadAllModules, useHash: true }) ],
exports: [ RouterModule ]
})
export class ... (){}
```
useHash set to **true** will make url becomes `https://my-website/#/some-page`
in `app.module.ts` import the class
in `app.component.html`
`<router-outlet></router-outlet>`
### forChild
```typescript
const childRoutes: Routes = [
...
]
@NgModule({
imports: [ RouterModule.forChild(childRoutes) ],
exports: [ RouterModule ]
})
export class ... (){}
```
### routerLink
`<a routerLink="/some"></a>`
`<a [routerLink]="['/some']"></a>`
### routerLinkActive
`routerLinkActive="someClass"`
`[routerLinkActiveOptions]="{ exact: true }"`
### navigate
`constructor( private router: Router ){ }`
`this.router.navigate(['/some']);`
`constructor( private route: ActivatedRoute ){ }`
`this.router.navigate(['/some'], { relativeTo: this.route });`
### parameter ( 要寫在 ngOnInit 裡 )
`{ path: 'some/:id', component: SomeComponent }`
`constructor( private route: ActivatedRoute ){ }`
`this.id = this.route.snapshot.params['id'];`
```typescript
this.route.params.subscribe(
( params: Params ) => {this.id = params['id']; }
);
```
### query string and fragment ( hash tag )
`[queryParams]="{ some: 10 }"`
`fragment="someTag"`
```typescript
this.router.navigate(
['/some'],
{ queryParams: { some: '10' }, fragment: 'someTag' }
);
```
`this.route.snapshot.queryParams['some'];`
`this.route.snapshot.fragment;`
`this.route.queryParams.subscribe( ... )`
`this.route.fragment.subscribe( ... )`
### 404 page not found
`{ path: 'page-not-found', component: PageNotFoundComponent }`
`{ path: '**', redirectTo: '/page-not-found' }`
### guard
```typescript
canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observalbe<boolean> | Promise<boolean> | boolean {
return true;
}
canActivateChild( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observalbe<boolean> | Promise<boolean> | boolean {
return this.canActivate();
}
```
`{ path: 'some', canActivate: [ AuthGuard ], component: SomeComponent }`
`{ path: 'some', canActivateChild: [ AuthGuard ], component: SomeComponent, children: [ ... ] }`
```typescript
import { canDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
export interface CanComponentDeactivate {
canDeactivate: () => boolean;
}
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate( component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot ): boolean {
return component.canDeactivate();
}
}
```
`{ path: 'some', canDeactivate: [ CanDeactivateGuard ], component: SomeComponent }`
`import { CanComponentDeactivate } from './can-deactivate-guard.service'`
`implements CanComponentDeactivate`
`canDeactivate(): boolean { ... }`
## Forms
### Template Driven Forms
```html
<form (ngSubmit)="somefunction(f)" #f="ngForm">
<div ngModelGroup="some-group" #data="ngModelGroup">
<select [ngModel]="'some1'" name="some-select">
<option value="some1">some1</option>
<option value="some2">some2</option>
</select>
<input
name="some-input"
[(ngModel)]="answer"
required
email
#email="ngModel"
pattern="^some regex here$"
>
<span *ngIf="email.invalid && email.touched">input invalid!</span>
<p>{{ answer }}</p>
</div>
<div class="radio-style" *ngFor="let some of somes">
<label>
<input name="some" ngModel [value]="some">
{{ some }}
</label>
</div>
<button [disabled]="f.invalid"></button>
</form>
```
```typescript
export class SomeComponent {
@ViewChild('f') some_form: NgForm;
some_function(){
this.some_form.setValue({ ... });
this.some_form.form.patchValue({ ... });
this.some_form.reset({ ... });
}
}
```
```css
input.ng-invalid.ng-touched {
/* some warning style */
}
```
### Model Driven Forms
需要在 module 裡 import ReactForm
```typescript
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@ngModule({
imports: [
FormsModule,
ReactiveFormsModule,
...
],
...
});
export class ... {
}
```
在需要的 TS 裡
```typescript
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
export class AppComponent {
myForm: FormGroup;
// 寫在 form 出現之前就 ok 了(可能在 constructor, ngOnInit, ...)
initForm() {
this.myForm = new FormGroup({
'a': new FormControl(null), // 可以是空的
'b': new FormControl('b'), // 可以有值
'c': new FormControl(null, Validators.required), // 可能需要 validator
'd': new FormControl('d', [Validators.required, Validators.minLength(3), Validators.maxLength(10)]) // validator 可以多個
'e': ['', Validators.pattern('[A-Za-z]{5}')], // 也可以這樣寫,好處是會變成很單純的 json 格式
'f': new FormArray([]) // 或者可能一個 form 裡有多個值
});
}
// 需要用的時候
onSubmit(test: any) {
// 可以這樣接起來用
console.log(test);
// 也可以這樣接
const json = {
'a': this.myForm.get('a').value,
'b': this.myForm.value.b,
}
}
}
```
在 templete 寫
```htmlmixed
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
<lable for="a">a</label>
<input id="a" type="text" formControlName="a"/>
<span *ngIf="myForm.get('a').valid">a 的值可用</span>
<lable for="f">f</label>
<input *ngFor="let item of myForm.get('f').controls"
[formControlName]="index"/> <!-- 這裡可能要再查一下 -->
<buttom type="submit" [disabled]="!myForm.valid">
Submit
</buttom>
</form>
```
### nested form
一樣利用 reactive form
TS:
```typescript
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
export class AppComponent {
nestedForm: FormGroup = new FormGroup({
'hobbies': new FormArray([])
});
addHobby() {
const control: FormControl(null, Validators.required);
(<FormArray>this.nestedForm.get('hobbies')).push(control);
}
}
```
templete:
```htmlmixed
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">
<div FormArrayName="hobbies">
<h2>my hobbies</h2>
<button type="button" (click)="addHobby">Add Hobby</button>
<div *ngFor="let hobby of myForm.get('hobbies').controls; let i = index">
<input type="text" [formControlName]="i"/>
</div>
</div>
</form
```
### custom Validors
Validors 跟 pipe 很像,也是一個 function
TS:
```typescript
myForm: FormGroup = new FormGroup({
'a': ['', this.forbidden.bind(this)]
});
forbidden(control: FormControl):{[s: string]: boolean} {
if (control.value === 'a') {
return {'aIsNotValid': true};
}
return null; // 若合法的值,輸出 null
}
```
## Rxjs
參考資料: [30 天精通 RxJS](http://ithelp.ithome.com.tw/users/20103367/ironman/1199)
### Subject
可以一次訂閱多個物件 => 統一管理清單
```typescript
var subject = new Rx.Subject();
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.subscribe(observerB);
subject.next(1);
// "A next: 1"
// "B next: 1"
subject.next(2);
// "A next: 2"
// "B next: 2"
```
### BehaviorSubject
- BehaviorSubject 跟 Subject 最大的不同就是 BehaviorSubject 是用來呈現當前的值,而不是單純的發送事件
- 如果今天有一個新的訂閱,我們希望 Subject 能立即給出最新的值,而不是沒有回應
- BehaviorSubject 會記住最新一次發送的元素,並把該元素當作目前的值
- BehaviorSubject 在建立時就需要給定一個狀態
```typescript
var subject = new Rx.BehaviorSubject(0); // 0 為起始值
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
// "A next: 0"
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 3"
},3000)
```
### ReplaySubject
- 希望 Subject 代表事件,但又能在新訂閱時重新發送最後的幾個元素
- BehaviorSubject 是代表著狀態而 ReplaySubject 只是事件的重放而已
```typescript
var subject = new Rx.ReplaySubject(2); // 重複發送最後 2 個元素
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.next(1);
// "A next: 1"
subject.next(2);
// "A next: 2"
subject.next(3);
// "A next: 3"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 2"
// "B next: 3"
},3000)
```
### AsyncSubject
- 會在 subject 結束後送出最後一個值
- 跟 Promise 很像,都是等到事情結束後送出一個值
- 但實務上我們非常非常少用到 AsyncSubject,絕大部分的時候都是使用 BehaviorSubject 跟 ReplaySubject 或 Subject
```typescript
var subject = new Rx.AsyncSubject();
var observerA = {
next: value => console.log('A next: ' + value),
error: error => console.log('A error: ' + error),
complete: () => console.log('A complete!')
}
var observerB = {
next: value => console.log('B next: ' + value),
error: error => console.log('B error: ' + error),
complete: () => console.log('B complete!')
}
subject.subscribe(observerA);
subject.next(1);
subject.next(2);
subject.next(3);
subject.complete();
// "A next: 3"
// "A complete!"
setTimeout(() => {
subject.subscribe(observerB);
// "B next: 3"
// "B complete!"
},3000)
```