LoginSignup
3
2

More than 5 years have passed since last update.

Multi-slot transclusion - AngularJS v1.5

Posted at

AngularJS Advent Calendar 2015 7日目の投稿です。
v1.5 で追加された Multi-slot transclusion についてです。

Multi-slot transclusion

従来の AngularJS にも transclusion をする仕組みはありました。それは、directive を作る時に利用していた transcludeプロパティ です。ただし、1つの directive で transclusion をおこなうことができるのは1箇所だけでした。
Multi-slot transclusion では、その transclusion を複数箇所に対しておこなえるようにするための仕組みです。それが v1.5 から実装されました。先日書いた module.componentでも利用可能です。

とりあえず見てみる

以下のような構文です。Demoはこちらから。

<autocomplete-widget>
  <input type="text" ng-model="app.searchCriteria" placeholder="Search..." />

  <results>
    <div ng-repeat="item in app.getResults(app.searchCriteria) as results">
      {{ item }}
    </div>
    <div ng-if="results.length==0">
      No results found
    </div>
  </results>    
</autocomplete-widget>

上記のように、 <autocomplete-widget>タグの中に <input><results>が書かれています。
この2つの要素が、autocomplete-widgettemplate に transclusion されます。

<!-- autocomplete-widget-tpl.html -->
<div class="autocomplete-container">
  <div class="search-area">
    <div class="search-icon">
      <i class="fa fa-search"></i>
    </div>
    <div class="search-input" ng-transclude="inputArea"></div>
  </div>
  <div class="results-area">
    <div ng-transclude="resultsArea"></div>
  </div>
</div>

こちらが autocomplete-widgettemplate の実装です。
ng-transclude="inputArea"ng-transclude="resultsArea" となっているところが、今回追加された機能となります。

angular.module('MyApplication', [])
  .controller('ApplicationController', function() {
    ... // スコープの初期化
  })
  .component('autocompleteWidget', {
    templateUrl: 'autocomplete-widget-tpl.html',
    transclude: {
      input: 'inputArea',
      results: 'resultsArea'
    }
  });

transcludeプロパティに transclusion 元のタグ(derective名含む)と ng-transclude="xxx" の属性値で、どこに何を配置するかを決定してます。

コードを見てみる

ngTransclude.jsにあります。

var ngTranscludeMinErr = minErr('ngTransclude');
var ngTranscludeDirective = ngDirective({
  restrict: 'EAC',
  link: function($scope, $element, $attrs, controller, $transclude) {

    if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
      // If the attribute is of the form: `ng-transclude="ng-transclude"`
      // then treat it like the default
      $attrs.ngTransclude = '';
    }

    function ngTranscludeCloneAttachFn(clone) {
      if (clone.length) {
        $element.empty();
        $element.append(clone);
      }
    }

    if (!$transclude) {
      throw ngTranscludeMinErr('orphan',
       'Illegal use of ngTransclude directive in the template! ' +
       'No parent directive that requires a transclusion found. ' +
       'Element: {0}',
       startingTag($element));
    }

    // If there is no slot name defined or the slot name is not optional
    // then transclude the slot
    var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
    $transclude(ngTranscludeCloneAttachFn, null, slotName);
  }
});

最後で渡している slotName が、今回追加された設定です。($transclude は深くて追いきれなかったので断念...)

Fallback

API Docの最後の方にもサンプルが書かれていて、ここを見ると Fallback 機能が書かれているんですけど、なんかうまくいきませんでした。(ドキュメント自体が古い?)

おまけ

API Docですが、なぜかデフォルトが v1.5.0-build.x になっているのでお気をつけください。この状態でサンプルへのリンクを踏んでもエラーを出力している状態になってしまいます。
お試ししたい場合は、 v1.5.0-beta.x の API Doc になっているか確認してからみてください。
スクリーンショット 2015-12-08 0.02.50.png

まとめ

これは結構、活躍する場面が出てくると思います。便利!
ただ、親子関係のコンポーネントを作る場合は、module.component より module.directve で作ったほうが、後々幸せになれそう(細い設定ができるため)
まだ Fallback の挙動なのか仕様なのかがはっきりしていないのはβだからってことにしておこう(この投稿参考に
ならないかも?)

参考サイト

http://angularjs.blogspot.jp/2015/11/angularjs-15-beta2-and-14-releases.html
https://docs.angularjs.org/api/ng/directive/ngTransclude

3
2
0

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
3
2