[筆記][Angular 2][Angular 4]透過Angular-CLI 撰寫 Angular 2針對單一資料表的CRUD

這篇記錄小喵透過Angular-CLI撰寫針對單一資料表的CRUD,從WebAPI,到Angular 2。文章有點長,如果您對於WebAPI的部分已經很熟悉,建議您可以從Angular 2的部分開始看。

緣起

小喵這一篇,紀錄著如何透過Angular-CLI,開發撰寫 Angular 2 透過 WebAPI 針對單一資料表的CRUD,主要重點會放在 Angular 2 的相關部分。提供小喵自己外來參考,也提供網友們參考。
本來預計著介紹完Angular 2的,不過剛好同事正準備第一次用 Angular 2 要開發CRUD的功能,所以小喵這一篇先發,過程會盡量讓看的人可以依照步驟逐一完成,中間用到的,有些觀念後續小喵再補充在其他篇中。
文章有點長,如果您對於WebAPI的部分已經很熟悉,那麼,建議您可以從Angular 2的部分開始看~

開發順序

小喵這篇會依照以下這樣的順序開發:

首先,是WebAPI的部分

  1. 建立WebAPI專案
  2. Nuget取得Dapper, CORS套件
  3. 資料存取Model撰寫
  4. Controller撰寫

接著,是 Angular 2 的部分:

  1. 建立NG2專案
  2. 撰寫資料存取的Service
  3. 撰寫Component
    1. 多筆顯示
    2. 分頁
    3. 單筆維護表單
    4. 新增資料
    5. 維護資料
    6. 刪除資料
    7. 新增按鈕

整體上就以上述的順序來進行囉。

資料庫的部分,則選擇北風資料庫裡面的Products這個資料表來當作範例。

 

WebAPI的部分

建立WebAPI的專案

我們新增一個名稱為SBLG的方案,在方案中,新增方案資料夾WebAPI,在方案資料夾的WebAPI上右鍵,加入,新專案「PBLGAPI」

由於這一篇主要是要練習為主,驗證的部分就暫時先選擇無驗證

按下確定專案就建立起來

Nuget套件:Dapper, CORS

Dapper套件,主要是在資料存取時,可以透過Dapper的幫忙,將的屬性自動對應到相關的欄位,這樣的開發可以讓程式碼更為簡潔,詳細想進一步瞭解Dapper,請參考小喵的『[筆記]好用輕量型ORM神器--Dapper』這一篇。

CORS套件,由於在瀏覽器中,跨專案使用WebAPI,一定會有CORS的問題,因此我們在撰寫WebAPI的同時,就把這部分的問題先處理掉。

資料存取的Model

接著,我們開始撰寫資料存取的部分,首先,我們的資料來源是北風資料庫,裡面的『Products』資料表,因此,我們先依據 Products 的 Data Schema來撰寫資料存取類別『ProductInfo』。我們先在資料夾『Models』中,新增一個『Infos』的資料夾,未來相關資料類別,我們放在這裡面。之後,在Infors資料夾中,新增類別如下:

Public Class ProductInfo
    Public Property ProductID() As Integer = 0
    Public Property ProductName() As String = ""
    Public Property SupplierID() As Integer = 0
    Public Property CategoryID() As Integer = 0
    Public Property QuantityPerUnit() As String = ""
    Public Property UnitPrice() As Integer = 0
    Public Property UnitsInStock() As Int16 = 0
    Public Property UnitsOnOrder() As Int16 = 0
    Public Property ReorderLevel() As Int16 = 0
    Public Property Discontinued() As Boolean = False
End Class

屬性名稱與資料表的欄位名稱一致,這樣後續,就可以讓Dapper發揮大功用,自動對應相關欄位。

接著,在Models再新增一個資料夾,名為『DAOs』未來所有資料存取的程式,放此資料夾。接著,新增一個『ProductDAO』的類別,來放資料群取的相關程式碼。相關程式碼內容如下:

Imports System.Data
Imports System.Data.SqlClient
Imports Dapper

Public Class ProductDAO
    ''' <summary>
    ''' 依據傳入的第幾頁(PageNum)、頁面筆數(PageSize),傳回該分頁的相關資料物件集合
    ''' </summary>
    ''' <param name="PageNum">第幾頁</param>
    ''' <param name="PageSize">頁面筆數</param>
    ''' <returns>
    ''' 成功回傳資料物件集合oProds
    ''' </returns>
    Public Function GetProdsByPage(ByVal PageNum As Integer, ByVal PageSize As Integer) As List(Of ProductInfo)
        'Throw New NotImplementedException()
        Dim oProds As New List(Of ProductInfo)
        Try
            Dim ConnStr As String = GetConnStr()
            Using Conn As New SqlConnection(ConnStr)
                Dim SqlTxt As String = ""
                SqlTxt &= " SELECT *  "
                SqlTxt &= " FROM  "
                SqlTxt &= " ( "
                SqlTxt &= "     SELECT rank() OVER ( ORDER BY ProductID ) AS RankNumber, *  "
                SqlTxt &= " 	FROM Products (NOLOCK) "
                SqlTxt &= " ) AS P "
                SqlTxt &= " WHERE RankNumber BETWEEN @PageSize*(@PageNum-1)+1 AND @PageSize*@PageNum "
                SqlTxt &= "  "

                oProds = Conn.Query(Of ProductInfo)(SqlTxt, New With {.PageSize = PageSize, .PageNum = PageNum})
            End Using
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
        Return oProds
    End Function

    ''' <summary>
    ''' 依據傳入的頁面筆數(PageSize),傳回該資料總共有幾頁
    ''' </summary>
    ''' <param name="PageSize">頁面筆數</param>
    ''' <returns>
    ''' 成功傳回總頁數TotalPage
    ''' </returns>
    Public Function GetProdsTotalPageByPageSize(ByVal PageSize As Integer) As Integer
        Dim TotalPage As Integer = 0
        Try
            Dim ConnStr As String = GetConnStr()
            Using Conn As New SqlConnection(ConnStr)
                Dim SqlTxt As String = ""
                SqlTxt &= " SELECT CASE	 "
                SqlTxt &= " 		WHEN Cnt % @PageSize = 0 THEN Cnt / @PageSize "
                SqlTxt &= " 		ELSE (Cnt / @PageSize) + 1 "
                SqlTxt &= " 	END AS TotalPage "
                SqlTxt &= " FROM "
                SqlTxt &= " ( "
                SqlTxt &= " 	SELECT COUNT(*) Cnt "
                SqlTxt &= " 	FROM Products (NOLOCK) "
                SqlTxt &= " ) V "
                SqlTxt &= "  "

                Conn.Open()

                Using Cmmd As New SqlCommand(SqlTxt, Conn)
                    Cmmd.Parameters.AddWithValue("@PageSize", PageSize)

                    Dim Dr As SqlDataReader = Cmmd.ExecuteReader()
                    If Dr.HasRows Then
                        Dr.Read()
                        TotalPage = Dr.Item("TotalPage")
                    End If
                    Dr.Close()
                End Using
            End Using
            Return TotalPage

        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    ''' <summary>
    ''' 依據傳入的資料物件,維護該筆資料
    ''' </summary>
    ''' <param name="oProd">產品資料物件</param>
    ''' <returns>
    ''' 成功回傳Success
    ''' </returns>
    Public Function ChgProd(oProd As ProductInfo) As String
        Try
            Dim ConnStr As String = GetConnStr()
            Using Conn As New SqlConnection(ConnStr)
                Conn.Open()
                Dim SqlTxt As String = ""
                SqlTxt = ""
                SqlTxt &= " UPDATE [dbo].[Products] "
                SqlTxt &= " SET "
                SqlTxt &= " ProductName=@ProductName"
                SqlTxt &= ", SupplierID=@SupplierID"
                SqlTxt &= ", CategoryID=@CategoryID"
                SqlTxt &= ", QuantityPerUnit=@QuantityPerUnit"
                SqlTxt &= ", UnitPrice=@UnitPrice"
                SqlTxt &= ", UnitsInStock=@UnitsInStock"
                SqlTxt &= ", UnitsOnOrder=@UnitsOnOrder"
                SqlTxt &= ", ReorderLevel=@ReorderLevel"
                SqlTxt &= ", Discontinued=@Discontinued"
                SqlTxt &= ", PGUID=@PGUID"
                SqlTxt &= " WHERE ProductID = @ProductID "

                Conn.Execute(SqlTxt, oProd)
            End Using

            Return "Success"
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    ''' <summary>
    ''' 依據傳入的產品代號(ProductID),刪除該商品資料
    ''' </summary>
    ''' <param name="ProductID">產品代號</param>
    ''' <returns>
    ''' 成功傳回Success
    ''' </returns>
    Public Function DelProd(ProductID As Integer) As String
        Try
            Dim ConnStr As String = GetConnStr()
            Using Conn As New SqlConnection(ConnStr)
                Conn.Open()
                Dim SqlTxt As String = ""
                SqlTxt = ""
                SqlTxt &= " DELETE Products "
                SqlTxt &= " WHERE ProductID = @ProductID "
                SqlTxt &= "  "

                Conn.Execute(SqlTxt, New With {.ProductID = ProductID})

            End Using
            Return "Success"
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    ''' <summary>
    ''' 依據傳入的商品物件,新增一筆資料
    ''' </summary>
    ''' <param name="oProd">商品物件</param>
    ''' <returns>
    ''' 成功傳回Success
    ''' </returns>
    Public Function InsertProd(oProd As ProductInfo) As String
        Try
            Dim ConnStr As String = GetConnStr()
            Using Conn As New SqlConnection(ConnStr)
                Conn.Open()
                Dim SqlTxt As String = ""
                SqlTxt = ""
                SqlTxt &= " INSERT INTO [dbo].[Products] "
                SqlTxt &= " 	(ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued) "
                SqlTxt &= " VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice, @UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued) "
                SqlTxt &= "  "

                Conn.Execute(SqlTxt, oProd)

            End Using
            Return "Success"
        Catch ex As Exception
            Throw New Exception(ex.Message)
        End Try
    End Function

    Private Function GetConnStr() As String
        Return "Data Source=.\SQLExpress;Initial Catalog=NorthwindChinese;Integrated Security=True"
    End Function
End Class

另外,Controller會用到的錯誤處理,小喵也寫成一個類別來幫忙。還有,分頁查詢時,最後傳回的時候,會同時傳回總頁數與該分頁資料,因此也會撰寫一個類別來傳回。相關程式碼如下:

錯誤處理的類別:(ErrMsgInfo)

Imports System.Net.Http
Imports System.Net

Public Class ErrMsgInfo
    Private m_ErrCode As String = ""
    Private m_ErrMsg As String = ""
    Private m_ErrTime As String = ""
    Private m_ErrJSON As String = ""

    Private m_Ex As Exception = Nothing
    Private m_RepMsg As HttpResponseMessage

    Public WriteOnly Property Ex As Exception
        Set(value As Exception)
            m_Ex = value
            If m_Ex IsNot Nothing Then
                m_ErrCode = m_Ex.HResult
                m_ErrMsg = m_Ex.Message
                GenErrJSON()
            End If
        End Set
    End Property

    Public ReadOnly Property RepMsg As HttpResponseMessage
        Get
            Return m_RepMsg
        End Get
    End Property


    Private Sub GenErrJSON()
        If m_ErrCode <> "" And m_ErrMsg <> "" Then
            '產生錯誤回傳的JSON 
            m_ErrJSON = "{" & Chr(34) & "ErrCode" & Chr(34) & ":" & Chr(34) & m_ErrCode & Chr(34) & "," & Chr(34) & "ErrMsg" & Chr(34) & ":" & Chr(34) & m_ErrMsg & Chr(34) & "}"
            '產生迴船的HttpResponseMessage 
            m_RepMsg = New HttpResponseMessage(HttpStatusCode.ExpectationFailed)
            m_RepMsg.Content = New StringContent(m_ErrJSON)
            m_RepMsg.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("application/json")
        End If
    End Sub
End Class

傳回一頁商品資料的類別:

Public Class ProductPageInfo
    Public Property TotalPage As Integer = 0
    Public Property oPages As List(Of ProductInfo)
End Class

醬子,WebAPI的模組部分準備好了,下一步撰寫Controller的部分

Controller的部分

CORS的設定

要使用CORS,必須先在App_Start裡面的『WebApiConfig.vb』做個簡單的設定,需加上『config.EnableCors()』這一句

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web.Http

Public Module WebApiConfig
    Public Sub Register(ByVal config As HttpConfiguration)
        ' Web API 設定和服務

        ' CORS請加上這一行
        config.EnableCors()

        ' Web API 路由
        config.MapHttpAttributeRoutes()

        config.Routes.MapHttpRoute(
            name:="DefaultApi",
            routeTemplate:="api/{controller}/{id}",
            defaults:=New With {.id = RouteParameter.Optional}
        )
    End Sub
End Module

Controller中,需要加上CORS的設定,Angular 2 那邊才不會被擋住,整體內容如下:

Imports System.Net
Imports System.Web.Http
Imports System.Net.Http
Imports System.Web.Http.Cors

Namespace Controllers
    <EnableCors("*", "*", "*", "*")>
    Public Class ProductController
        Inherits ApiController


        <Route("api/Prod/{PageNum}/{PageSize}")>
        <HttpGet>
        Public Function GetProdsByPage(ByVal PageNum As Integer, ByVal PageSize As Integer) As ProductPageInfo
            Try
                Dim dao As New ProductDAO
                Dim oProdPage As New ProductPageInfo
                Dim TotalPage As Integer = dao.GetProdsTotalPageByPageSize(PageSize)
                Dim oProds As List(Of ProductInfo) = dao.GetProdsByPage(PageNum, PageSize)
                oProdPage.TotalPage = TotalPage
                oProdPage.oPages = oProds
                Return oProdPage

            Catch ex As Exception
                'Throw New Exception(ex.Message) 
                Dim oErr As New ErrMsgInfo
                oErr.Ex = ex

                Dim RepMsg As HttpResponseMessage = oErr.RepMsg
                Throw New HttpResponseException(RepMsg)

            End Try
        End Function

        ' POST: api/Prod 
        <Route("api/Prod")>
        <HttpPost>
        Public Sub PostValue(<FromBody()> ByVal oProd As ProductInfo)
            Try
                Dim dao As New ProductDAO
                Dim Rc As String = dao.InsertProd(oProd)

            Catch ex As Exception
                'Throw New Exception(ex.Message) 
                Dim oErr As New ErrMsgInfo
                oErr.Ex = ex

                Dim RepMsg As HttpResponseMessage = oErr.RepMsg
                Throw New HttpResponseException(RepMsg)

            End Try
        End Sub

        ' PUT: api/Prod/5 
        <Route("api/Prod")>
        <HttpPut>
        Public Sub PutValue(<FromBody()> ByVal oProd As ProductInfo)
            Try
                Dim dao As New ProductDAO
                Dim Rc As String = dao.ChgProd(oProd)

            Catch ex As Exception
                'Throw New Exception(ex.Message) 
                Dim oErr As New ErrMsgInfo
                oErr.Ex = ex

                Dim RepMsg As HttpResponseMessage = oErr.RepMsg
                Throw New HttpResponseException(RepMsg)

            End Try

        End Sub

        ' DELETE: api/Prod/5 
        <Route("api/Prod/Delete/{ProductID}")>
        <HttpDelete>
        Public Sub DeleteValue(ByVal ProductID As Integer)
            Try
                Dim dao As New ProductDAO
                Dim Rc As String = dao.DelProd(ProductID)

            Catch ex As Exception
                'Throw New Exception(ex.Message) 
                Dim oErr As New ErrMsgInfo
                oErr.Ex = ex

                Dim RepMsg As HttpResponseMessage = oErr.RepMsg
                Throw New HttpResponseException(RepMsg)

            End Try
        End Sub
    End Class

End Namespace

稍微用Chrome的DHC測試一下:OK,沒問題。

Angular 2 的部分

我們希望將相關的程式碼放在一起,因此,開啟檔案總管,進入剛剛Solution的資料夾,新增一個Ag2的資料夾,並進入該資料夾。然後如下處理:

就會開啟一個命令視窗,且位置是在這邊,此時輸入指令:『ng new NG2Blg』,建立新的 Angular 2 專案,這裡要稍等一下~
當您看到如下的訊息,專案就建立完成囉

補充:ng new 專案名稱,這中間會透過「npm」安裝一些套件,剖費時間。如果想加快一些,可以先忽略npm的套件安裝,之後再用『yarn』安裝套件,加快時間。

  • ng new 專案名稱 --skip-npm
  • cd 專案名稱
  • yarn

 

接著,輸入『Code NG2Blg』,就可以開啟Visual Studio Code並且開好這個專案。

撰寫Service

接著,我們先透過命令產生Data Service的檔案。在Visual Studio Code中,可以透過『Ctrl + `』在VS Code中開啟命令視窗,未來輸入指令直接在此即可。我們輸入產生Prod服務的指令如下:

ng g s Prod

會在「src/app」產生「prod.service.ts」的檔案,我們雙擊這個檔案,開始撰寫service,我們會先初步撰寫取得分頁內容的部分。大致步驟如下:

  1. 屬性宣告:宣告datas屬性,型別先設定為any,預設為空的陣列,用以承接從API取得的JSON物件集合。
  2. 注入http:在「constractor」中,宣告「private http:Http」,同時記得加上Import Http的宣告在最上方『import { Http } from '@angular/http';』
  3. 函數撰寫:撰寫「GetDatasByPage」的函數,傳入PageNum,透過this.http.get取得資料。
  4. 回頭在「constructor」中,呼叫function,讀取第一頁的資料。

相關程式碼如下:

import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class ProdService {

  //API來源名稱
  serverName:string='http://localhost:54514/';

  //取回的內容
  datas:any=[];
  error:any;

  //分頁相關屬性
  currentPageNum:number=1;
  pageSize:number=10;
  totalPage:number=0;

  constructor(private http:Http) {  
    this.GetDatasByPage(1);
  }

  GetDatasByPage(pageNum:number){
    this.currentPageNum = pageNum;
    let url:string=this.serverName + 'api/prod/' + this.currentPageNum.toString() + '/' + this.pageSize.toString();
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.get(url, options)
    .subscribe(
      (value:Response)=>{
        let jData:any;
        jData = value.json();
        this.datas = jData.oPages;
        this.totalPage = jData.TotalPage;
      },
      (error)=>{
        this.error = error;
      }
    );
  }

}

 

app.module的providers

不要忘記,在 app.module.ts 的 providers中,把ProdService加進去。

providers: [ProdService],

 

Prod Component

DataService好了後,我們接著就來將資料顯示出來。我們先透過命令視窗下「ng g c Prod」產生一個Prod的Component,然後讓資料顯示在這個Prod的Component中。

首先開啟『prod.component.ts』,將寫好的prod data service注入:在constructro中加入 dataSvc,如下:

import { ProdService } from './../prod.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-prod',
  templateUrl: './prod.component.html',
  styleUrls: ['./prod.component.css']
})
export class ProdComponent implements OnInit {

  constructor(public dataSvc:ProdService) { }

  ngOnInit() {
  }

}

接著,開啟Templete檔案「prod.component.html」,撰寫一個table來展現資料,相關內容如下:

<table border="1">
  <thead>
    <tr>
      <th>功能</th>
      <th>ProductID</th>
      <th>ProductName</th>
      <th>SupplierID</th>
      <th>CategoryID</th>
      <th>QuantityPerUnit</th>
      <th>UnitPrice</th>
      <th>UnitsInStock</th>
      <th>UnitsOnOrder</th>
      <th>ReorderLevel</th>
      <th>Discontinued</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let oItem of dataSvc.datas">
      <td></td>
      <td>{{oItem.ProductID}}</td>
      <td>{{oItem.ProductName}}</td>
      <td>{{oItem.SupplierID}}</td>
      <td>{{oItem.CategoryID}}</td>
      <td>{{oItem.QuantityPerUnit}}</td>
      <td>{{oItem.UnitPrice}}</td>
      <td>{{oItem.UnitsInStock}}</td>
      <td>{{oItem.UnitsOnOrder}}</td>
      <td>{{oItem.ReorderLevel}}</td>
      <td>{{oItem.Discontinued}}</td>
    </tr>
  </tbody>
</table>

醬子,prod component大致顯示多筆已經ok囉,再來,把prod component放到 app component中,開啟「app.component.html」,加上『<app-prod></app-prod>』

<h1>
  {{title}}
</h1>
<app-prod></app-prod>

就醬子,可以執行看看囉,執行結果如下:

分頁控制

接著,我們加上分頁的控制,讓他可以切換分頁、並且可以調整每頁的筆數。

首先,開啟「prod.service.ts」,增加分頁所需要的屬性

  pageNumArray:number[]=[];   //用以存放所有分頁的數字(3頁就會是1,2,3)
  pageNumArrayMax:number=0;   //分頁數的陣列最大數
  startPg:number=0;     //分頁陣列,開始的頁數

撰寫計算分頁控制項陣列的function

  //分頁相關開始
  //  設定分頁控制項的陣列
  set_pageNumArray(){
      //初始劃分頁陣列
        this.pageNumArray=[];
        let i:number=0;

        //計算起始頁
        this.startPg = Math.floor((this.currentPageNum-1)/10)*10+1;

        if(this.startPg+10>this.totalPage){
          this.pageNumArrayMax = this.totalPage;
        }
        else{
          this.pageNumArrayMax = this.startPg+10-1;
        }
        for(i=this.startPg;i<=this.pageNumArrayMax;i++){
            this.pageNumArray.push(i);
        }
  }
  //分頁相關結束

撰寫分頁功能的畫面,在「prod.component.html」中,加上控制分頁的功能如下:

  <!--分頁開始-->
  <span><button (click)="dataSvc.GetDatasByPage(1)">第一頁</button></span>
  <span *ngIf="(dataSvc.startPg-10)>=1">
    <button (click)="dataSvc.GetDatasByPage(dataSvc.startPg-1)">...{{dataSvc.startPg-1}}</button>
  </span>
  <span *ngFor="let pn of dataSvc.pageNumArray">
    <button (click)="dataSvc.GetDatasByPage(pn)" [ngClass]="{currentPage: pn==dataSvc.currentPageNum}">{{pn}}</button>
  </span>
  <span *ngIf="dataSvc.pageNumArrayMax < dataSvc.totalPage">
    <button (click)="dataSvc.GetDatasByPage(dataSvc.pageNumArrayMax+1)">{{dataSvc.pageNumArrayMax+1}}...</button>
  </span>
  <span><button (click)="dataSvc.GetDatasByPage(dataSvc.totalPage)">最後一頁</button></span>
  每頁筆數:
  <select name="sltPageSize" id="sltPageSize" [(ngModel)]="dataSvc.pageSize" (change)="dataSvc.GetDatasByPage(dataSvc.currentPageNum)">
    <option value="5">5</option>
    <option value="10">10</option>
    <option value="15">15</option>
    <option value="20">20</option>
  </select>
  <!--分頁結束-->

其中,目前的分頁,要加上一個顏色來區別,這部分的設定,就設定在「prod.component.css」中

.currentPage{background-color:yellow;}

就醬子,目前的執行結果如下:

其中,Discontinued的型態是Boolen,這部分採用Checkbox比較合適,所以在稍微改寫一下html~

{{oItem.Discontinued}}改為『<input type="checkbox" [(ngModel)]="oItem.Discontinued">』,透過ngModel將input綁定oItem.Discontinued。畫面結果如下:

加上選取的按鈕

接著,我們想在每一筆的前面,增加一個『編』的按鈕,按下後,可以編輯該資料。

我們首先在『Prod.Service』中,新增一個屬性「currentData」,用來記錄被選取的那一筆資料內容。另外,用一個屬性「editMode」來記錄目前編輯區的狀況,期望按下「編」的按鈕,將currentData設定為目前該筆資料,並且設定「editMode」為『Edit』

所以我們新增兩個屬性

  currentData:any={};   //被選取作用中的單筆資料  
  editMode:string='';   //記錄編輯模式為何?Edit:編輯中, AddNew:新增中,空字串為非作用中,初始值為''非作用中。

增加一個function

  doSetcurrentData(oData:any){
    this.currentData=oData;
    this.editMode='Edit';
  }

接著,在畫面中,每一筆前面增加一個按鈕,設定如下:

      <td>
        <button type="button" (click)="dataSvc.doSetcurrentData(oItem)">編</button>
      </td>

我們在畫面的下方,暫時把editMode與currentData顯示出來,因此畫面html改如下:

  <tbody>
    <tr *ngFor="let oItem of dataSvc.datas">
      <td>
        <button type="button" (click)="dataSvc.doSetcurrentData(oItem)">編</button>
      </td>
      <td>{{oItem.ProductID}}</td>
      <td>{{oItem.ProductName}}</td>
      <td>{{oItem.SupplierID}}</td>
      <td>{{oItem.CategoryID}}</td>
      <td>{{oItem.QuantityPerUnit}}</td>
      <td>{{oItem.UnitPrice}}</td>
      <td>{{oItem.UnitsInStock}}</td>
      <td>{{oItem.UnitsOnOrder}}</td>
      <td>{{oItem.ReorderLevel}}</td>
      <td>
          <input type="checkbox" [(ngModel)]="oItem.Discontinued">
      </td>
    </tr>
  </tbody>
</table>
{{dataSvc.editMode}}<br>
{{dataSvc.currentData|json}}
<hr>

執行結果如下:

 

單筆維護介面

接著,我們處理單筆維護的介面。在editMode==Edit的時候,我們希望多筆顯示的區塊不顯示,另外一個區塊(單筆維護)改已顯示這個
這部分可以透過ngif來處理

我們先將目前的多筆查詢+分頁,用一個div包起來,並且再另一個div用來作編輯區,多筆查詢區,當editMode==''時,顯示,另外,編輯區則是ngif editMode!='' 的時候

另外,在編輯區,新增一個按鈕「取消」,click的時候呼叫prod service中的doCancel,將editMode的屬性內容重設為'',這樣就可以恢復瀏覽模式。

prod.service:

  doSetcurrentData(oData:any){
    this.currentData=oData;
    this.editMode='Edit';
  }

  doCancel(){
    this.editMode = '';
  }

prod.component.html:

<div *ngIf="dataSvc.editMode==''">
  <table border="1">
    <thead>
      <tr>
        <th>功能</th>
        <th>ProductID</th>
        <th>ProductName</th>
        <th>SupplierID</th>
        <th>CategoryID</th>
        <th>QuantityPerUnit</th>
        <th>UnitPrice</th>
        <th>UnitsInStock</th>
        <th>UnitsOnOrder</th>
        <th>ReorderLevel</th>
        <th>Discontinued</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let oItem of dataSvc.datas">
        <td>
          <button type="button" (click)="dataSvc.doSetcurrentData(oItem)">編</button>
        </td>
        <td>{{oItem.ProductID}}</td>
        <td>{{oItem.ProductName}}</td>
        <td>{{oItem.SupplierID}}</td>
        <td>{{oItem.CategoryID}}</td>
        <td>{{oItem.QuantityPerUnit}}</td>
        <td>{{oItem.UnitPrice}}</td>
        <td>{{oItem.UnitsInStock}}</td>
        <td>{{oItem.UnitsOnOrder}}</td>
        <td>{{oItem.ReorderLevel}}</td>
        <td>
            <input type="checkbox" [(ngModel)]="oItem.Discontinued">
        </td>
      </tr>
    </tbody>
  </table>
    <!--分頁開始-->
    <span><button (click)="dataSvc.GetDatasByPage(1)">第一頁</button></span>
    <span *ngIf="(dataSvc.startPg-10)>=1">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.startPg-1)">...{{dataSvc.startPg-1}}</button>
    </span>
    <span *ngFor="let pn of dataSvc.pageNumArray">
      <button (click)="dataSvc.GetDatasByPage(pn)" [ngClass]="{currentPage: pn==dataSvc.currentPageNum}">{{pn}}</button>
    </span>
    <span *ngIf="dataSvc.pageNumArrayMax < dataSvc.totalPage">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.pageNumArrayMax+1)">{{dataSvc.pageNumArrayMax+1}}...</button>
    </span>
    <span><button (click)="dataSvc.GetDatasByPage(dataSvc.totalPage)">最後一頁</button></span>
    每頁筆數:
    <select name="sltPageSize" id="sltPageSize" [(ngModel)]="dataSvc.pageSize" (change)="dataSvc.GetDatasByPage(dataSvc.currentPageNum)">
      <option value="5">5</option>
      <option value="10">10</option>
      <option value="15">15</option>
      <option value="20">20</option>
    </select>
    <!--分頁結束-->
</div>
  {{dataSvc.editMode}}<br>
  {{dataSvc.currentData|json}}
  <hr>
<div *ngIf="dataSvc.editMode!=''">
  編輯區
  <br>
  <button (click)="dataSvc.doCancel()">取消</button>
</div>

執行查詢模式時:

按下『編』按鈕後

此時再按下『取消』,則會恢復為多筆查詢模式。

套用Bootstrap

我們為了畫面的美觀,並可以響應式,將專案安裝上ng2-bootstrap,安裝的方式請參考以下

https://github.com/valor-software/ng2-bootstrap/blob/development/docs/getting-started/ng-cli.md

套用後,畫面稍微修改如下:

<div *ngIf="dataSvc.editMode==''">
  <table class="table table-hover table-striped table-bordered">
    <thead>
      <tr class="info">
        <th>功能</th>
        <th>ProductID</th>
        <th>ProductName</th>
        <th>SupplierID</th>
        <th>CategoryID</th>
        <th>QuantityPerUnit</th>
        <th>UnitPrice</th>
        <th>UnitsInStock</th>
        <th>UnitsOnOrder</th>
        <th>ReorderLevel</th>
        <th>Discontinued</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let oItem of dataSvc.datas">
        <td>
          <button type="button" (click)="dataSvc.doSetcurrentData(oItem)" class="btn">編</button>
        </td>
        <td>{{oItem.ProductID}}</td>
        <td>{{oItem.ProductName}}</td>
        <td>{{oItem.SupplierID}}</td>
        <td>{{oItem.CategoryID}}</td>
        <td>{{oItem.QuantityPerUnit}}</td>
        <td>{{oItem.UnitPrice}}</td>
        <td>{{oItem.UnitsInStock}}</td>
        <td>{{oItem.UnitsOnOrder}}</td>
        <td>{{oItem.ReorderLevel}}</td>
        <td>
            <input type="checkbox" [(ngModel)]="oItem.Discontinued">
        </td>
      </tr>
    </tbody>
  </table>
    <!--分頁開始-->
    <span><button (click)="dataSvc.GetDatasByPage(1)">第一頁</button></span>
    <span *ngIf="(dataSvc.startPg-10)>=1">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.startPg-1)">...{{dataSvc.startPg-1}}</button>
    </span>
    <span *ngFor="let pn of dataSvc.pageNumArray">
      <button (click)="dataSvc.GetDatasByPage(pn)" [ngClass]="{currentPage: pn==dataSvc.currentPageNum}">{{pn}}</button>
    </span>
    <span *ngIf="dataSvc.pageNumArrayMax < dataSvc.totalPage">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.pageNumArrayMax+1)">{{dataSvc.pageNumArrayMax+1}}...</button>
    </span>
    <span><button (click)="dataSvc.GetDatasByPage(dataSvc.totalPage)">最後一頁</button></span>
    每頁筆數:
    <select name="sltPageSize" id="sltPageSize" [(ngModel)]="dataSvc.pageSize" (change)="dataSvc.GetDatasByPage(dataSvc.currentPageNum)">
      <option value="5">5</option>
      <option value="10">10</option>
      <option value="15">15</option>
      <option value="20">20</option>
    </select>
    <!--分頁結束-->
</div>
  {{dataSvc.editMode}}<br>
  {{dataSvc.currentData|json}}
  <hr>
<div *ngIf="dataSvc.editMode!=''">
  編輯區
  <br>
  <button (click)="dataSvc.doCancel()" class="btn">取消</button>
</div>

執行後,畫面UI就變成

我們編輯區,修改單筆維護Form的相關如下:

<div class="container" *ngIf="(dataSvc.editMode!='')">
  <div class="row">
    <form class="form-horizontal">
      <fieldset>
        <legend>Product</legend>
        <div class="form-group" *ngIf="dataSvc.editMode=='Edit'">
          <label for="inputProductID" class="col-lg-2 control-label">ProductID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputProductID" name="ProductID" placeholder="ProductID" [(ngModel)]="dataSvc.currentData.ProductID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputCategoryID" class="col-lg-2 control-label">CategoryID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputCategoryID" name="CategoryID" placeholder="CategoryID" [(ngModel)]="dataSvc.currentData.CategoryID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputPGUID" class="col-lg-2 control-label">PGUID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputPGUID" name="PGUID" placeholder="PGUID" [(ngModel)]="dataSvc.currentData.PGUID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputProductName" class="col-lg-2 control-label">ProductName</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputProductName" name="ProductName" placeholder="ProductName" [(ngModel)]="dataSvc.currentData.ProductName">
          </div>
        </div>
        <div class="form-group">
          <label for="inputQuantityPerUnit" class="col-lg-2 control-label">QuantityPerUnit</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputQuantityPerUnit" name="QuantityPerUnit" placeholder="QuantityPerUnit" [(ngModel)]="dataSvc.currentData.QuantityPerUnit">
          </div>
        </div>
        <div class="form-group">
          <label for="inputReorderLevel" class="col-lg-2 control-label">ReorderLevel</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputReorderLevel" name="ReorderLevel" placeholder="ReorderLevel" [(ngModel)]="dataSvc.currentData.ReorderLevel">
          </div>
        </div>
        <div class="form-group">
          <label for="inputSupplierID" class="col-lg-2 control-label">SupplierID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputSupplierID" name="SupplierID" placeholder="SupplierID" [(ngModel)]="dataSvc.currentData.SupplierID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitPrice" class="col-lg-2 control-label">UnitPrice</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitPrice" name="UnitPrice" placeholder="UnitPrice" [(ngModel)]="dataSvc.currentData.UnitPrice">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitsInStock" class="col-lg-2 control-label">UnitsInStock</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitsInStock" name="UnitsInStock" placeholder="UnitsInStock" [(ngModel)]="dataSvc.currentData.UnitsInStock">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitsOnOrder" class="col-lg-2 control-label">UnitsOnOrder</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitsOnOrder" name="CategoryID" placeholder="UnitsOnOrder" [(ngModel)]="dataSvc.currentData.UnitsOnOrder">
          </div>
        </div>
        <div class="form-group">
          <label for="chkDiscontinued" class="col-lg-2 control-label">Discontinued</label>
          <div class="col-lg-10">
            <div class="checkbox">
              <label>
                <input type="checkbox" id="chkDiscontinued" name="Discontinued" [(ngModel)]="dataSvc.currentData.Discontinued"> Discontinued
              </label>
            </div>
          </div>
        </div>
        <div class="form-group">
          <div class="col-lg-10 col-lg-offset-2">
            <button type="button" class="btn btn-default"
              (click)="dataSvc.editMode=''"
            >取消</button>
          </div>
        </div>
      </fieldset>
    </form>
  </div>
</div>

在表單中,直接將Prod Service中的currentData與各控制項做雙向綁定,因此,在點選資料後,可以立即的看到資料,而且修改資料後,可以立即反映到Prod Service中的currentData。

接著在Prod Service中,我們加上新增、修改、刪除的相關function,去存取對應的API如下:


  //新增
  doInsert(){
    let url=this.serverName +'/api/Prod';
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});
    this.http.post(url,this.currentData, options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.editMode = '';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

  //修改
  doUpdate(){
    let url=this.serverName +'/api/Prod';
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.put(url,this.currentData,options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.currentData = {};
        this.editMode='';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

  //刪除
  doDelete(){
    let ProductID:number = this.currentData.ProductID;
    let url=this.serverName +'/api/Prod/Delete/' + ProductID.toString();
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.delete(url,options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.editMode='';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

另外,我們希望按下刪除時,可以多一個confirm來確認是否真的要刪除,所以在prod.component.ts中,新增一個doDelete的function

  doDelete(){
    if(confirm('您確定要刪除此筆資料嗎?')){
      this.dataSvc.doDelete();
    }
  }

最後,prod.component中,我們在單筆維護介面中,加上維護新增、修改、刪除的按鈕,如下:

            <button class="btn btn-primary" *ngIf="dataSvc.editMode=='AddNew'"
              (click)="dataSvc.doInsert()"
            >維護新增</button>
            <button type="button" class="btn btn-primary" *ngIf="dataSvc.editMode=='Edit'"
             (click)="dataSvc.doUpdate()"
             >更新</button>
             <button type="button" class="btn" *ngIf="dataSvc.editMode=='Edit'"
              (click)="doDelete()"
             >刪除</button>

如此單筆維護的介面差不多囉,還差一個新增單筆的按鈕。我們在畫面table的上方新增一個按鈕,讓editMode切換為AddNew,醬子就大功告成囉。

完成後畫面如下:

按下新增:

點選單一筆資料:

按下刪除,出現確認對話視窗

就醬子,多筆查詢+分頁+單筆維護的CRUD完成。

整理最終程式碼如下:

Data Service : Prod.Service.ts

import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class ProdService {

  //API來源名稱
  serverName:string='http://localhost:54514/'

  //取回的內容
  datas:any=[];
  error:any;
  currentData:any={};   //被選取作用中的單筆資料
  editMode:string='';   //記錄編輯模式為何?Edit:編輯中, AddNew:新增中,空字串為非作用中,初始值為''非作用中。

  //分頁相關屬性開始
  currentPageNum:number=1;
  pageSize:number=10;
  totalPage:number=0;

  pageNumArray:number[]=[];   //用以存放所有分頁的數字(3頁就會是1,2,3)
  pageNumArrayMax:number=0;   //分頁數的陣列最大數
  startPg:number=0;     //分頁陣列,開始的頁數
  //分頁相關屬性結束


  constructor(private http:Http) {
    this.GetDatasByPage(1);
  }


  //分頁相關開始
  //  設定分頁控制項的陣列
  set_pageNumArray(){
      //初始劃分頁陣列
        this.pageNumArray=[];
        let i:number=0;

        //計算起始頁
        this.startPg = Math.floor((this.currentPageNum-1)/10)*10+1;

        if(this.startPg+10>this.totalPage){
          this.pageNumArrayMax = this.totalPage;
        }
        else{
          this.pageNumArrayMax = this.startPg+10-1;
        }
        for(i=this.startPg;i<=this.pageNumArrayMax;i++){
            this.pageNumArray.push(i);
        }
  }
  //分頁相關結束


  GetDatasByPage(pageNum:number){
    this.currentPageNum = pageNum;
    let url:string=this.serverName + 'api/prod/' + this.currentPageNum.toString() + '/' + this.pageSize.toString();
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.get(url, options)
    .subscribe(
      (value:Response)=>{
        let jData:any;
        jData = value.json();
        this.datas = jData.oPages;
        this.totalPage = jData.TotalPage;
        this.set_pageNumArray();
      },
      (error)=>{
        this.error = error;
      }
    );
  }

  doSetcurrentData(oData:any){
    this.currentData=oData;
    this.editMode='Edit';
  }

  doCancel(){
    this.editMode = '';
  }

  //新增
  doInsert(){
    let url=this.serverName +'/api/Prod';
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});
    this.http.post(url,this.currentData, options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.editMode = '';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

  //修改
  doUpdate(){
    let url=this.serverName +'/api/Prod';
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.put(url,this.currentData,options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.currentData = {};
        this.editMode='';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

  //刪除
  doDelete(){
    let ProductID:number = this.currentData.ProductID;
    let url=this.serverName +'/api/Prod/Delete/' + ProductID.toString();
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.delete(url,options)
    .subscribe(
      (value:Response)=>{
        this.GetDatasByPage(this.currentPageNum);
        this.editMode='';
      },
      (error)=>{
        this.error = error;
      }
    );
  }

}

Prod Component : 

ts:

import { ProdService } from './../prod.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-prod',
  templateUrl: './prod.component.html',
  styleUrls: ['./prod.component.css']
})
export class ProdComponent implements OnInit {

  constructor(public dataSvc:ProdService) { }

  ngOnInit() {
  }

  doDelete(){
    if(confirm('您確定要刪除此筆資料嗎?')){
      this.dataSvc.doDelete();
    }
  }
}

css:

.currentPage{background-color:yellow;}

html:

<div *ngIf="dataSvc.editMode==''">
  <button (click)="dataSvc.editMode='AddNew'">新增</button>
  <table class="table table-hover table-striped table-bordered">
    <thead>
      <tr class="info">
        <th>功能</th>
        <th>ProductID</th>
        <th>ProductName</th>
        <th>SupplierID</th>
        <th>CategoryID</th>
        <th>QuantityPerUnit</th>
        <th>UnitPrice</th>
        <th>UnitsInStock</th>
        <th>UnitsOnOrder</th>
        <th>ReorderLevel</th>
        <th>Discontinued</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let oItem of dataSvc.datas">
        <td>
          <button type="button" (click)="dataSvc.doSetcurrentData(oItem)" class="btn">編</button>
        </td>
        <td>{{oItem.ProductID}}</td>
        <td>{{oItem.ProductName}}</td>
        <td>{{oItem.SupplierID}}</td>
        <td>{{oItem.CategoryID}}</td>
        <td>{{oItem.QuantityPerUnit}}</td>
        <td>{{oItem.UnitPrice}}</td>
        <td>{{oItem.UnitsInStock}}</td>
        <td>{{oItem.UnitsOnOrder}}</td>
        <td>{{oItem.ReorderLevel}}</td>
        <td>
            <input type="checkbox" [(ngModel)]="oItem.Discontinued">
        </td>
      </tr>
    </tbody>
  </table>
    <!--分頁開始-->
    <span><button (click)="dataSvc.GetDatasByPage(1)">第一頁</button></span>
    <span *ngIf="(dataSvc.startPg-10)>=1">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.startPg-1)">...{{dataSvc.startPg-1}}</button>
    </span>
    <span *ngFor="let pn of dataSvc.pageNumArray">
      <button (click)="dataSvc.GetDatasByPage(pn)" [ngClass]="{currentPage: pn==dataSvc.currentPageNum}">{{pn}}</button>
    </span>
    <span *ngIf="dataSvc.pageNumArrayMax < dataSvc.totalPage">
      <button (click)="dataSvc.GetDatasByPage(dataSvc.pageNumArrayMax+1)">{{dataSvc.pageNumArrayMax+1}}...</button>
    </span>
    <span><button (click)="dataSvc.GetDatasByPage(dataSvc.totalPage)">最後一頁</button></span>
    每頁筆數:
    <select name="sltPageSize" id="sltPageSize" [(ngModel)]="dataSvc.pageSize" (change)="dataSvc.GetDatasByPage(dataSvc.currentPageNum)">
      <option value="5">5</option>
      <option value="10">10</option>
      <option value="15">15</option>
      <option value="20">20</option>
    </select>
    <!--分頁結束-->
</div>

<div class="container" *ngIf="(dataSvc.editMode!='')">
  <div class="row">
    <form class="form-horizontal">
      <fieldset>
        <legend>Product</legend>
        <div class="form-group" *ngIf="dataSvc.editMode=='Edit'">
          <label for="inputProductID" class="col-lg-2 control-label">ProductID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputProductID" name="ProductID" placeholder="ProductID" [(ngModel)]="dataSvc.currentData.ProductID" readonly>
          </div>
        </div>
        <div class="form-group">
          <label for="inputCategoryID" class="col-lg-2 control-label">CategoryID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputCategoryID" name="CategoryID" placeholder="CategoryID" [(ngModel)]="dataSvc.currentData.CategoryID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputProductName" class="col-lg-2 control-label">ProductName</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputProductName" name="ProductName" placeholder="ProductName" [(ngModel)]="dataSvc.currentData.ProductName">
          </div>
        </div>
        <div class="form-group">
          <label for="inputQuantityPerUnit" class="col-lg-2 control-label">QuantityPerUnit</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputQuantityPerUnit" name="QuantityPerUnit" placeholder="QuantityPerUnit" [(ngModel)]="dataSvc.currentData.QuantityPerUnit">
          </div>
        </div>
        <div class="form-group">
          <label for="inputReorderLevel" class="col-lg-2 control-label">ReorderLevel</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputReorderLevel" name="ReorderLevel" placeholder="ReorderLevel" [(ngModel)]="dataSvc.currentData.ReorderLevel">
          </div>
        </div>
        <div class="form-group">
          <label for="inputSupplierID" class="col-lg-2 control-label">SupplierID</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputSupplierID" name="SupplierID" placeholder="SupplierID" [(ngModel)]="dataSvc.currentData.SupplierID">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitPrice" class="col-lg-2 control-label">UnitPrice</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitPrice" name="UnitPrice" placeholder="UnitPrice" [(ngModel)]="dataSvc.currentData.UnitPrice">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitsInStock" class="col-lg-2 control-label">UnitsInStock</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitsInStock" name="UnitsInStock" placeholder="UnitsInStock" [(ngModel)]="dataSvc.currentData.UnitsInStock">
          </div>
        </div>
        <div class="form-group">
          <label for="inputUnitsOnOrder" class="col-lg-2 control-label">UnitsOnOrder</label>
          <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUnitsOnOrder" name="CategoryID" placeholder="UnitsOnOrder" [(ngModel)]="dataSvc.currentData.UnitsOnOrder">
          </div>
        </div>
        <div class="form-group">
          <label for="chkDiscontinued" class="col-lg-2 control-label">Discontinued</label>
          <div class="col-lg-10">
            <div class="checkbox">
              <label>
                <input type="checkbox" id="chkDiscontinued" name="Discontinued" [(ngModel)]="dataSvc.currentData.Discontinued"> Discontinued
              </label>
            </div>
          </div>
        </div>
        <div class="form-group">
          <div class="col-lg-10 col-lg-offset-2">
            <button class="btn btn-primary" *ngIf="dataSvc.editMode=='AddNew'"
              (click)="dataSvc.doInsert()"
            >維護新增</button>
            <button type="button" class="btn btn-primary" *ngIf="dataSvc.editMode=='Edit'"
             (click)="dataSvc.doUpdate()"
             >更新</button>
             <button type="button" class="btn" *ngIf="dataSvc.editMode=='Edit'"
              (click)="doDelete()"
             >刪除</button>
            <button type="button" class="btn btn-default"
              (click)="dataSvc.editMode=''"
            >取消</button>
          </div>
        </div>
      </fieldset>
    </form>
  </div>
</div>

末記:

單一資料表CRUD,其中Component的html與資料提供的Data Service彼此關係還蠻密切的。除了資料外,大部分使用到的變數,都設定在Service中的參數,然後直接取用。這樣的方式撰寫起來,與之前在Angular 1.x時開發的感覺有點點雷同。只是以前1.X會將這些變數寫在Controller,但Angular 2則是寫在Service中。其中,分頁的部分屬性與function,其實可以抽出來放一個類別。沒有必要每個CRUD都寫一遍,分頁抽出類別後,撰寫Service的時候,再import進來使用即可。

小喵將CRUD的相關程式記錄下來,提供自己與網友們參考。


2017/10/2 補充:

這一篇的表格,雖然具有分頁功能,但基本的還是缺了排序的功能,相關『排序』的部分,我們補充了一篇,相關連結如下:

[筆記][angular][排序] angular 2 / 4 查詢畫面增加 排序功能

^_^

 

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6) 
topcat
Blog:http://www.dotblogs.com.tw/topcat