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-widget
の template
に 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-widget
の template
の実装です。
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 になっているか確認してからみてください。
まとめ
これは結構、活躍する場面が出てくると思います。便利!
ただ、親子関係のコンポーネントを作る場合は、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