tag:crieit.net,2005:https://crieit.net/tags/Angular/feed
「Angular」の記事 - Crieit
Crieitでタグ「Angular」に投稿された最近の記事
2019-12-23T03:48:26+09:00
https://crieit.net/tags/Angular/feed
tag:crieit.net,2005:PublicArticle/15633
2019-12-22T23:20:13+09:00
2019-12-23T03:48:26+09:00
https://crieit.net/posts/ionic-angular-WebComponents
@ionic/angularがWebComponentsでテンプレートチェックを効かせるためにやっていることを調べる
<h1 id="AngularでWebComponetsライブラリを使う時の基本"><a href="#Angular%E3%81%A7WebComponets%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AE%E5%9F%BA%E6%9C%AC">AngularでWebComponetsライブラリを使う時の基本</a></h1>
<p>CustomElementsは基本的にはHTMLElementです(正確にはHTMLElementを継承して作ったもの)。<code>customElements.define()</code>することでグローバルに存在するようになるので、なんらかの方法でライブラリをインクルードすれば、あとはtemplateに書いておけば動きます。Angularから見れば、<code>input</code>タグでも<code>custom-input</code>タグでも同じ話なわけです。</p>
<pre><code class="html"><input type="text"/>
<custom-input type="text"></custom-input>
</code></pre>
<p>ただし、Angularのテンプレートチェックに<code><input></code>はわかるけど<code><custom-input></code>は知らないやつだなあと怒られるので対策が必要です。</p>
<pre><code class="ts">@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA], ←templateチェックを無効にする
})
export class SomeModule {}
</code></pre>
<p>これで自由にHTML要素を書いてもよくなりますが、テンプレートの事前チェックが無効化されて実行時の存在判定に後送りされるため、他のタイポなども道連れで検出できなくなります。</p>
<pre><code class="html"><typo-input type="text"></typo-input> ←どこにも定義されてないがビルド時にエラーにならない
</code></pre>
<p>このようにAngularでWebComponetsを使うには<strong>誓約と制約</strong>があります。</p>
<h1 id="@ionic/angularがやっていること"><a href="#%40ionic%2Fangular%E3%81%8C%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8">@ionic/angularがやっていること</a></h1>
<p>さて、ionicのv4はWebComponentsベースで書き直され、Angularだけでなくreactやvueでもできるようになりましたが、Angularは引き続きテンプレートチェックがビシッと効いています。</p>
<p>不思議なので中身を調べてみましょう。</p>
<p>なんと、同じ名称のAngularComponentが定義されていました❗️</p>
<p>ソースを読むとプロキシという扱いのようなので、これをプロキシコンポーネントと呼びます。<br />
どうやらこいつがビルド時にAngularに差し出されているから、テンプレートの型チェックが効いてるようです。</p>
<p><code><ion-input></code>を例に引っ張ってきました。現バージョンだと<code>@ProxyCmp</code>という新デコレータが作られていて、<code>proxyMethods</code>や<code>proxyInputs</code>の記述は省力化されていますが、構造がわかりやすいので旧スタイルで掲載しています。</p>
<pre><code class="ts">export declare interface IonInput extends StencilComponents<'IonInput'> {}
@Component({
selector: 'ion-input',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
inputs: [
'accept',
'autocapitalize',
'autocomplete',
'autocorrect',
'autofocus',
'clearInput',
'clearOnEdit',
'color',
'debounce',
'disabled',
'inputmode',
'max',
'maxlength',
'min',
'minlength',
'mode',
'multiple',
'name',
'pattern',
'placeholder',
'readonly',
'required',
'size',
'spellcheck',
'step',
'type',
'value',
],
})
export class IonInput {
ionInput!: EventEmitter<CustomEvent>;
ionChange!: EventEmitter<CustomEvent>;
ionBlur!: EventEmitter<CustomEvent>;
ionFocus!: EventEmitter<CustomEvent>;
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionInput', 'ionChange', 'ionBlur', 'ionFocus']);
}
}
proxyMethods(IonInput, ['setFocus', 'getInputElement']);
proxyInputs(IonInput, [
'color',
'mode',
'accept',
'autocapitalize',
'autocomplete',
'autocorrect',
'autofocus',
'clearInput',
'clearOnEdit',
'debounce',
'disabled',
'inputmode',
'max',
'maxlength',
'min',
'minlength',
'multiple',
'name',
'pattern',
'placeholder',
'readonly',
'required',
'spellcheck',
'step',
'size',
'type',
'value',
]);
</code></pre>
<h2 id="全体構造"><a href="#%E5%85%A8%E4%BD%93%E6%A7%8B%E9%80%A0">全体構造</a></h2>
<p>通常のAngularComponentに則ったデコレータとclassです。<code>proxyOutputs</code>、<code>proxyMethods</code>、<code>proxyInputs</code>はionicが用意しているユーティリティ関数です。</p>
<h2 id="生のWebComponentsに対してバインドする方法"><a href="#%E7%94%9F%E3%81%AEWebComponents%E3%81%AB%E5%AF%BE%E3%81%97%E3%81%A6%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95">生のWebComponentsに対してバインドする方法</a></h2>
<h3 id="接続"><a href="#%E6%8E%A5%E7%B6%9A">接続</a></h3>
<p>コンポーネントデコレータのselectorをcustomElementと同じ名称にしています。相当ハックだと思ったのですが、Angularのコンポーネントはカスタム要素としてHTMLにそのまま出力するので、selectorをHTMLに存在している要素名にするとHTMLではHTMLに存在している要素として解釈するわけなんですよね。もちろん<code>_nghost-wke-c2</code>のような属性は付きます。良い子は真似するな案件ですが、customElementに限らず標準HTML要素の<code>input</code>とか<code>span</code>とかでも同じことできます。</p>
<h3 id="プロキシ"><a href="#%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7">プロキシ</a></h3>
<p>コンポーネントでは、elementRefから引っ張ったnativeElementの生DOM参照をクラスプロパティに保持しています。<br />
proxyInputsとproxyMethodsは、そのクラスプロパティに保持したnativeElementを通して生の<code>ionic-input</code>に対して、HTML属性のget/setやionic内部に用意されているメソッドを生やしています。</p>
<pre><code class="ts">export const proxyInputs = (Cmp: any, inputs: string[]) => {
const Prototype = Cmp.prototype;
inputs.forEach(item => {
Object.defineProperty(Prototype, item, {
get() {
return this.el[item];
},
set(val: any) {
this.z.runOutsideAngular(() => (this.el[item] = val));
}
});
});
};
export const proxyMethods = (Cmp: any, methods: string[]) => {
const Prototype = Cmp.prototype;
methods.forEach(methodName => {
Prototype[methodName] = function () {
const args = arguments;
return this.z.runOutsideAngular(() =>
this.el[methodName].apply(this.el, args)
);
};
});
};
</code></pre>
<p>proxyOutputsは、rxjsの<code>fromEvent</code>(中でaddEventListenerやremoveEventListenerしながらObservable化するやつ)を、コンポーネントの各EventEmitter型のプロパティにセットしているものです。コンポーネントclassに同名のクラスプロパティが用意されています。</p>
<pre><code class="ts">export const proxyOutputs = (instance: any, el: any, events: string[]) => {
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
}
</code></pre>
<h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1>
<p>プロキシするコンポーネントを用意することで、グローバルに存在しているWebComponentsライブラリでも、Angularのテンプレートの中でまるでAngularComponentかのように扱えるようになり、テンプレートの型チェックが効くようになっています。</p>
studioTeaTwo
tag:crieit.net,2005:PublicArticle/15079
2019-06-09T23:29:17+09:00
2019-06-10T04:54:16+09:00
https://crieit.net/posts/ionic-AngulaCLI
ionicをAngulaCLIで使う
<p>ionicは長らくAngularとcordovaを用いたUIフレームワークでしたが、最新バージョンではwebComponentsベースになり、reactでもvueでも生jsでもなんでもいけるようになりました。</p>
<p>素晴らしいんですが、逆に構成パターンがめっちゃ増えて迷うというところもあります。<br />
Angularをベースにする上でもパッと3パターンがありえます。</p>
<ol>
<li>ionic CLIを用いる(従来通り)</li>
<li>scriptタグで@ionic/coreをインクルードしてピュアWebComponentとして利用する</li>
<li>angularCLIプロジェクトに@ionic/angularをインストールして利用する</li>
</ol>
<p>本記事では3番を取り上げます。普通にnpmPackageとして組み込むだけで、組み込んだ後はいつものngコマンドで開発します。</p>
<hr />
<h1 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h1>
<p>最初にインストールします。angularCLIで作成したプロジェクトの前提です。</p>
<pre><code>$ npm install @ionic/angular
</code></pre>
<p><code>ng add @ionic/angular</code>ができそうな記述も見られますが、Anuglar v8ではダメです。さっとコードを見た限りだとangular.jsonのスキーマ変更をキャッチアップすればいけそうな感もありましたが、ionic v4はもはや別ライブラリでは?というくらい内部的には激しい刷新が行われているので焦らず待ちましょうという判断しました。</p>
<p>ionicCLIでは<code>*.page.ts</code>とかの独自レイヤーがあるようですが、このやり方ではそういうのは考えない感じです。</p>
<p>他にも<code>@ionic/angular-toolkit</code>というcordvaやschematicsのプラグインを提供しているパッケージがありますが、現時点では要らなそうです。cordvaはただのwebアプリなら要らないですし、schematicsは上述のようにもう少し待ちましょう判断です。もしかしたらng addだけでなく<code>*.page.ts</code>とかをng generateできるようになるのかな。</p>
<h1 id="コンフィグレーション"><a href="#%E3%82%B3%E3%83%B3%E3%83%95%E3%82%A3%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">コンフィグレーション</a></h1>
<p>普通にNgModuleをimportすればOKです。WebComponentなので、Angularのtemplateコンパイラチェックを外すために<code>CUSTOM_ELEMENTS_SCHEMA</code>を加えます。</p>
<p>app.module.ts</p>
<pre><code class="typescript">import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
IonicModule.forRoot(),
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
</code></pre>
<p>ルートのcssファイルにスタイルをインポートします。著者はscssでAngularCLIを起こしていますが、それぞれ置き換えてください。</p>
<pre><code class="scss">/* Core CSS required for Ionic components to work properly */
@import "~@ionic/angular/css/core.css";
/* Basic CSS for apps built with Ionic */
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";
/* Optional CSS utils that can be commented out */
@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";
@import "~@ionic/angular/css/display.css";
</code></pre>
<p>アイコンは若干トリックが要りました。参照解決してくれなかったのでassetsにコピーして利用しています。<br />
この辺りは、<code>ng add @ionic/angular</code>を実装している<a target="_blank" rel="nofollow noopener" href="https://github.com/ionic-team/ionic/blob/master/angular/src/schematics/add/index.ts">schematicsのコード</a>が大変参考になります。というか、ピックアップして手動でコピペします。</p>
<p>angular.json</p>
<pre><code class="json">{
"projects": {
"architect": {
"build": {
"options": {
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg"
}
],
}
}
}
}
}
</code></pre>
<p>アイコンタグでは相対URLを記述します。</p>
<pre><code class="html"><ion-item button routerLink="/home">
<ion-icon slot="start" src="/assets/svg/md-home.svg"></ion-icon>
<ion-label> Home </ion-label>
</ion-item>
</code></pre>
<p>以上となりますが、基本的にはschematicsの<a target="_blank" rel="nofollow noopener" href="https://github.com/ionic-team/ionic/blob/master/angular/src/schematics/add/index.ts"><code>ng add</code>のコード</a>を教科書にして構成するとよいです。<code>ng add @ionic/angular</code>コマンドそのものは動きませんが、リファレンスには最適です。</p>
studioTeaTwo
tag:crieit.net,2005:PublicArticle/14479
2018-07-11T13:04:02+09:00
2018-08-31T01:19:22+09:00
https://crieit.net/posts/Angular-Github-Pages
AngularのプロジェクトをGithub Pagesに公開する
<p>Crieit初投稿です。元記事は<a target="_blank" rel="nofollow noopener" href="https://chocol4t.hatenablog.com/entry/2018/07/07/144504">こちら</a></p>
<p>AngularのプロジェクトをGithub Pagesにデプロイしたときのメモ。</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/angular-schule/angular-cli-ghpages">angular-cli-ghpages</a>というパッケージを使用することで簡単にデプロイ出来た。</p>
<h2 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h2>
<p>コマンドの利用するには、次の条件を満たしておく必要がある。</p>
<ul>
<li>Node.js: 4.x以上</li>
<li>Git: 1.7.6以上</li>
<li>(オプション)angular-cliで作成されたプロジェクト</li>
</ul>
<p>今回実行した環境は次の通り。</p>
<ul>
<li>MacBook Air (Early 2015)</li>
<li>macOS High Sierra (10.13.5)</li>
<li>angular-cli: 6.0.7</li>
<li>Node.js: 8.11.2</li>
<li>Git: 2.17.1</li>
</ul>
<h2 id="使い方"><a href="#%E4%BD%BF%E3%81%84%E6%96%B9">使い方</a></h2>
<p>angular-cli v6だと、ビルド時のディレクトリ構成が<code>dist/REPOSITORY_NAME</code>となっている。</p>
<p>そのため、<code>ng build</code>もしくは<code>angular-cli-ghpages</code>でディレクトリを指定する必要がある。</p>
<p>今回は<code>angular-cli-ghpages</code>のオプションを使用した。</p>
<pre><code class="sh">ng build --prod --base-href "https://USERNAME.github.io/REPOSITORY_NAME/"
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"
</code></pre>
<p>もしくは、</p>
<pre><code class="sh">ng build --prod --base-href "/REPOSITORY_NAME/"
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"
</code></pre>
<p>もしくは、</p>
<pre><code class="sh"># <base href="">となる
ng build --prod
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"
</code></pre>
<p>を実行すると、リモートへプッシュが行われる。</p>
<p><code>angular-cli-ghpages</code>の代わりに<code>ngh</code>でも実行可能。</p>
<h2 id="オプション"><a href="#%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3">オプション</a></h2>
<p>コマンド実行時にいくつかオプション設定が可能。</p>
<p>使用したものだけ記載しているので、他のオプションについては<a target="_blank" rel="nofollow noopener" href="https://github.com/angular-schule/angular-cli-ghpages/blob/master/README.md">README.md</a>を参照してください。</p>
<h3 id="--message"><a href="#--message">--message</a></h3>
<ul>
<li>コミット時のメッセージを設定。デフォルトは<code>Auto-generated commit</code></li>
<li>e.g. <code>ngh --message="New message"</code></li>
</ul>
<h3 id="--dir"><a href="#--dir">--dir</a></h3>
<ul>
<li>デプロイするディレクトリを設定。デフォルトは<code>dist</code></li>
<li>angular-cli v6だと<code>"outputPath": "dist/REPOSITORY_NAME"</code>となっているので注意</li>
<li>e.g. <code>ngh --dir="dist/REPOSITORY_NAME"</code></li>
</ul>
<h3 id="--branch"><a href="#--branch">--branch</a></h3>
<ul>
<li>プッシュ先のブランチ名を設定。デフォルトは<code>gh-pages</code></li>
<li>e.g. <code>ngh --branch=other-branch</code></li>
</ul>
<h2 id="Gihub Pagesの設定"><a href="#Gihub+Pages%E3%81%AE%E8%A8%AD%E5%AE%9A">Gihub Pagesの設定</a></h2>
<p><code>ngh</code>でプッシュされたのが確認出来れば以下を設定する。</p>
<ol>
<li>Githubのリポジトリの<code>Settings</code>タブをクリック。</li>
<li><code>Github Pages</code>の項目までスクロールし、<code>Source</code>からプッシュしたブランチを設定。</li>
<li>URLにアクセスしてページが表示されれば完了!</li>
</ol>
<h2 id="参考URL"><a href="#%E5%8F%82%E8%80%83URL">参考URL</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/angular-schule/angular-cli-ghpages">angular-schule/angular-cli-ghpages</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kasaharu/items/29117827a1417fed50e6">Angular アプリケーションを GitHub Pages で公開する方法 - Qiita</a></li>
</ul>
choco