Angular の electron application をそのまま build する
はじめに
Angular の electron application をそのまま build する方法をまとめます。
Angular の electron アプリは maximegris/angular-electron のようなボイラープレートをベースに開発する記事が多いですが、今回は利用せず electron-userland/electron-builder をそのまま利用していきます。
Angular の electron application をそのまま build する
まずは Angular CLI でプロジェクトを開始します。
ng serve
でいつもの画面が出ることを確認します。
$ ng new angular-electron
$ cd angular-electron
$ ng serve
Angular と electron の開発環境を作る
ng serve
で十分ではありますが electron の window で確認したくなることがあるかもしれません。
大した手間ではないので electron を devDependencies にインストールします。
$ npm install --save-dev electron@latest
package.json
に main
と scripts
に以下を追加します。
"main": "main.js", "scripts": { ... "start:electron": "ng build --base-href ./ && tsc -p tsconfig.electron.json && electron .", // 追加 ... }
package.json
と同じ階層の project root dir に main.ts
を配置します。
webPreferences
で webSecurity: false
を指定すると CORS を無視できるようです。
const { app, BrowserWindow } = require('electron'); const url = require('url'); const path = require('path'); let mainWindow; function createWindow(): void { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webSecurity: false, }, }); mainWindow.loadURL( url.format({ pathname: path.join(__dirname, `/dist/index.html`), protocol: 'file:', slashes: true, }) ); // Open the DevTools. mainWindow.webContents.openDevTools(); mainWindow.on('closed', () => { mainWindow = null; }); } app.on('ready', createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (mainWindow === null) { createWindow(); } });
今回 main.ts
と typescript ファイルとしたので tsconfig.electron.json
として tsconfig をproject root に配置します。
"target": "es2015"
でもそのまま動くようです。
{ "compilerOptions": { "sourceMap": true, "declaration": false, "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowJs": true, "target": "es2015", "types": ["node"], "lib": ["es2017", "es2016", "es2015", "dom"] }, "files": ["main.ts"], "exclude": ["node_modules", "**/*.spec.ts"] }
最後に angular.json
の outputPath
を dist
に変更します。
json はコメントアウトできないので削除してください。
... "options": { ... // "outputPath": "dist/angular-electron", <- 削除 "outputPath": "dist", ... }
それでは electron の開発環境を start してみましょう。
main.js
で mainWindow.webContents.openDevTools();
としているのでデバッグコンソールごと開きます。
$ npm run start:electron
Angular の electron application を build する
mac や windows 向けに electron を build していきます。
electron-builder を devDependencies にインストールします。
$ npm install --save-dev electron-builder
electron-builder.json
を project root に配置します。
このファイルの files
配下がビルドに含めるファイルとなります。
"output": "release/"
としているので angular の outputPath とは別に release
というフォルダに実行ファイルが吐かれます。
{ "productName": "angular-electron", "directories": { "output": "release/" }, "files": [ "**/*", "!**/*.ts", "!*.code-workspace", "!LICENSE.md", "!package.json", "!package-lock.json", "!src/", "!e2e/", "!hooks/", "!angular.json", "!_config.yml", "!karma.conf.js", "!tsconfig.json", "!tslint.json" ], "win": { "icon": "dist/assets/icons", "target": ["portable"] }, "mac": { "icon": "dist/assets/icons", "target": ["dmg"] }, "linux": { "icon": "dist/assets/icons", "target": ["AppImage"] } }
package.json
の scripts
に以下を追加します。
"scripts": { ... "electron:build:mac": "npm run build:electron && node_modules/.bin/electron-builder build --mac --x64", "electron:build:win": "npm run build:electron && node_modules/.bin/electron-builder build --win --x64", "build:electron": "npm run electron:tsc && ng build --base-href ./", "electron:tsc": "tsc -p tsconfig.electron.json" },
あとはターミナルから build するだけ!
$ # mac 向けの build $ npm run electron:build:mac $ # win 向けの build $ npm run electron:build:win
すると release
フォルダに dmg/exe ができているので実行するだけ!
macであれば application フォルダに入れちゃいなよって出ます。
まとめ
Angular の electron application をそのまま build する方法をまとめました。
github レポジトリに置いておいたので参考にしてください。
参考
material icons を css でカスタマイズしてログインアイコン作る
はじめに
material icons を css でカスタマイズしてよく見るログインアイコンを作る流れをまとめます。
早い話このアイコンです。
material icons を css でカスタマイズする流れ
まずはパクり参考にしたいiconのカラーコードを取得したいところです。
私は以下のサイトにパクり参考にしたい画像を放り込んでカラーコードを取得することがあります。
あとはhtml/cssを書くだけですね。
html
<button mat-icon-button > <mat-icon class="login-icon">person</mat-icon> </button>
css
.login-icon { background: #a4c4fc; color: #4374e3; -webkit-border-radius: 50%; border-radius: 50%; }
material icons は background 指定できるんですね。
と言う気付きでした。
まとめ
material icons を css でカスタマイズしてよく見るログインアイコンを作る流れをまとめます。
Angular Material で Dark Mode への切替をつくる
はじめに
Angular Material で Dark Mode への切替を作ります。
Angular Material
Angular Material の mat-light-theme
と mat-dark-theme
という予め準備されているものを利用します。
.light-theme
という class がつけばライトモードで .dark-theme
という class がつけばダークモードとし ngClass で切り替えます。
$app-light-primary: mat-palette($mat-orange, 600); $app-light-accent: mat-palette($mat-blue, 800); $app-light-warn: mat-palette($mat-red, 500); $app-dark-primary: mat-palette($mat-lime, A200); $app-dark-accent: mat-palette($mat-cyan, A400); $app-dark-warn: mat-palette($mat-red, A400); $app-light-theme: mat-light-theme($app-light-primary, $app-light-accent, $app-light-warn); $app-dark-theme: mat-dark-theme($app-dark-primary, $app-dark-accent, $app-dark-warn); .light-theme { @include angular-material-theme($app-light-theme); } .dark-theme { @include angular-material-theme($app-dark-theme); }
hasToggledTheme
という bool値を toggleさせます。
ドキュメントにある通り mat-app-background
という class で変化させたい対象を括ります。
<div [ngClass]="hasToggledTheme ? 'dark-theme' : 'light-theme'"> <div class="mat-app-background"> <mat-toolbar> <span>Theme Toggle</span> <span class="toolbar-spacer"></span> <mat-icon>brightness_5</mat-icon> <mat-slide-toggle [checked]="hasToggledTheme" (change)="toggledTheme()"></mat-slide-toggle> <mat-icon>brightness_2</mat-icon> </mat-toolbar> <!-- ここにボタン等をいれる --> </div> </div>
あとは typescript 側で toggle します。
toggledTheme() { this.hasToggledTheme = !this.hasToggledTheme; }
stackblitz 貼っておきます。
まとめ
Angular Material で Dark Mode への切替を作りました。
今回まとめた方法以外にも service から theme を持ってくる方法もあるようです。
ユーザーにもう少し theme をいじらせたい時はこういう感じにすると良いかもしれません。
go の構造体の cross field validation
go の middleware で status code を取得する
はじめに
go の net/http の middleware 内で status code を取得する方法をまとめます。
go の ResponseWriter という interface を status code 断面で深掘りするお話となります。
go の middleware で status code を取得する
go の net/http の middleware として rs/zerolog を使う で書いた通り logger などは status code に応じて処理を変えたいことがあります。
net/http の middleware は以下のような型を持っている必要があるので http.ResponseWriter
を Wrap して status code を取得してみます。
func Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) }) }
chi/middleware の NewWrapResponseWriter()
chi/middleware には NewWrapResponseWriter() という関数があり ResponseWriter を引数にとって status code を含む WrapResponseWriter という構造体を返してくれます。
この関数を使って status code を取得する流れを見ていきます。
ww := middleware.NewWrapResponseWriter(http.ResponseWriter, *http.Request.ProtoMajor)
fmt.Println("status code: ", ww.Status())
ResponseWriter
そもそも http.ResponseWriter
には status code というプロパティを持ってません。
ResponseWriter
は以下のような interface になってます。
type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) }
以下のような ResponseWriter
を Wrap して取り出しやすい位置に status code を置いた構造体 basicWriter
があります。
type basicWriter struct { http.ResponseWriter wroteHeader bool code int bytes int tee io.Writer }
この構造体に以下のように WriteHeader() を実装してあげると Wrap した構造体側にも status code を格納することができます。
func (b *basicWriter) WriteHeader(code int) { if !b.wroteHeader { b.code = code b.wroteHeader = true b.ResponseWriter.WriteHeader(code) } }
通常は WriteHeader は以下のように response に対して生えているメソッドになっていて w.status = code
として response へ status code を格納するような形で使われているようです。
func (w *response) WriteHeader(code int) { if w.conn.hijacked() { caller := relevantCaller() w.conn.server.logf("http: response.WriteHeader on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) return } if w.wroteHeader { caller := relevantCaller() w.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line) return } checkWriteHeaderCode(code) w.wroteHeader = true w.status = code if w.calledHeader && w.cw.header == nil { w.cw.header = w.handlerHeader.Clone() } if cl := w.handlerHeader.get("Content-Length"); cl != "" { v, err := strconv.ParseInt(cl, 10, 64) if err == nil && v >= 0 { w.contentLength = v } else { w.conn.server.logf("http: invalid Content-Length of %q", cl) w.handlerHeader.Del("Content-Length") } } }
chi/middleware
の basicWriter
では Status() というメソッドが生えているので、先ほど格納した status code が簡単に取得できるという流れでした。
func (b *basicWriter) Status() int { return b.code }
まとめ
go の middleware で status code を取得する流れについてまとめました。
net/http は何度読んでも勉強になります。