跳到主要內容

發表文章

分散式系統的好幫手-RabbitMQ (下)

在上次的 文章 中間單的介紹RabbitMQ 如何發送和接收訊息,今天我們要來介紹一下RabbitMQ的四種 Exchange以及其路由規則。 1. Fanout Exchange: 就像是廣播一樣,發送時會將資料送到有綁定Fanout Exchange的Queue中。 例如:發送資料到fanout.exchange,則queue-1和queue-2都會收到資料。 public void FanoutPublisher() { ConnectionFactory factory = new ConnectionFactory() { HostName = "localhost" }; using (IConnection connection = factory.CreateConnection()) { using (IModel channel = connection.CreateModel()) { channel.QueueDeclare(queue: "queue-1", durable: false, exclusive: false, autoDelete: false, arguments: null); channel.QueueDeclare(queue: "queue-2", durable: false, exclusive: false, autoDelete: false, arguments: null); channel.ExchangeDeclare(exchange: "fanout.exchange", type: ExchangeType.Fanout, durable: true, autoDelete: false); channel.QueueBind(queue: "queue-1...

分散式系統的好幫手-RabbitMQ (上)

在分散式系統架構中,子系統之間應該要遵守低耦合的設計原則,盡可能地減少它們之間的相依性,然而我們要如何才能達到解耦合? 一般來說我們會使用Message Queue 作為分散式系統架構中的一個解耦合手段,簡單說地說一下它的運作方式,一般會有發佈者、接收者這兩種角色,發佈者將Message送到Queue ,接收者可以訂閱Queue,以便接收發佈者所傳送過來的Message,這種設計模式也就是所謂的觀察者模式。 目前市面上已經有很多Message Queue的框架,如ActiveMQ、RabbitMQ和RocketMQ, 而我們今天來簡單的地介紹最知名也最多人使用的RabbitMQ框架,它是由Erlang 語言開發且基於AMQP (Advanced Message Queuing Protocol) ,因此包含訊息、佇列、路由、可靠性、易用性、擴展性、安全性重要特徵,同時RabbitMQ也支援多樣的用戶端程式語言如Python、Ruby、.NET、Java、JavaScript、C、PHP。 現在我們以簡單的程式碼來介紹一下,如何在.NET使用RabbitMQ Client來建立MQ,並且進行發送與收取訊息: 1. 安裝 RabbitMQ.Client 。 2. 建立Queue後,接著綁定路由規則Exchange與RoutingKey 。 ConnectionFactory factory = new ConnectionFactory() { HostName = "localhost" }; using (IConnection connection = factory.CreateConnection()) { using (IModel channel = connection.CreateModel()) { channel.QueueDeclare(queue: "test-queue", durable: false, exclusive: false, autoDelete: false, argume...

ASP.NET Core 新手常見問題整理

ASP.Net Core 新手常見問題如下: 1. ASP.Net Core預設使用CamelCase作為命名規則,如何改用PascalCase作為命名規則 ? ※解答:在Startup.cs加入以下設定: public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()); } 2. 如何回傳JSON時忽略null欄位 ? ※解答:在Startup.cs加入以下設定: public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; }); } 3. 如何忽略JSON序列化時的循環參考 ? ※解答:在Startup.cs加入以下設定: public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; }); } 4. 如何將ASP.Net Core佈署至IIS ? ※解答: 佈署步驟如下: (1) 安裝.NET Core Windows Server Hosting (2) 新增站台並且將CLR版本改為沒有Managed程式碼 (3) 透過dotnet publish指令將編譯後的dll與dependency放到pu...

Angular Third Party : create an editable table using Handsontable

這次來介紹一個很棒的Table第三方套件 Handsontable ,其功能相當豐富像是編輯、排序、查詢、欄位驗證、匯出Excel等需求通通難不倒Handsontable,甚至可以直接將Excel的表格內容直接貼到Handsontable或者是將Handsontable的表格內容直接貼Excel,並且不會造成跑版和亂碼的問題。此外,前端三大主流框架(React、Angular、Vue)它都支援。 接下來,我們來簡單示範一下如何在Angular使用Handsontable。 1. 首先安裝Handsontable套件。 npm install @handsontable/angular handsontable 2. 在angular.json內匯入相關css。 "styles": [ "node_modules/handsontable/dist/handsontable.full.css", "src/styles.css" ] 3. 匯入Handsontable Module import { HotTableModule } from '@handsontable/angular'; @NgModule({ declarations: [ AppComponent ], imports: [ HotTableModule, ... ] }) export class AppModule { } 4. 使用hot-table元件且並設定settings,其常用屬性如下: colHeaders : 設定欄位的表頭名稱 data : 設定資料 dataSchema : 設定資料結構 columns : 可用於設定欄位的型別(text、autocomplete、dropdown、checkbox、password、date)、呈現方式(renderer)以及資料驗證(validator) ※客製化的呈現方式與資料驗證需要撰寫renderer callback 與validator callback 5. 如果需要對hot-table進行存取或是D...

Angular Third Party : ng-multiselect-dropdown(Style類似Bootstrap)

一般來說,表單內有欄位是可以選取多個選項時,我們會採用CheckBox或Multiselect Dropdown,但當選項過多時為了節省版面空間,我們通常會使用Multiselect Dropdown 以下拉勾選的方式來呈現。 目前在Angular幾個知名的元件像是Angular Material和PrimeNG都有Multiselect Dropdown元件可以用,但筆者使用的是Ngx-Bootstrap...很不幸地...它並沒有Multiselect Dropdown元件,所以可能要多花心力自己刻元件或是上網找找看其他符合Bootstrap Style的Multiselect Dropdown元件來用了。當然Ngx-Bootstrap也可以與Angular Material或PrimeNG的元件混用,但在視覺呈現上會顯得突兀並且有些奇怪。 最後很幸運地,筆者在npm找到了一個Multiselect Dropdown元件名為 ng-multiselect-dropdown ,它的Style類似Bootstrap且功能完備(如Filter、Limit Selection等功能),更重要的是它支持Template Driven Form和Reactive Form,其用法如下所示 : 1. 匯入 NgMultiSelectDropDownModule import { NgMultiSelectDropDownModule } from 'ng-multiselect-dropdown'; @NgModule({ imports: [ NgMultiSelectDropDownModule.forRoot(), ] }) export class AppModule { } 2. 在Reactive Form使用ng-multiselect-dropdown元件並設定settings,其常用屬性如下: singleSelection: 設定單選或多選 idField: 設定欄位的Id textField: 設定欄位的Text selectAllText: 設定選取所有項目時,所顯示的文字 unSelectAllText: 設定未選取項目時,所顯示的文字 limitSe...

Angular: let a singleton service create multiple instances

我們都知道Angular Service是Singleton Pattern,但是不幸的事... 我們有時會因為一些需求,希望能夠在多個元件中不要共用同一個Instance,而是在多個元件中使用不同的Instance。 事實上這個問題並不難,其實只需要短短幾行程式碼就可以解決,我們只要使用useClass並配合@Inject去注入Service就可以得到不同的Instance,其範例如下所示。 1. 首先,我們在providers它建立了兩個provider分別是Instance_1和Instance_2。 2. 接著在constructor使用@Inject去注入那兩個provider。 3. 最後你會發現Instance_1和Instance_2會是不同的Instance。 @Component({ selector: 'app-root', providers: [ { provide: 'Instance_1', useClass: StorageService }, { provide: 'Instance_2', useClass: StorageService } ], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor( @Inject('Instance_1') private Instance_1: StorageService, @Inject('Instance_2') private Instance_2: StorageService) { console.log(this.Instance_1 === this.Instance_2); } }

reCAPTCHA v3 的簡單教學

Google於10/29正式發布reCAPTCHA v3 API,該版本中最大的亮點就是透過分析使用者瀏覽網站的行為,以辨識使用者是否為機器人。換句話說,使用者再也不用一直回答問題和不停地點選圖片,所以說以後再也不用聽到客戶抱怨老是點錯圖片,果然科技始於人性啊~ 另外reCAPTCHA v3 的使用方式也非常簡單,只要短短幾個步驟,就能輕鬆地將reCAPTCHA v3運用在專案中。 1. 首先,至  https://www.google.com/recaptcha/admin  替你的網站註冊一個reCAPTCHA。 2. 註冊後,將會得到Site Key和Secret Key,其中Site Key是給前端使用,而Secret Key則是給後端使用。 3. 在前端HTML head的部分加入reCAPTCHA的api.js。 <script src='https://www.google.com/recaptcha/api.js?render=6Lc5vHgUAAAAAKHxlH0FdDJdA2-8yfzpoMDLIWPc'></script> 4. 接著在body的部分呼叫reCAPTCHA API向Google取得token後,再將token送至後端進行驗證,範例如下: <script> grecaptcha.ready(function () { console.log('1. grecaptcha.ready'); console.log('2. grecaptcha.execute("6Lc5vHgUAAAAAKHxlH0FdDJdA2-8yfzpoMDLIWPc", { action: "@Url.Action("VerifyBot", "Account")" })'); grecaptcha.execute('6Lc5vHgUAAAAAKHxlH0FdDJdA2-8yfzpoMDLIWPc', { action: '@...

Upgrade Angular 7

隨著Angular 7的到來,最近也將手邊的專案都順利的升級到Angular 7,可說是無痛無雷呀~ 不過還是有些地方是需要注意,就是在Angular 7開始不予許混用Template-Driven Form和Reactive Form,所以像下面這段程式碼是不能被編譯的。 <form [formGroup]="form"> <input formControlName="Email" [(ngModel)]="value"> </form> 實際上這樣的混合的做法很糟也很難維護和測試,只會讓品質變差,所以Angular 7把這個卡掉是很正確的事(拍手)。 題外話,目前大部分的Angular 開發者也幾乎都是以Reactive Form為主來開發表單,最主要的原因是它比Template-Driven Form要來好維護和測試,特別是對於大表單與動態欄位,更是要採用Reactive Form,絕對會省下不少測試維護的時間。 可想而知Angular 7不予許混用Template-Driven Form和Reactive Form這件事,其實影響層面不大。 另外一項值得注意的事,Angular 7加入Budget Bundles的機制。簡單來說,當Bundle Size超過設定的門檻值就會觸發警告和錯誤,所以你會在angular.json看到下面這段設定。 "budgets": [ { "type": "initial", "maximumWarning": "2mb", "maximumError": "5mb" } ] 專案升級至Angular 7的方式: 1. 如果專案還是使用Rxjs 5,請安裝 "npm i -g rxjs-tslint",並且執行"rxjs-...

JavaScript : How to copy properties of objects into another object ?

有時候我們可能會想要將某一個物件的屬性複製到另一個物件,所以我們可能會寫出像下面這樣的程式碼。 let order = { OrderID: 'A001', OrderDate: '2018-10-14' }; let items = [ { ItemName: '橡皮擦', Price: 15, Quantity: 10 }, { ItemName: '鉛筆', Price: 10, Quantity: 10 } ]; order.Items = items; // Output : // { // OrderID: 'A001', // Items: [ // { ItemName: '橡皮擦', Price: 15, Quantity: 10 }, // { ItemName: '鉛筆', Price: 10, Quantity: 10 } // ], // OrderDate: '2018-10-14' // } 如果我們有很多的屬性要做複製的話,向上面這樣的寫法實在太費時。 事實上,我們可以使用Object.assign()來輕鬆地完成這件事,它用於複製一個或多個物件,將它們所有可列舉的屬性到另一個目標物件。 let order = { OrderID: 'A001', OrderDate: '2018-10-14' }; let items = [ { ItemName: '橡皮擦', Price: 15, Quantity: 10 }, { ItemName: '鉛筆', Price: 10, Quantity: 10 } ]; let customer = { CustomerID : 'C001' }; ...

Angular : Difference between value and ngValue in reactive form

當我們從在Reactive Form取得欄位資料之後可能還需要做一些型別轉換。 例如Select的value為"ture"時,而它的型別是string,這時我們免不了一定要做型別轉換。 幸好,在Angular有ngValue可以使用,它與value的差別在於,value總是回傳string型別,而ngValue會回傳Object型別, 所以如果我們使用ngValue,其值為"true"時,我們得到的型別會是boolean,而不是string。 現在當我們了解value和ngValue之間的差異之後,那些型別轉換的瑣事自然就不存在了! // (1) 取出的型態會是string <select class="form-control" formControlName="Sent"> <option value="true">Yes</option> <option value="false">No</option> </select> // (2) 取出的型態會是string <select class="form-control" formControlName="Sent"> <option [value]="true">Yes</option> <option [value]="false">No</option> </select> // (3) 取出的型態會是boolean <select class="form-control" formControlName="Sent"> <option [ngValue]="true">Yes</option> <option [ngValue]="false...

Upload large files to web api using resumable.js

最近工作剛好遇到大檔案(> 2GB)上傳的需求,大檔案的上傳方式與平常處裡小檔案上傳當方式有些不同,對於大檔案上傳我們需要額外考量三個問題 : 1. 上傳逾時。 2  IIS 不支援超過2BG 的檔案上傳 3. 檔案在上傳過程中中斷。 上述這三個問題可以透過multipart/form-data和chuck 的方式,將檔案分割成無數個chunk,且給予每個chunk編號,如果傳送失敗就重新傳送chunk ,進而實現斷點續傳的功能。 目前市面三個有支援大檔案與斷點續傳的JavaScript Library分別有 Resumable.js 、 Dropzone.js 、jQuery Ajax File Upload,且它們底層皆是使用HTML5 File API,此外它們的作者都有提供Server Side Implementation Sample,而且連TypeScript的定義檔都有,真的都是佛心來著。 接著我們開始介紹如何使用Resumable.js 上傳檔案至Web API(.NET) : Server Side (Web API)的主要流程: 1. 首先,在Web API需要使用MultipartFormDataStreamProvider來取得每一次Resumable.js 所傳過來的chunk,而chunk由FormData和FileData組成,其中FormData記載Resumable.js所定義的chuck欄位,而FileData則存放chunk的檔案內容。 2. 接著從FormData得到這些Resumable.js所定義的chuck欄位resumableFilename、resumableIdentifier、resumableTotalChunks、resumableChunkNumber欄位,並且根據這些欄位我們將chunk儲存在Upload 目錄下。 3. 等待所有chunk都到位,將合併這些chunk並組成完整的檔案。 FileStorageController Class [RoutePrefix("api/FileStorage")] public class FileStorageController : ApiController { ...

HTML5 : Object URL

在上一篇介紹HTML5 File API的文章中 提到Data URI是一種文字型態的檔案格式,其格式為"data:<mime type>;base64,<data>", 範例:"data:image/png;base64,PCFET0NUWVB..." ,而Data URI 的好處就是它可以直接寫入 HTML,且img元素可以接受Data URI格式。 這次的文章主要是介紹運用Object URL將檔案儲存在瀏覽器。過去我們往往都會透過後端的Web API 將檔案路徑傳送到前端link或img元素,以便讓使用者下載或瀏覽,但這有一些問題,像是縮圖殘留,這時就是必須自己去寫一段Code清理Cache。如今我們可以透過HTML5 Object URL將檔案儲存在瀏覽器,而且由於檔案物件的生命週期與網頁綁在一起,所以當網頁被關閉,這些檔案物件就會被釋放,因此我們自然可以不用去處理縮圖殘留的問題。 事實上Object URL的儲存機制就是利用GUID向瀏覽器提取檔案物件,其而格式為"blob:<url>;<GUID>", 範例:"blob:https://www.myblogger.com/e75c76a2-138a-4b17-b9f6-029199e3985f" Object URL 程式碼範例如下所示: 1. 首先,在Input註冊Chang事件以便取得File。 2. 我們利用URL.createObjectURL去建立ObjectURL,以及取得ObjectURL String。 3. 最後,將得到的ObjectURL String丟到link 元素,此時我們可以透過download屬性指定下載時的檔案名稱。 ※ 如果想要使連結失效並且釋放檔案物件,可以呼叫URL.revokeObjectURL來註銷ObjectURL。 <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title...

HTML5 : File API

今天我們示範使用HTML5 File API來讀取檔案內容。雖然市面上有很多好用且功能又強大的File Uploader ,但是骨子裡還是用HTML5 File API,所以不妨多去了解一些HTML5的東西對於前端開發絕對是有益處的。 File API的幾個重要的核心類別 : Blob(Binary Large Object): 表示二進制資料,此外還可以設定MIME類型。 File: 表示檔案且繼承於Blob,但與Blob不同的是它增加name、lastModified、lastModifiedDate、webkitRelativePath等屬性。 FileList: 表示 files的集合。 FileReader: 以非同步的方式讀取檔案內容,以下幾種方法與事件 : 1. readAsArrayBuffer:讀取檔案後,並回傳 ArrayBuffer 物件。 2. readAsBinaryString:讀取檔案後,並回傳 Binary String 。 (非標準,建議不要使用) 3. readAsDataURL:讀取檔案後,並回傳 DataURI 。 4. readAsText:讀取檔案後,並傳回文字內容(預設UTF-8)。 5. onload : 讀取完檔案後,所執行的事件。 6. abort:取消讀取。 ※Data URI 是一種文字型態的檔案格式,其格式為"data:<mime type>;base64,<data>", 範例:"data:image/png;base64,PCFET0NUWVB..." ,而Data URI 的好處就是它可以直接寫入 HTML,且img元素可以接受Data URI格式。 ※若要以同步的方式讀取檔案請使用FileReaderSync  File API 程式碼範例如下所示: 1. 首先,在Input註冊Chang事件以便取得File。 2. 建立FileReader物件,並且註冊onload事件用於接收回傳的DataURL。 3. 執行readAsDataURL來讀取File,讀取完檔案後會觸發onload事件。 <!DOCTYPE html> <head> <meta c...

MongoDB: Save Files Using GridFS

過去我們在使用File System,我們必須自己處理備份、複製、擴充的問題;如今我們可以我們可以使用MongoDB作為File DB,它可以利用Replica和Sharing的機制幫助我們解決備份、複製、動態擴充、分散式儲存、自動平衡、故障回復的問題,且效能優於RDBMS。若真要說MongoDB這類NoSql的缺點就是它不能處理Transaction。 在MongoDB中對大於16MB BSON Document(如:圖片、音頻、影片等)是使用GridFS的方式做儲存。 GridFS是一種在MongoDB中存儲大二進製文件的機制,GridFS 會將文件分割成多個Chunk(預設256 KB),而GridFS使用fs.files和fs.chunks等兩個Collection來存儲檔案資料,其中fs.files負責存放文件的名稱、大小、上傳的時間等資訊,而fs.chunks則是負責存放經過分割後的Chunks,其優點是透過分割儲存的方式能夠快速讀取檔案中任何的片段。 fs.files Collection { "_id" : , // 檔案的Unique ID "filename": data_string, //檔案名稱 "length" : data_number, // 檔案大小 "chunkSize" : data_number, // chunk大小,預設256k "uploadDate" : data_date, // 儲存時間 "md5" : data_string //檔案的md5值 } fs.chunks Collection { "_id" : , // 檔案chunk的Unique ID "files_id" : , //對應檔案的Unique ID "n" : chunk_number, // 檔案chunk的數量 "data" : data_binary, // 以二進為儲存檔案 } 在下面例子,我們將簡單地示範使用.NET MongoDB Driver來存取與操作MongoDB的G...

Angular Route Guards : CanActivate和CanActivateChild的差異

在幾天前,同事向我請教關於Angular Router的CanActivate和CanActivateChild的差別,所以想寫這篇文章來說明CanActivate和CanActivateChild的路由行為。 在Angular的Route Guards包含以下四種 : CanActivate : 用於控制是否允許進入路由。 CanActivateChild :用於控制是否允許進入子路由。 CanDeactivate : 用於控制是否允許離開路由。 CanLoad : 用於控制是否允許延遲載入Module。 Resolve : 用於在路由啟動ㄉ之前取得路由資料。 由上可知,雖然CanActivate和CanActivateChild同是用於控制是否允許進入路由,但CanActivateChild只針對子路由,其行路由為會有些不同。 以下面例子來說: 以為CanActivate例,當我們第一次拜訪A/B,Guard_1和Guard_2都會被執行,但是如果我們接著導向A/C,此時只有Guard_2會被執行。 那如果我們又接續著導向E/F會怎樣呢 ? 答案是Guard_1和Guard_2都會被執行,因為A/C導向至A/C並不是子路由的切換,所以Guard_1也會被執行。 const routes: Routes = [{ path: 'A', canActivate: [ Guard_1 ], children: [ { path: 'B', component: B_Component, canActivate: [ Guard_2 ] }, { path: 'C', component: C_Component, canActivate: [ Guard_2 ] } ] },{ path: 'E',...

Angular : NgComponentOutlet And NgTemplateOutlet

在 上一篇文章 中,我們使用ComponentFactoryResolver動態產生Component。 今天我們來介紹ngComponentOutlet這個Directive,它的使用方式比ComponentFactoryResolver簡單許多,可以更輕鬆容易地達成動態產生Component的工作。 ngComponentOutlet的使用方式如下: <ng-container *ngComponentOutlet="YourComponentType; injector: YourInjector; content: YourContent; ngModuleFactory: YourModuleFactory;"> </ng-container> ※除了指定嵌入的Component Type,還有其他Input 選項,如injector 、content、ngModuleFactory。 ※injector : 當Component 需要使用到服務時,需配合ReflectiveInjector做注入的動作。 ※content : 當Component有用到<ng-content>時,這個欄位可以被用來插入要顯示在<ng-content> 的內容。 ※ngModuleFactory : 當Component 來自其他Module 時,需配合NgModuleFactory和Compiler做載入的動作。 如果我們拿 上一篇文章 的例子以ngComponentOutlet來改寫的話會變成這樣: <div *ngFor="let note of NoteComponents"> <ng-container *ngComponentOutlet="note"> </ng-container> </div> 看起來是不是比ComponentFactoryResolver還要更加簡潔和簡單!! 另外,如果我們今天要嵌入的是一個Template而不是Component ,那...

Angular : Dynamic Component

在 上一篇文章 中,我們使用Reactive Form動態產生表單欄位,但有時候我們難免也會遇到需要動態產生Component的需求。舉例來說,各位如果有用過Google Keep,它的每一個Note就是一個獨立的Component,當每次想要新增一個Note,勢必需要以動態的方式來產生Note Component。 這次我們以Note例子示範使用ComponentFactoryResolver動態產生Component,其重點流程如下: 1. 新增Note Component,並且在NgModule裡面加入entryComponents: [NoteComponent]。 import { Component, OnInit} from '@angular/core'; import { FormGroup, FormControl} from '@angular/forms'; @Component({ selector: 'app-note', templateUrl: './note.component.html', styleUrls: ['./note.component.css'] }) export class NoteComponent implements OnInit { Form: FormGroup; NoteID: number; constructor( ) { } ngOnInit() { this.InitForm(); } InitForm(): void { this.Form = new FormGroup({ Title: new FormControl(null), Content: new FormControl(null) }); } } <div class="col-sm-12"> <div class="card"> <div class="card-header font-weight-bold text-white bg-info...

Angular : Dynamically Creating Form Fields Using Reactive Form

一般我們使用Angular處裡表單的問題,對於資料結構較為複雜的Model,我們都會採用Data Driven的方式,也就是使用Reactive Form,因為這樣對於資料結構複雜的Model來說是比較容易維護與掌控。 回到今天的主題,我們將介紹使用 Reactive Form動態產生表單欄位。因為我們平常工作可能多少會遇到需要動態產生表單欄位的需求。舉例來說,像是訂單的問題,它的資料結構可能像是這樣: {OrderID:"A001", Items:[ {ItemName:"橡皮擦",Price:15,Quantity:10}, {ItemName:"鉛筆",Price:10,Quantity:10}, {ItemName:"筆記本",Price:25,Quantity:25} ]} 如果我們新增一筆Item,就需要動態的產生Item的DOM。其實在Angular要完成動態產生表單欄位這樣的需求,不管資料結構有多複雜,大致只需要用到 FormGroup、 FormArray、FormControl、ngFor、 track by 就能搞定了 !! 這裡我們將用訂單的例子,來示範使用 Reactive Form來動態產生表單欄位 1. 首先,我們使用FormGroup、 FormArray與FormControl將表單建立起來。 2. 為了能夠動態產生Items,對於欄位Items我們使用FormArray。 3. 建立AddItem、CreateItem,用於新增欄位。 4. 使用ngFor來產生出Items的DOM。 5. 利用trackBy ,減少DOM 更新次數,提升效能。 import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, FormArray } from '@angular/forms'; @Component({ selector: 'app-reactive-form-sample', templateUrl: './reactive-form-sample...

Upgrade to Angular 6

本次Angular 6 升級需要特別注意的事,在Angular 6將採用Rxjs 6,過去的Rxjs 5的語法將有些微的變動,但是沒關係只要乖乖依照官網上的步驟升級,就可以順利的無痛升級。 升級Angular 6的方式: 1. 更新Angular CLI (global and local)。    "npm update -g @angular/cli"    "npm update @angular/cli" 2. 執行"ng update --all" 更新所有相依性套件,此時既有的angular-cli.json轉為angular.json,點開angular.json會發現projects這個屬性,這意味著它可以讓多個Angular專案共用同一個 開發環境。 3. 雖然Angular 6 沒有破壞性更新,但Rxjs 6卻變動了不少,為了方便升級Rxjs 6,請全域安 裝"npm i -g rxjs-tslint"。 4. 執行rxjs-5-to-6-migrate -p src/tsconfig.app.json,會自動將專案內的Rxjs 5轉換成Rxjs 6的語法。 5. 如果專案內有相依的套件是Rxjs 6以前的版本的話,可以安裝rxjs-compat讓它向下相容。 "npm i rxjs-compat --save" 6. 如果專案想要轉成Progressive Web App (PWA) 架構,請執行"ng add @angular/pwa",會自動加入 ngsw-config.json、manifest.json 與 service worker 設定。 參考文獻 1.  https://update.angular.io/