[{"content":"Introduction I always forget how can i listen input event. So I write this post.\nListening Event With out Angular With out Angular framework we have to use javascript to bind event like this code\n\u0026lt;input type=\u0026#34;text\u0026#34; id=\u0026#34;myInput\u0026#34;\u0026gt; document.getElementById(\u0026#39;myInput\u0026#39;).addEventListener(\u0026#39;keyup\u0026#39;, myInput); function myInput(input) { console.log(input.keyCode); } EventList\nKeyCodeRef\nSourceCode\nWith Angular With Angular framework is more easier to bind event\n\u0026lt;input type=\u0026#34;text\u0026#34; (keyup.enter)=\u0026#34;onEnter()\u0026#34;\u0026gt; onEnter(): void { console.log(\u0026#34;submit\u0026#34;); } SourceCode\nyou can change another key like shift or anthoer you want\n","date":"2023-08-01T14:40:21+09:00","permalink":"https://tonny0531.github.io/post/angular/inputkeyevent/","title":"InputkeyEvent"},{"content":"前言 前面已經有寫了幾篇關於解耦合，抽象行為等等概念的說明了．\n而建置測試專案雖然已經做了第二次了，但發現兩次我都會忘記怎麼做，因此才特別寫了這篇初始化的文章出來\n碰到問題 在.NET Core 開發的過程中，都知道有IServiceProvider和IConfiguration這兩個介面可以取得實例或是Config的配置\n但正常情況下都被框架包好了，根本不用去考慮他怎麼做出來的．\n但在測試的途中，框架不會知道你是否要使用，因此也不會自動幫你建立一個出來，導致我們需要自己來建立．\n建立專案 由於我現在的開發環境是 Mac OS 因此這裡就只示範 VSCode \u0026amp; .NET CLI\ndotnet new nunit -n {ProjectName} 如果是6版本以上的話，會自動建立using.cs\n這個是GloableUsing的部分，如果有全域要使用的using的話可以考慮放到這裡\nNunit的介紹 正常來講在寫整合測試的時候，我們為了減少變因，因此都會做一次初始化以及完畢的行為\n例如：初始化建立相依的物件(DB,Redis等等相關連線)\n完畢的時候要刪除測試的資料等等行為\n而在NUnit中的屬性則叫做Setup(初始) \u0026amp; TearDown(結束)\n實際上寫出來會長得像這樣\n[SetUp] public void SetUp() { //初始化區塊  } [TearDown] public void TearDown() { //測試完成後執行的區塊  } 建立IServiceProvider \u0026amp; IConfiguration 建立方式其實沒有很困難，實際方式如下\nprotected IServiceCollection _services; protected IServiceProvider _sp; protected IConfiguration _configuration; [SetUp] public void SetUp() { // 先建立新的ServiceCollection  this._services = new ServiceCollection(); // 使用檔案進行Config抓取  var configuration = new ConfigurationBuilder().AddJsonFile(\u0026#34;appsettings.Test.json\u0026#34;); this._configuration = configuration.Build(); //add MongoDB  var appSettingsSection_MongoDB = _configuration.GetSection(\u0026#34;MongoDbConfig\u0026#34;); var appSettingsMongoDB = appSettingsSection_MongoDB.Get\u0026lt;DbConfig\u0026gt;(); this._services.AddScoped\u0026lt;IMongoStorage\u0026gt;(sp =\u0026gt; { return new MongoStorage(appSettingsMongoDB); }); this._services.AddSingleton\u0026lt;IMongoFileStorage\u0026gt;(sp =\u0026gt; { return new MongoFileStorage(appSettingsMongoDB); }); // 創建出ServiceProvider  this._sp = _services.BuildServiceProvider(); } [TearDown] public void TearDown() { IMongoStorage db = this._sp.GetRequiredService\u0026lt;IMongoStorage\u0026gt;(); Assert.AreEqual(\u0026#34;TestDB\u0026#34;,db.GetDB().DatabaseNamespace.DatabaseName, \u0026#34;DB Name must be TestDB\u0026#34;); BsonDocument empty = new BsonDocument(); DeleteResult deleteResult; deleteResult = db.GetCollection\u0026lt;UserInfo\u0026gt;(\u0026#34;UserInfo\u0026#34;).DeleteMany(empty); } } 當中比較需要特別注意的是 appsettings.Test.json 會需要在csproj裡面特別說明要輸出到輸出目錄，否則會抓不到Config導致報錯\n\u0026lt;ItemGroup\u0026gt; \u0026lt;None Update=\u0026#34;appsettings.Test.json\u0026#34;\u0026gt; \u0026lt;CopyToOutputDirectory\u0026gt;Always\u0026lt;/CopyToOutputDirectory\u0026gt; \u0026lt;/None\u0026gt; \u0026lt;/ItemGroup\u0026gt; 而因為這些比較算是每個測試都會使用的Init 所以其實可以抽成一個BasicTestClass 後續有相關測試的Class可以直接繼承使用，就不用每個Class都寫一次了．\n總結 測試其實不難，真正難的會是在開發過程中的設計，如何設計商業邏輯以及低耦合才是最困難的點．\n","date":"2023-04-09T13:55:48+09:00","permalink":"https://tonny0531.github.io/post/dotnet/test/init/","title":"NUnit Test Project Init"},{"content":"前言 前面一篇提到了 DI 的概念，透過 DI 的方式來實現解耦合，而這篇則是要來講 DI 的一些進階應用了\n工廠模式 先說明工廠模式的運作方式，直接看Code比較快\n資料來源\n// Empty vocabulary of actual object public interface IPerson { string GetName(); } public class Villager : IPerson { public string GetName() { return \u0026#34;Village Person\u0026#34;; } } public class CityPerson : IPerson { public string GetName() { return \u0026#34;City Person\u0026#34;; } } public enum PersonType { Rural, Urban } /// \u0026lt;summary\u0026gt; /// Implementation of Factory - Used to create objects. /// \u0026lt;/summary\u0026gt; public class Factory { public IPerson GetPerson(PersonType type) { switch (type) { case PersonType.Rural: return new Villager(); case PersonType.Urban: return new CityPerson(); default: throw new NotSupportedException(); } } } 這個案例裡面有兩個角色，分別是\n Rural Urban  而我只要告訴工廠，現在是哪種人，工廠就會建立相對應的實例會來給我．\n而我也不用關心他的細節是什麼，只要關心他有提供什麼方法即可(Interface)\n 封裝的概念：隱藏細節，注重提供什麼\n DI工廠 其實就只是將工廠做成Interface 放進 DIPool中\nPersonModel namespace DelegateFactory.Model { public interface IPerson { string GetName(); } public class Villager : IPerson { public string GetName() { return \u0026#34;Village Person\u0026#34;; } } public class CityPerson : IPerson { public string GetName() { return \u0026#34;City Person\u0026#34;; } } public enum PersonType { Rural, Urban } } FactoryClass namespace DelegateFactory.Factory { public interface IFactory { IPerson GetPerson(PersonType type); } public class PersonFactory : IFactory { public IPerson GetPerson(PersonType type) { switch (type) { case PersonType.Rural: return new Villager(); case PersonType.Urban: return new CityPerson(); default: throw new NotSupportedException(); } } } } Controller namespace DelegateFactory.Controllers; [ApiController] [Route(\u0026#34;[controller]\u0026#34;)] public class WeatherForecastController : ControllerBase { private readonly ILogger\u0026lt;WeatherForecastController\u0026gt; _logger; private readonly IFactory _factory; public WeatherForecastController(ILogger\u0026lt;WeatherForecastController\u0026gt; logger, IFactory factory) { _logger = logger; _factory = factory; } [HttpGet(\u0026#34;name/{role}\u0026#34;)] public ActionResult\u0026lt;string\u0026gt; GetName(string role) { bool isEnumRole =Enum.TryParse(role,out PersonType personType); if(isEnumRole){ string name = this._factory.GetPerson(personType).GetName(); return Ok(name); }else{ return BadRequest(\u0026#34;Unknow Role\u0026#34;); } } } 這樣起起來就有一個很基本的工廠模式，透過傳進來的 Role 來決定實際上拿到哪個實例並回傳\n結論 透過這樣的方式可以大幅的增加擴充的彈性，但相對的因為抽象，也會導致使用上會變有些不方便，例如:Debug，Code Review 等會變成有些困難\n因此是否要採用這種方式還需要特別根據團隊能力或商務邏輯來決定是否要採用\nSample Code https://github.com/tonny0531/DIFactory\n","date":"2023-04-06T12:48:19+09:00","permalink":"https://tonny0531.github.io/post/dotnet/di/factorywork/","title":"Dependency Injection 與工廠模式的結合(2)"},{"content":"Dependency Injection 簡介 Dependency Injection 是一種物件改變耦合的一種設計概念，通常會跟 IOC(Inversion Of Control) 一起談\n以下一個簡單的注入範例\n範例 故事 父母今天要教育小孩科目，但一對父母並不是只會擁有一個小孩，可能會有多個。因此我們把小孩當作參數傳給教育小孩這個方法，使其變得有彈性。\n方法中注入小孩其實就是 方法注入(Method Injection)\n創建小孩的過程會注入一個姓名，這種注入方式就是 建構式注入(Constructor Injection)\n透過物件設定值的方式，就是 屬性注入(Property Injection)\nStruct public interface IParent { void TeachChild (IClild child, Subject string); } public class Parent: IParent { public Parent () { } public void TeachChild (IClild child, string subject ) { Console.WriteLine($\u0026#34;Teach {child.GetName} about ${subject}\u0026#34;) } } public interface IChild { string GetName (); void SetAge(int age); } public class Child { private string _childName; private int _age; public Child (string name) { this._childName = name; } public string GetName() { return this._childName; } public void SetAge(int age) { this._age = age; } } .NET 的特別之處 當我們知道 DI 之後，就要來細講 NET 的 DI 概念了\n在 NET 的架構下，會建議改用框架預設的 IServiceCollection(IServiceProvider)\n來取代手動 DI ，而透過 IServiceProvider 取出來的物件\n概念如下圖\n NETDI \n先將所有的 Service 註冊到資料提供中心，如果有其他物件需要就在從資料提供中心中取出來\n這樣的好處是 Service 跟 其他物件的耦合性，會變成是跟資料提供中心耦合（詳細概念可參考前篇的AbstractClass比較Interface )\n而在 Service 註冊的時候，會有三種特性，這三種特性會改變資料提供中心對外的時候，是否要產生新的物件(生命週期）給需求者，分別是\n Singleton： 在 APP 的生命週期中，只會存在一個實例，因此每次取回都是相同的記憶體位置 Scoped： 在一個 Requeset 的過程中僅在第一次建立新的實例，第二次開始會取得與第一次同樣的實例 Transient：每次要都取回一個新的實例  結語 透過 DI 的方式可以解決物件之間高耦合的問題，以利未來要做調整或抽象會更加簡單，而下一篇再來說明怎麼透過原生的 DI Pull 達成工廠模式\n參考資料 NETDI\n","date":"2023-02-15T12:35:19+08:00","permalink":"https://tonny0531.github.io/post/dotnet/di/agenda/","title":"Dependency Injection 與工廠模式的結合(1)"},{"content":"前言 繼上一篇做了 Kestrel Port 的大概說明後，後來又發生了一個狀況\n因那個 App 會呼叫公司內部的 API 而公司內的API採用了 Https 引發了憑證不信任的問題\n以下有兩種避免的作法\n HttpClient 注入 HttpClientHandler  services.AddHttpClient\u0026lt;JY_COG_FactoryWorkClient\u0026gt;().ConfigurePrimaryHttpMessageHandler(sp =\u0026gt; { return sp.GetRequiredService\u0026lt;SSLClientHandler\u0026gt;(); }); public class SSLClientHandler : HttpClientHandler { public SSLClientHandler() { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } } 在 Docker Image 下新增信任憑證   目前看起來 Dotnet Core Image Kernel 是使用 Ubuntu 參考Ubuntu官網\n $ sudo apt-get install -y ca-certificates #預設已經有了 $ sudocp local-ca.crt /usr/local/share/ca-certificates #複製自己的憑證進去 $ sudo update-ca-certificates # 系統更新憑證 而因為我偷懶不想要每次匯，因此就寫在 DockerFile 中\nFROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY . . RUN dotnet restore \u0026quot;./{ProjectName}.csproj\u0026quot; RUN dotnet build \u0026quot;{ProjectName}.csproj\u0026quot; -c Release -o /app/build FROM build AS publish RUN dotnet publish \u0026quot;{ProjectName}.csproj\u0026quot; -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . #複製編譯完成結果 COPY root04.crt /usr/local/share/ca-certificates #複製憑證 RUN update-ca-certificates #更新憑證 ENTRYPOINT [\u0026quot;dotnet\u0026quot;, \u0026quot;{ProjectName}.dll\u0026quot;] 只是因為剛好這個 Image 算好找憑證匯入方式，若是其他Linux版本更新就要找個版本的更新方式了\n","date":"2023-02-08T11:31:27+08:00","permalink":"https://tonny0531.github.io/post/dotnet/certificate/","title":"Docker Image Certificate 信任問題"},{"content":"前言 .NET 會紅的一部份很大原因是因為終於實現了跨平台\n而原先相依於 IIS 的 HttpServer 也因為跨平台而改採用較小的 Kestrel\n 也是可以託管給IIS: 詳細參考\n 而會寫這篇最大的原因還是因為，使用了微軟官方的 DockerImage 進行包裝後，發現都還是 Kestrel 都只會吃到 80 Port\n DefaultListen80 \n而網路上爬了一些文，後來找到官方說明綁定的以下幾種方式\n No configuration   預設為 http:localhost:5000 \u0026amp; https:localhost:5001  使用系統環境變數   使用ASPNETCORE_URLS 來決定使用的Port   Docker 設定的預設為80，導致此次會發生此問題\n 使用參數來決定   使用 dotnet XXX.dll --urls http://+:1234  寫死在程式碼中(不推薦)  webBuilder.UseKestrel((context, serverOptions) =\u0026gt; { serverOptions.Configure(context.Configuration.GetSection(\u0026quot;Kestrel\u0026quot;)) .Endpoint(\u0026quot;HTTPS\u0026quot;, listenOptions =\u0026gt; { listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12; }); }); 透過 Appsetting.json 來設定(推薦)  Appsettings   設定完成後包起來就會發現已經吃到 Config 了  Success \n","date":"2023-02-08T10:02:24+08:00","permalink":"https://tonny0531.github.io/post/dotnet/kestrel_config/","title":"Kestrel監聽Port的設定方式"},{"content":"前言 Angular 提供了相當於 Object.keys 的 Pipe 給大家使用，但有個細節卻常常被忽略\n就是排序的行為，在官方預設會進行排序\n舉例 預期的資料如下\n 創建中 取消或未過審 募資中 募資成功 募資失敗  在開發的過程中想像應該也會是一樣的顯示才對\n但實質上會變成\n 創建中 募資失敗 募資中 募資成功 取消或未過審  可以看到其中兩筆順序已被對調\n回頭去看官方的 Pipe 的用法說明\n {{ input_expression | keyvalue [ : compareFn ] }} 後方會有 compareFn 可以注入 而固定回傳 0 就會變成原始資料了\nSampleCode\n參考資料  angular keyvalue pipe sort properties / iterate in order KevValuePipe  ","date":"2022-08-15T09:23:48+08:00","permalink":"https://tonny0531.github.io/post/angular/keyvaluepipe/","title":"KeyValuePipe使用細節"},{"content":"前言 最近在看同事們在分享 OOP(Object-oriented programming) 的觀念，在過程中他特別提到了抽象\n也是我今天寫這篇的原因，在過程中我假設了一種情境來討論物件應該怎麼切分比較好\n這裡要說明，這個討論的過程中的程式語言是以 Dotnet 來說明的，因此有些地方並不一定是用在每種語言上\n專有名詞 耦合(Coupling) 在我的認知裡面，耦合是指物件跟物件之間的關係，若高耦合的話，很高機會改了其中一個物件\n導致另外一個物件故障\n 也就是業界常說的改 A 壞 B\n 抽象類別(Abstract Class) 特性  不可以直接透過 New 來建立物件 可以訂定抽象方法，強迫繼承的物件去實驗方法 可以寫共用方法的邏輯在抽象類別層  介面(Interface) 特性  不可以實作，僅僅只是是規範而已 繼承的Class必須要實現規範才可當作成此介面   C#9.0打破了這個特性，但我不太能接受就是了\n 情境 有一個班級，裡面有學生和老師\n我們要定義出班級裡面的學生和老師\n學生和老師都有開始工作(上課)、結束工作(下課)\n並且由班級來決定是否開始工作(上課)\n物件切分 依據情境來看，我們可以切出有三個主要物件\n 班級 學生 老師   物件圖開始 \n由上方可見班級對老師、學生來說是耦合的，很高機會改動了學生這個物件，會連帶影響到班級\n最後面再來解決物件耦合的問題\n第一次抽象 基於老師與學生都是人的情況下，我們可以抽出一個抽象類別叫做 People\n並在 People 裡面定義開始工作與結束工作的抽象方法，待繼承的人實作\n 第一次抽象 \n好處 若未來有共用的方法要實作我只要修改 People 即可\n壞處 根據前面的類別圖可以看到 People 和 Class 仍是高耦合的情況\n People、Student、Teacher 本身就是同類，因此不算是耦合\n 第二次抽象(解耦合) 這邊解耦合的方法會採用 Interface 的方式來進行解耦合\n 第二次抽象 \nClass 與 People 的相依變成了一個抽象的 Interface ，後續不管 People 底下的類別怎麼變更，只要符合 Worker 的規範，就都不會影響到 Class 導致編譯錯誤而綁手綁腳的\n而若要做測試，也可以在 Woker 那層建立一個 FakeWoker 就可以透過注入的方式來實現 Mock、Stub 了\n結語 透過上述的抽象，可以很明顯看出 Interface 才是可以真正拿出來解耦合的\n只要下面實作的類別抽換了，仍然可以工作，反而抽象類別是無法進行抽換的。\n","date":"2022-03-25T15:59:54+08:00","permalink":"https://tonny0531.github.io/post/developer/abstract_interface/","title":"AbstractClass比較Interface"},{"content":"主旨 最近幾天工作上都在使用 MongoDB，但常常忘記一些指令的格式\n因此特別紀錄一下，並說明一下怎麼使用\nLookup 在 MongoDB 初期並沒有所謂的 Join\n只有單純的 Collection Search\n但由於單一文本最大限制 16MB 的存在 And 物件管理上的設計\n而衍伸出了 Lookup 這個 Operator\n可以理解成他就是 Sql 的 Join\n{ $lookup: { from: \u0026lt;foreign collection\u0026gt;, localField: \u0026lt;field from local collection's documents\u0026gt;, foreignField: \u0026lt;field from foreign collection's documents\u0026gt;, let: { \u0026lt;var_1\u0026gt;: \u0026lt;expression\u0026gt;, …, \u0026lt;var_n\u0026gt;: \u0026lt;expression\u0026gt; }, pipeline: [ \u0026lt;pipeline to run\u0026gt; ], as: \u0026lt;output array field\u0026gt; } }  from: 關聯到哪個Collection localField: 當前Collection的欄位名稱 foreignField: 關聯到Collection的欄位名稱 let: 相當於 Sql 的要甚麼欄位並且命名 pipeline: 可以在關聯到的資料庫多做一次Pipline Ex: $match、$gt(e)\u0026hellip;. as: 查詢出來的新欄位名稱  select \u0026lt;let\u0026gt; as where [currentCollection] a , \u0026lt;from\u0026gt; b a.localField = b.foreignField 實際例子 Orders db.orders.insertMany( [ { \u0026#34;_id\u0026#34; : 1, \u0026#34;item\u0026#34; : \u0026#34;almonds\u0026#34;, \u0026#34;price\u0026#34; : 12, \u0026#34;quantity\u0026#34; : 2 }, { \u0026#34;_id\u0026#34; : 2, \u0026#34;item\u0026#34; : \u0026#34;pecans\u0026#34;, \u0026#34;price\u0026#34; : 20, \u0026#34;quantity\u0026#34; : 1 }, { \u0026#34;_id\u0026#34; : 3 } ] ) inventory db.inventory.insertMany( [ { \u0026#34;_id\u0026#34; : 1, \u0026#34;sku\u0026#34; : \u0026#34;almonds\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;product 1\u0026#34;, \u0026#34;instock\u0026#34; : 120 }, { \u0026#34;_id\u0026#34; : 2, \u0026#34;sku\u0026#34; : \u0026#34;bread\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;product 2\u0026#34;, \u0026#34;instock\u0026#34; : 80 }, { \u0026#34;_id\u0026#34; : 3, \u0026#34;sku\u0026#34; : \u0026#34;cashews\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;product 3\u0026#34;, \u0026#34;instock\u0026#34; : 60 }, { \u0026#34;_id\u0026#34; : 4, \u0026#34;sku\u0026#34; : \u0026#34;pecans\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;product 4\u0026#34;, \u0026#34;instock\u0026#34; : 70 }, { \u0026#34;_id\u0026#34; : 5, \u0026#34;sku\u0026#34;: null, \u0026#34;description\u0026#34;: \u0026#34;Incomplete\u0026#34; }, { \u0026#34;_id\u0026#34; : 6 } ] ) Aggregate db.orders.aggregate( [ { $lookup: { from: \u0026#34;inventory\u0026#34;, localField: \u0026#34;item\u0026#34;, foreignField: \u0026#34;sku\u0026#34;, as: \u0026#34;inventory_docs\u0026#34; } } ] ) 結果 { \u0026#34;_id\u0026#34; : 1, \u0026#34;item\u0026#34; : \u0026#34;almonds\u0026#34;, \u0026#34;price\u0026#34; : 12, \u0026#34;quantity\u0026#34; : 2, \u0026#34;inventory_docs\u0026#34; : [ { \u0026#34;_id\u0026#34; : 1, \u0026#34;sku\u0026#34; : \u0026#34;almonds\u0026#34;, \u0026#34;description\u0026#34; : \u0026#34;product 1\u0026#34;, \u0026#34;instock\u0026#34; : 120 } ] } { \u0026#34;_id\u0026#34; : 2, \u0026#34;item\u0026#34; : \u0026#34;pecans\u0026#34;, \u0026#34;price\u0026#34; : 20, \u0026#34;quantity\u0026#34; : 1, \u0026#34;inventory_docs\u0026#34; : [ { \u0026#34;_id\u0026#34; : 4, \u0026#34;sku\u0026#34; : \u0026#34;pecans\u0026#34;, \u0026#34;description\u0026#34; : \u0026#34;product 4\u0026#34;, \u0026#34;instock\u0026#34; : 70 } ] } { \u0026#34;_id\u0026#34; : 3, \u0026#34;inventory_docs\u0026#34; : [ { \u0026#34;_id\u0026#34; : 5, \u0026#34;sku\u0026#34; : null, \u0026#34;description\u0026#34; : \u0026#34;Incomplete\u0026#34; }, { \u0026#34;_id\u0026#34; : 6 } ] }  如果要將 inventory_docs 轉成陣列單一物件屬性 可以透過 $unwind\n ","date":"2022-03-18T15:54:51+08:00","permalink":"https://tonny0531.github.io/post/mongodb/lookup/","title":"MongoDb Lookup 使用紀錄"},{"content":"原由 這幾天研究從 Medium 轉成自己架 Blog\n為了避免下一個人架設過程中一頭霧水(實質是為了推別人入坑)\n故撰寫此筆記\n不繼續使用 Medium 的原因 大部分工程師都喜歡喜歡用 Markdown 進行文件撰寫(包含我在內)\n但 Medium 是不支援的(有工具可以透過 MarkDown 轉成 Medium 文章)\n因此建議如果是想先養成撰寫 Blog 的習慣的話\n可以從 Medium 下手 但長期喜歡用 Markdown 撰寫的話\n強烈推薦自己架一個 Blog\n安裝Hugo Windows 透過 Chocolatey 安裝\nchoco install hugo-extended -confirm Mac brew install hugo 檢查安裝是否完成 透過查詢 Hugo 版本來進行確認是否安裝完成\nhugo version  Version \n建立新網頁 hugo new site \u0026lt;\u0026#39;SiteName\u0026#39;\u0026gt;  NewSite \n hugo 建立新網頁真的快\n 執行 Hugo Server hugo server 這時候開起來會是一片空白 這是正常的\n是因為現在實際上甚麼都沒有\n等套用了 Theme 就會正常了\n套用 Theme 這裡我採用了 Stack 來進行\n先建立 Git 並初始化\ngit init git add . git commit -m \u0026#34;Init\u0026#34; 透過 Submodule 的方式載入\ngit submodule add https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack 偷懶一點直接複製 Theme 裡面的 Config 來做使用\n themes\\hugo-theme-stack\\exampleSite\\config.yaml\n   複製到我們網站的目錄下\n  刪除 config.toml\n  並在檔案開頭加上一些屬性\n  baseURL:\u0026#39;http://example.org/\u0026#39;languageCode:\u0026#39;en-us\u0026#39;title:\u0026#39;Demo\u0026#39;# 你的網站名稱theme:\u0026#39;hugo-theme-stack\u0026#39;module:hugoVersion:extended:truemin:\u0026#34;0.87.0\u0026#34;params:mainSections:- postfeaturedImageField:imagerssFullContent:true這樣就套用上主題了\n Sample \n建立新頁面 透過 CLI 來建立一個新的文章\nhugo new post/\u0026lt;\u0026#39;newPageName\u0026#39;\u0026gt;.md 建立完成後應該還是看不到\n因為建立文章預設會將文章設定成草稿\n有兩種方式可以呈現\n 在啟動 Hugo Server 的時候追加屬性  hugo server -D 將文章屬性下的 Draft 改為 false  ---title:\u0026#34;Test\u0026#34;date:2021-10-29T01:11:07+08:00draft:false---這樣就是建立一個新文章的流程了\n這裡留個伏筆\n下一篇再來補關於Search、Archives為什麼不見了\n","date":"2021-10-28T14:52:48Z","permalink":"https://tonny0531.github.io/post/hugo_tutorial1/","title":"Hugo使用教學(1)"}]