使用ng2-admin搭建成熟可靠的後臺系統 -- ng2-admin(五)

使用ng2-admin搭建成熟可靠的後臺系統 -- ng2-admin(五)

完善動態表單組件

  • 升級Angular 4.1 -> 4.3
  • 添加 json-server 模擬數據
  • 建立本身的 http
  • 完成一次表單提交

升級Angular 4.1 -> 4.3(版本大於 4.3.0 可跳過)

由於 httpClient 支持的最低 Angular 版本爲 4.3, 因此須要先升級咱們的 Angular
以下圖 配置 package.json
mark
markcss

而後須要修改一個配置項,兼容到 4.3.0 版本
tsconfig.json 添加一行html

"paths": { "@angular/*": ["../node_modules/@angular/*"] }

這樣便完成了版本的升級node

添加 json-server 模擬數據

咱們完成一個數據查詢,提交須要一些模擬數據,json-server 工具能夠幫助咱們,全局安裝git

npm i json-server -ggithub

package.json 中的 scripts 添加一行ajax

"db:mock": "json-server --watch ./db/db.json"

咱們如今須要建立一個 json 文件,用於裝載咱們的模擬數據,根據上述命令,在項目根目錄建立npm

db/db.jsonjson

{
  "user": [
    {
      "id": 1,
      "firstName": "張小",
      "emailAddress": "15135131@qq.com",
      "brave": "solid"
    }
  ]
}

打開一個命令行窗口,在項目文件夾執行
npm run db:mock
咱們的 json-server 就啓動了,如今來回到咱們的項目redux

建立本身的 http

在提交表單以前,咱們須要 ajax 去提交咱們的數據,這時候涉及到服務,數據驗證,處理 response,這時候須要建立一套 http/httpClient 去負責這些任務。後端

建立一個 http 工具類,負責提供 header, host, 以及完成拼接 url, 參數的工做。 根據咱們的 json-server 配置,來建立這個文件。

api/http/http.service.ts

import { Injectable } from "@angular/core";
import { HttpHeaders } from "@angular/common/http";

@Injectable()
export class HttpComponentUtil {
  public headers = new HttpHeaders({
    "Content-Type": "application/json"
  });

  private url: string = "http://localhost:3000/";

  public getUrl(url: string): string {
    return this.url + url;
  }
}

建立 http 的統一攔截服務,作統一攔截,而且在將來作一些通用的工做
api/http/noopInterceptor.ts

import { Injectable } from "@angular/core";
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpResponse
} from "@angular/common/http";

@Injectable()
export class NoopInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    const started = Date.now();
    return next
      .handle(req)
      .do(event => {
        if (event instanceof HttpResponse) {
          const elapsed = Date.now() - started;
          console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
        }
      });
  }
}

http 的統一攔截,還須要在 appModule.ts 中單獨註冊
app.module.ts

@NgModule({
  ...
  providers: [
    ...
    {
      provide: HTTP_INTERCEPTORS,
      useClass: NoopInterceptor,
      multi: true,
    }
  ]
});

當 http 請求出錯時,須要作一些錯誤處理,如今來建立一個服務,作統一的錯誤處理
api/http/handle.service.ts

import { Injectable, Inject } from '@angular/core';
import { Location } from '@angular/common';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import swal from 'sweetalert2';

@Injectable()

export class HandleService {
    constructor(
        private router: Router,
        private _location: Location
    ) {

    };

    // 處理系統錯誤
    handleError(err: any): Observable<any> {
        let errMsg = '發生未知錯誤,請重試';
        if (typeof err == 'object' && err.status !== undefined) {
            if (err.status == 404) {
                errMsg = '服務器處理異常,請重試';
            } else if (err.status == 401) {
                swal('當前頁面無權限查看', '', 'warning');
                this._location.back();
                return Observable.empty();
            } else if (err.status == 504) {
                errMsg = '服務器請求超時,請重試';
            } else if (err.status == 503) {
                errMsg = '相關服務正在部署發佈,請稍等';
            } else {
                errMsg = err.json().message;
            }
        }
        swal(errMsg, '', 'error');
        return Observable.empty();
    }

    // 處理returnCode 這裏假定接口 200 經過後,後端返回的狀態碼爲 returnCode
    handleStatus(result: any): Observable<any> {
        switch ((result.returnCode && String(result.returnCode)) || "201") {
            case '201':
              return Observable.of(result);
            case '1000':
                return Observable.of(result);
            case '1001':
                swal('當前頁面無權限查看', '', 'warning');
                this._location.back();
                return Observable.empty();
            case '1002': // 數據爲空
                return Observable.of(result);
            default:
                swal('沒法識別的錯誤碼,請聯繫管理員', '', 'error');
                return Observable.empty();
        }
    }
}

上面有兩個依賴注入,須要在 providers 注入,如今建立一個文件來提供注入
api/index.ts

import { HttpComponentUtil } from './http/http.service';
import { HandleService } from './http/handle.service';

export const API_SERVICE = [
  HttpComponentUtil,
  HandleService
];

pages/pages.module.ts 中注入這兩個服務

@NgModule({
  imports: [CommonModule, AppTranslationModule, NgaModule, routing],
  declarations: [Pages],
  providers: [
    ...API_SERVICE
  ]
})

到這裏,http 服務建立完成,下半部分在表單提交時完成,屬於應用層

完成一次表單提交

完成表單提交,須要用到 ajax, 咱們將使用 httpClient 完成 ajax 的工做,咱們須要先注入 HttpClientModule, 在 nga.module.ts 中注入,這裏不貼代碼了

注入完成後,如今來開始編寫咱們的各項請求實例。
由於是表單提交,因此咱們新建一個服務,由它來完成表單提交的最後一步。
theme/components/dynamic-form/dynamic-form.service.ts

import { Observable } from "rxjs/Rx";
import { Injectable } from "@angular/core";
import { HttpClient, HttpEvent, HttpHeaders } from "@angular/common/http";
import { HttpComponentUtil } from '../../../pages/api/http/http.service';
import { HandleService } from '../../../pages/api/http/handle.service';

@Injectable()
export class DynamicFormService {
  constructor(
    private http: HttpClient,
    private https: HttpComponentUtil,
    private handleService: HandleService
  ) {}

  public getList(url: string, params: {} = {}): Observable<any> {
    return new Observable();
  }

  /**
   *
   *
   * @param {string} url
   * @param {{}} [params={}] 請求入參的 body,參數
   * @returns {Observable<any>}  返回一個可供訂閱的觀察者對象
   * @memberof DynamicFormService
   */
  public saveQuery(url: string, params: {} = {}): Observable<any> {
    let api_url: string = this.https.getUrl(url); // 利用公用的 http 服務,拼接獲取url
    return this.http.post(api_url, params, {
      headers: this.https.headers
    })
    .map((res: any) => (<any>this.handleService.handleStatus(res)).value || undefined) // 捕獲錯誤碼
    .catch(err => this.handleService.handleError(err)); // 捕獲系統錯誤
  }
}

上面構建了包含一個 saveQuery 功能的服務, 代碼已經添加註釋,能夠仔細研讀一下。

saveQuery 的兩個參數,params 應該由動態表單直接獲取提供,url 應該由頁面提供, 因此 DynamicFormComponent 應該接入一個 Input 參數

@Input() config: FormConfig;

dynamic-form/form-base.ts

export interface FormConfig {
  url: string;
}

如今須要在頁面中,把 config 參數傳入組件
user-add.component.ts

...
export class UserAddComponent {
  public UserAddConfig: FormConfig = {
    url: "user"
  }
  ...
}

user-add.component.html

<h1>
    新增用戶組件
</h1>
<div class="user-form">
  <dynamic-form [questions]="UserAddQuestions" [config]="UserAddConfig"></dynamic-form>
</div>

如今回到組件,咱們將完成咱們的提交表單操做,如今思考兩個問題,提交成功後的操做

  • 成功提示
  • 返回到上一個頁面

因此咱們的組件應該是
dynamic-form.component.ts

import { Component, Input, OnInit } from "@angular/core";
import { Location } from '@angular/common';
import { FormGroup } from "@angular/forms";

import { QuestionBase } from "../dynamic-form-components/dynamic-form-base/question-base";
import { QuestionControlService } from "./question-control.service";
import { DynamicFormService } from "./dynamic-form.service";
import "style-loader!./dynamic-fom-components.component.scss";
import { FormConfig } from './form-base';
import swal from "sweetalert2";

@Component({
  selector: "dynamic-form",
  templateUrl: "./dynamic-form.component.html",
  styleUrls: ["./dynamic-form.component.scss"],
  providers: [QuestionControlService, DynamicFormService]
})
export class DynamicFormComponent implements OnInit {
  @Input() questions: QuestionBase<any>[] = [];
  @Input() config: FormConfig;
  form: FormGroup;
  payload = "";

  constructor(
    private qcs: QuestionControlService,
    private service: DynamicFormService,
    private _location: Location
  ) {}

  ngOnInit() {
    this.form = this.qcs.toFormGroup(this.questions);
  }

  onSubmit() {
    this.payload = JSON.stringify(this.form.value);
    this.service.saveQuery(this.config.url, this.payload)
      .subscribe((res: Response) => {
        console.log(res);
        swal("success","","success").then(() => {
          this._location.back();
        });
      })
  }
}

這裏使用到了 sweetalert2 組件,須要讀者自行安裝,而且在
pages.component.ts 中引入樣式

import "style-loader!sweetalert2/dist/sweetalert2.min.css";

如今打開瀏覽器,來測試一下咱們剛纔的頁面,測試結果以下
mark
mark

添加成功和失敗,都有對應提示,而且提交成功後會返回到上一頁,如今來看看 db.json,以下圖,數據也被添加進了json!
mark

咱們的數據已經存入,下章就來說解,如何搭建一個動態表格組件,來展現咱們的數據,後續會把增刪改查功能一一介紹,在介紹完基礎組件後,會注入 redux 方便咱們的狀態管理

(此章代碼在ng2-admin 的 httpclient-submit 分支上,能夠pull 下來,方便讀者練習)

相關文章
相關標籤/搜索