Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
399

Go to list of users who liked

391

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

その使い方はもう古いかも?AngularJS老化チェック(ディレクティブ篇)

Last updated atPosted at 2016-02-07

こんにちは、らこです。AngularJSのバージョン1.5.0がリリースされましたね!

コードネームはennoblement-facilitation、ざっくり訳すなら「高尚化促進」でしょうか。コンポーネント志向の「高尚」なアプリケーション設計への移行をサポートするバージョンだということでしょう。

1.5.0ではcomponentの追加をメインに、Angular2へのスムーズな移行を行うための足がかりとなるアップデートです。いい機会なので、今の自分のAngularJSの使い方がどれくらい古いのか をチェックし、どのようにモダンにしていけばいいのかを知っておきましょう。

ちなみに、1.4までに関してはAngularJSモダンプラクティス - Qiita を参考にするとよいでしょう。
このモダンプラクティスに従ったコードになっていればcomponent()対応はあっという間のはずです。

到達目標 - component()

1.5.0から可能になるcomponent()によるカスタム要素の定義は次のように記述します。

<body><my-app></my-app><scriptsrc="app.js"></script></body>
constapp=angular.module("app",[]);classMyAppCtrl{}app.component("myApp",{template:`<greeting name="'World'"></greeting>`,controller:MyAppCtrl});classGreetingCtrl{getupperName(){returnthis.name.toUpperCase();}}app.component("greeting",{bindings:{name:"="},template:`<h1>Hello {{$ctrl.upperName}}!</h1>`,controller:GreetingCtrl});angular.bootstrap(document.body,[app.name]);

サンプル

ポイントは次の3点です

  • controllerにES6 Classを渡している
  • $scopeを使っていない
  • componentのテンプレート内で$ctrlを使っている

ES6での記述は機能とは直接関係はありませんが、Angular2を見据えたコードにするならばクラスにしておいて損はありません。フィールドの初期化もコンストラクタがあるので明確です。

データバインディングに$scopeを使っていないことも重要です。componentを使うと、デフォルトでcontrollerAs: "$ctrl"の状態になります。コンポーネント内の閉じたスコープが自動的に与えられ、コントローラのフィールドや関数に直接バインドすることができます。

なお、component()になってもdirective()と同じく定義するカスタム要素の名前はキャメルケースでmyApp のようにします。そしてHTML側ではハイフンケースでmy-app のように宣言します。

モダンなAngularJS - directive + scope + bTC + controllerAs

1.4まではcomponent()がないので、directive()でしかカスタム要素は定義できません。しかしscopeプロパティとbindToController (bTC)、controllerAsを使うことで同じようなコンポーネントを定義することができます。

functionMyAppCtrl(){}app.directive("myApp",()=>{return{restrict:"E",scope:{},template:`<greeting name="'World'"></greeting>`,controller:MyAppCtrl};});functionGreetingCtrl(){this.upperName=()=>this.name.toUpperCase();}app.directive("greeting",()=>{return{restrict:"E",scope:{},bindToController:{name:"="},template:`<h1>Hello {{$ctrl.upperName()}}!</h1>`,controller:GreetingCtrl,controllerAs:"$ctrl"};});

サンプル

実は1.5のcomponent()はこれと同じようなdirective()の呼び出しをラップしているだけです。また、bTCにオブジェクトを渡せるようになったのは1.4からです。

ナウいAngularJS - directive + scope + controllerAs

1.3まではbTCにオブジェクトが渡せないのでscopeと併用しないといけません。

functionMyAppCtrl(){}app.directive("myApp",function(){return{restrict:"E",scope:{},template:"<greeting name=\"'World'\"></greeting>",controller:MyAppCtrl};});functionGreetingCtrl(){this.upperName=function(){returnthis.name.toUpperCase();}}app.directive("greeting",function(){return{restrict:"E",scope:{name:"="},bindToController:true,template:"<h1>Hello {{$ctrl.upperName()}}!</h1>",controller:GreetingCtrl,controllerAs:"$ctrl"};});

サンプル

1.4のコードと殆ど変わらないですね。ここまでできていればほとんどコンポーネント化できていると言えるでしょう

古い - directive + scope + controllerAs

1.2以前はbTCがないので、親スコープからのデータバインディングは$scopeを介さないと受け取ることができません。しかし、controllerAsを使っているのでまだ自身のプロパティに関してはthisで扱えます。

functionMyAppCtrl(){}app.directive("myApp",function(){return{restrict:"E",scope:{},template:"<greeting name=\"'World'\"></greeting>",controller:MyAppCtrl};});functionGreetingCtrl($scope){this.upperName=function(){return$scope.name.toUpperCase();};}app.directive("greeting",function(){return{restrict:"E",scope:{name:"="},template:"<h1>Hello {{$ctrl.upperName()}}!</h1>",controller:GreetingCtrl,controllerAs:"$ctrl"};});

サンプル

ここまでは比較的すんなりcomponent()に移行しやすい(スコープの考え方がだいたい合ってる)コードだと思います。

時代遅れ - directive + scope + controller

controllerAsが導入されていないのでテンプレート中で変数名が直接登場し、コントローラのthisがスコープで参照されなくなりました。ただしscopeを使っていてisolated scopeがあります。また、コントローラでビューとロジックの分離がギリギリ出来ている状態です。

functionMyAppCtrl(){}app.directive("myApp",function(){return{restrict:"E",scope:{},template:"<greeting name=\"'World'\"></greeting>",controller:MyAppCtrl};});functionGreetingCtrl($scope){$scope.upperName=function(){return$scope.name.toUpperCase();};}app.directive("greeting",function(){return{restrict:"E",scope:{name:"="},template:"<h1>Hello {{upperName()}}!</h1>",controller:GreetingCtrl,};});

サンプル

このコードは1.5関係なく、早めにcontrollerAsを使ったスタイルに直すべきです。controllerAsについての議論は2014年あたりには決着がついており、

この辺りを読めばcontrollerAsを使うことによるメリット、使わないことによるデメリットがわかるはずです。

化石 - directive + link

directiveに紐付いたスコープが存在しないケースです。link関数やcompile関数は完全にcomponent()と互換性があるわけではないですが、カスタム要素の場合はほとんどのケースでlinkcompileも必要ないでしょう。directiveとcontrollerでロジックの分離もできていないのでとてもメンテ性の低いコードです。

app.directive("myApp",function(){return{restrict:"E",template:"<greeting name=\"World\"></greeting>"};});app.directive("greeting",function(){return{restrict:"E",template:"<h1>Hello {{upperName()}}!</h1>",link:function(scope,element,attrs){scope.name=attrs.name;scope.upperName=function(){returnscope.name.toUpperCase();};}};});

サンプル

論外 - html + ng-controller

カスタム要素によるコンポーネント化が全くなされていないコードです。

<divid="myApp"ng-controller="MyAppCtrl"><divid="greeting"ng-controller="GreetingCtrl"><h1>Hello {{upperName()}}!</h1></div></div>
app.controller("MyAppCtrl",["$scope",function($scope){$scope.name="World";}]);app.controller("GreetingCtrl",["$scope",function($scope){$scope.upperName=function(){return$scope.name.toUpperCase();};}]);

サンプル

綺麗にAngularJSを使うつもりがあればng-controllerは無くしましょう。せめてasを使って親スコープからの暗黙の継承を防ぎましょう。

これからやるべきこと

自分のAngularJSの現在地がなんとなくわかったでしょうか。それぞれの段階ごとに、適切な順序でコンポーネント志向なAngularJSにしていきましょう。

html + ng-controllerが残ってる場合

  1. まずはng-controller="SomeCtrl as some"にする
  2. ng-controllerのある要素をdirectiveに置き換える

すべてのコントローラをcontrollerAsにするのが最初の最優先課題です。
それができればあとは一番外側のコントローラから順番にdirectiveにしていきましょう。

directiveがcontrollerを使っていない場合

  1. directiveのlinkcompileはコントローラで代用
  2. directiveのscopeでスコープを分離する

次に最優先すべきはscopeを使ってdirectiveごとにスコープを分離することです。

bTCを使っていない場合

  1. AngularJS1.4以降に上げる
    2.scopeに渡していたオブジェクトをbTCに渡し、scopeを空オブジェクトにする

次は$scopeを使わずにすむようにdirectiveのスコープをコントローラにバインドしましょう。

1.4でbTCを使っていた場合

  1. AngularJS 1.5に上げる
  2. directive()の代わりにcomponent()を使う
  3. できればコントローラはES6 Classにする

完成です。ここまでくればAngular2も今いる場所の延長線上にあることがわかってくると思います。

まとめ

1.2から1.3、1.4とコンポーネント志向なdirectiveを作るための機能が少しずつ増えてきており、それらのベストプラクティスとして生まれたのがcomponent()です。
1.5以降はもうAngularJSとしての新しい機能は増えず、Angular2への移行サポートがメインになることがわかっています。
component()対応後にちゃぶ台を返される心配はないので、安心して1.5に向けたマイグレーションを行いましょう。

399

Go to list of users who liked

391
4

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
399

Go to list of users who liked

391

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp