iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
0
Modern Web

用Angular打造完整後台系列 第 4

day04 SharedModule(二)

簡述

SharedModule是共用模塊的集合地,
此篇分兩篇來介紹常見的幾種共用模塊。

功能

後台製作的過程中,有許多功能是複用的。

  • DialogModule
  • MateriaModule
  • PipeModule
  • ValidationModule
  • LoadingModule

實作

(一) ValidationModule

此功能模塊是表單驗證功能。

檔案結構:

-src
  |-app
    |-app.component.css
    |-app.component.html
    |-app.component.ts
    |-app.module.ts
    |-config
        |-errorcode.ts
    |-shared
       |-dialog
       |-materia
       |-pipe
       |-validation
            |-validation-messages.component.ts
            |-validation.directive.ts
            |-validation.module.ts
            |-validation.service.ts
       |-shared.module.ts

後台最多的元件就是表單了,
所以Angular的表單建立跟相關驗證,
建議大家要認真理解。

--

validation-messages.component.ts

此組件是用來 show 驗證錯誤的訊息。

showerror

import { Component, Input } from "@angular/core";
import { FormControl } from "@angular/forms";

@Component({
  selector: "validation-messages",
  template:
    '<div class="error-message" *ngIf="errorMessage !== null">{{errorMessage | translate}}</div>',
  styles: [".error-message{color:#DF7607;font-size: x-small;}"]
})
export class ValidationMessagesComponent {
  @Input() control: FormControl;
  constructor() {}

  get errorMessage() {
    if (!!this.control) {
      for (let propertyName in this.control.errors) {
        if (
          this.control.errors.hasOwnProperty(propertyName) 
          && this.control.touched
        ) {
          return propertyName;
        }
      }
    }
    return null;
  }
}

--

validation.service.ts

此檔寫驗證的幾種方式。

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

export class ValidationService {
  static userValidator(control: FormControl) {
    //英數字+@
    if (
        !!control.value.match && 
        control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!@#$%^&*]{6,100}$/)
      ) {
      return null;
    } else {
      return { invalidUser: true };
    }
  }

  static integerValidator(control: FormControl) {
    //正整数
    if (!!control.value.match && control.value.match(/^[1-9]\d*$/)) {
      return null;
    } else {
      return { invalidInteger: true };
    }
  }
  ....
}

--

validation.directive.ts

此指令可以把帶進來的控件做驗證。

import { Input, Directive, forwardRef } from "@angular/core";
import { AbstractControl, NG_VALIDATORS, Validator } 
from "@angular/forms";
import { ValidationService } from "./validation.service";

@Directive({
  selector: "[fnValidator]",
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => ValidationDirective),
      multi: true
    }
  ]
})
export class ValidationDirective implements Validator {
  @Input() fnValidator: string;
  validate(c: AbstractControl): { [key: string]: any } {
    return ValidationService[this.fnValidator](c);
  }
}

實際運用時:

<input
  type="text"
  name="idSel"
  #idSel="ngModel"
  [(ngModel)]="searchValue"
  [placeholder]="textholder | translate"
  (change)="validSearch(idSel)"
  [fnValidator]="'numberOnlyValidator'"
/>

--

validation.module.ts

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { TranslateModule } from "@ngx-translate/core";
import { ValidationDirective } from "./validation.directive";
import { ValidationService } from "./validation.service";
import { ValidationMessagesComponent } from 
"./validation-messages.component";

@NgModule({
  providers: [ValidationService],
  imports: [CommonModule, FormsModule, TranslateModule],
  declarations: [ValidationMessagesComponent, ValidationDirective],
  exports: [ValidationMessagesComponent, ValidationDirective]
})
export class ValidationModule {}

(二) LoadingModule

此功能模塊是在讀取文章時的特效。

loading

檔案結構:

-src
  |-app
    |-app.component.css
    |-app.component.html
    |-app.component.ts
    |-app.module.ts
    |-config
        |-errorcode.ts
    |-shared
       |-dialog
       |-materia
       |-pipe
       |-validation
    |-loading
            |-loading.component.css
            |-loading.component.html
            |-loading.component.ts
            |-loading.module.ts
       |-shared.module.ts

--

loading.component.css

.loading {
  width: 50px;
  height: 40px;
  text-align: center;
  font-size: 10px;
  margin: auto;
}

.loading > div {
  background-color: #333;
  height: 100%;
  width: 4px;
  display: inline-block;
  margin: 0 2px;
  -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
  animation: sk-stretchdelay 1.2s infinite ease-in-out;
}

.loading .rect2 {
  -webkit-animation-delay: -1.1s;
  animation-delay: -1.1s;
}

.loading .rect3 {
  -webkit-animation-delay: -1s;
  animation-delay: -1s;
}

.loading .rect4 {
  -webkit-animation-delay: -0.9s;
  animation-delay: -0.9s;
}

.loading .rect5 {
  -webkit-animation-delay: -0.8s;
  animation-delay: -0.8s;
}

@-webkit-keyframes sk-stretchdelay {
  0%,
  40%,
  100% {
    -webkit-transform: scaleY(0.4);
  }
  20% {
    -webkit-transform: scaleY(1);
  }
}

@keyframes sk-stretchdelay {
  0%,
  40%,
  100% {
    transform: scaleY(0.4);
    -webkit-transform: scaleY(0.4);
  }
  20% {
    transform: scaleY(1);
    -webkit-transform: scaleY(1);
  }
}

--

loading.component.html

<div class="flex">
  <div class="loading">
    <div class="rect1"></div>
    <div class="rect2"></div>
    <div class="rect3"></div>
    <div class="rect4"></div>
    <div class="rect5"></div>
  </div>
</div>

--

loading.component.ts

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

@Component({
  selector: "app-loading",
  templateUrl: "./loading.component.html",
  styleUrls: ["./loading.component.css"]
})
export class LoadingComponent {
  constructor() {}
}

--

loading.module.ts

import { NgModule } from "@angular/core";
import { LoadingComponent } from "./loading.component";

@NgModule({
  declarations: [LoadingComponent],
  exports: [LoadingComponent]
})
export class LoadingModule {}

總結

寫一個系統會有很多個子模塊,有些需要的功能模塊相同,
例如表單FormsModuleReactiveFormsModule
所以把通用的功能模塊彙整到SharedModule裡,
之後要使用的子模塊可直接匯入SharedModule就能使用。

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { TranslateModule } from "@ngx-translate/core";
import { NgxChartsModule } from "@swimlane/ngx-charts";
import { MateriaModule } from "./materia/materia.module";
import { ValidationModule } from "./validation/validation.module";
import { DialogModule } from "./dialog/dialog.module";
import { PipeModule } from "./pipe/pipe.module";
import { LoadingModule } from "./loading/loading.module";

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    TranslateModule,
    DialogModule,
    MateriaModule,
    PipeModule,
    ValidationModule,
    LoadingModule
  ],
  exports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    TranslateModule,
    DialogModule,
    MateriaModule,
    PipeModule,
    ValidationModule,
    LoadingModule
  ]
})
export class SharedModule {}

其他的模塊匯入SharedModule

@NgModule({
  imports: [SharedModule, ...],
  declarations: [...],
  exports: [...],
  providers: [...]
})

可能有人會想說TranslateModuleapp.moudle.ts 已經有匯入了,
而且 app.moudle.ts 是全域的,為什麼還要在SharedModule裡面匯入呢?
主因是有可能有好幾個模塊是延遲子路由的方式,
所以吃不到匯入。才會另外寫在這裡。


範例碼

https://stackblitz.com/edit/ngcms-base


上一篇
day03 SharedModule(一)
下一篇
day05 json-server 模擬與 Model 建置(一)
系列文
用Angular打造完整後台30

尚未有邦友留言

立即登入留言