I was wondering if there was any support for having element style transclusion for the multi-slot. What I mean by that is when you do a singular transclusion you can do "translcude: 'element'" instead of "transclude: true" to get different non-wrapping behavior. Is there any support for the element behavior with multi-slot?
Hmm, I'm not sure how this would work. transclude: element takes the whole element and makes it available to the template. How would that work with slots? Because you have different elements if you have different slots. And transcluding the whole slot doesn't make much sense to me, as it's only used to idenfify the slot.
@JordanFriendshuh Are you suggesting that they re-introduce their replace feature in transclusion? If so, I agree. I tried to wrap bootstrap's form-group with feedback into a component with multi-slot transclusion and it won't work because the ng-transclude wrapper breaks the expected HTML structure for the CSS.
Here's the code I'm working with:
feedbackInput.html:
<div class="form-group has-feedback" data-ng-class="{'has-success': (form.$submitted || form[field].$dirty) && form[field].$valid, 'has-error': (form.$submitted || form[field].$dirty) && form[field].$invalid}">
<ng-transclude ng-transclude-slot="label"></ng-transclude>
<ng-transclude ng-transclude-slot="input"></ng-transclude>
<span class="glyphicon glyphicon-remove form-control-feedback" data-ng-show="(form.$submitted || form[field].$dirty) && form[field].$invalid"></span>
<span class="glyphicon glyphicon-ok form-control-feedback" data-ng-show="(form.$submitted || form[field].$dirty) && form[field].$valid"></span>
<div ng-transclude data-ng-messages="form[field].$error" data-ng-show="form[field].$dirty || form.$submitted" role="alert"></div>
</div>
angular.module('Forms', ['ngMessages'])
.component('feedbackInput', {
templateUrl: '/app/templates/feedbackInput.html',
require: {
formCtrl: '^form'
},
transclude: {
'input': 'input',
'label': '?label'
},
//replace: true,
controller: ['$scope', '$element', '$timeout', function ($scope, $element, $timeout) {
$scope.field = '';
this.$onInit = function () {
$scope.form = this.formCtrl;
}
$timeout(function () {
$scope.field = $element.find('input').attr('name');
});
}]
});
And here's the component in action:
<feedback-input>
<label for="orderNumber" class="required"> Order #</label>
<input required type="number" class="form-control" id="orderNumber" name="orderNumber" data-ng-model="form.orderNumber" />
<div data-ng-message="required" class="text-danger slide-appear">This field is required.</div>
</feedback-input>
But is doesn't work because bootstrap expects the elements with the "form-control-feedback" class to be at the same level as the input. It can't be at that level because the span with the "ng-transclude" attribute remains. The "glyphicon-remove" images ends up appearing above the textbox. I would need AngularJS to remove the span and replace it with the input from the component.
Looks like this is what I'm trying to do as well... And I'm also trying to create directives for bootstrap components.
What I'm trying to do is, let the user provide optional transclude slot contents, and if the user provides content of a slot, then it replaces that part of the template. This is much required because otherwise it breaks the CSS.
For example, suppose I have the following template:
<div class="my-custom-container">
<label class="my-custom-label">Fallback</label>
<input class="my-custom-input"/>
</div>
for <my-custom-directive></<my-custom-directive>
directive,
If the user uses it like
<my-custom-directive>
<my-custom-label class="users-custom-class"><span>Custom content</span><my-custom-label>
</<my-custom-directive>
I want it to expand as:
<div class="my-custom-container">
<label class="my-custom-label users-custom-class"><span>Custom content</span></label>
<!-- <my-custom-label> merged with it's tranclude slot parent like a replace:true directive does. Good :) -->
<input class="my-custom-input"/>
</div>
After playing around with the available options I was only able to create something like
<div class="my-custom-container">
<label class="my-custom-label">
<label class="users-custom-class"><span>Custom content</span></label>
</label>
<input class="my-custom-input">
<input class="users-custom-class"/> <!-- everything goes inside the tranclude holder, bad :( -->
</input>
</div>
here's a stackoverflow question I asked regarding this
I'm not a fan of adding a replace functionality to the slot transclusion. It would introduced the same problems we have with normal "replace" and transclude: element. I also think that bootstrap needs to relax its styling rules.
But you can actually do this manually if your really need it:
http://plnkr.co/edit/vBEmbo65jB3Jug2L4Ynj?p=preview
@Narretz Yeah I figured I could do that and had posted that as an answer to my own question a while ago. It includes replace:true
as well so that it copies all the attributes.
I'm not sure what all are the problems you're referring to, still it'll be great to have these features built in. But if that is not at all possible and replace:true
option is going to be deprecated in future, it'll be nice to at least have the services that copes the attributes etc that the developer can use if the problems you were referring to doesn't affect his use case at his own risk.
Most problems would exactly come from merging attributes, especially angular expressions. This can get complex very fast. We currently don't special case ngClass / ngStyle / ngEvent directives, and they can break easily.
I think support for "element" style transclusion on multi-slot transclusion should be available.
Suppose the template for a component <list></list>
looks like this:
<ul ng-transclude="some-items"></ul>
<hr/>
<ul ng-transclude="other-items"></ul>
Currently, there is no way to pass <li></li>
elements directly to the <ul></ul>
. For example:
<list>
<some-items>
<li>item1</li>
<li>item2</li>
</some-items>
<other-items>
<li>item1</li>
<li>item2</li>
</other-items>
</list>
Would become:
<ul ng-transclude="some-items">
<some-items>
<li>item1</li>
<li>item2</li>
</some-items>
</ul>
<hr/>
<ul ng-transclude="other-items">
<other-items>
<li>item1</li>
<li>item2</li>
</other-items>
</ul>
But the desired outcome is:
<ul ng-transclude="some-items">
<li>item1</li>
<li>item2</li>
</ul>
<hr/>
<ul ng-transclude="other-items">
<li>item1</li>
<li>item2</li>
</ul>
One way to solve this is to allow transclusion based on attributes. For example:
<list>
<ul>
<li some-items>item1</li>
<li some-items>item2</li>
</ul>
<ul>
<li other-items>item1</li>
<li other-items>item2</li>
</ul>
</list>
Alternatively, an attribute to indicate that only the children should be transcluded (transclude-contents
)
<list>
<some-items transclude-contents>
<li>item1</li>
<li>item2</li>
</some-items>
<other-items transclude-contents>
<li>item1</li>
<li>item2</li>
</other-items>
</list>
This would allow easier interaction with bootstrap as well. Suppose there is a need to transclude dropdown elements in a <main-navbar></main-navbar>
component:
Template:
<nav class="navbar navbar-fixed-top">
<div class="container-fluid">
<header class="navbar-header pull-left">
<div uib-dropdown="" is-open="$ctrl.isOpen" class="pull-left">
<button type="button" class="btn btn-default navbar-btn" uib-dropdown-toggle>
<span class="sr-only">Toggle dropdown menu</span>
<i class="fa fa-lg fa-fw fa-bars"></i>
</button>
<ul class="uib-dropdown-menu" role="menu" ng-transclude="dropdown"></ul>
</div>
</header>
</div>
</nav>
Using it:
<main-navbar>
<dropdown transclude-contents>
<li>item1</li>
<li>item2</li>
</dropdown>
</main-navbar>
Most helpful comment
I think support for "element" style transclusion on multi-slot transclusion should be available.
Suppose the template for a component
<list></list>
looks like this:Currently, there is no way to pass
<li></li>
elements directly to the<ul></ul>
. For example:Would become:
But the desired outcome is:
One way to solve this is to allow transclusion based on attributes. For example:
Alternatively, an attribute to indicate that only the children should be transcluded (
transclude-contents
)This would allow easier interaction with bootstrap as well. Suppose there is a need to transclude dropdown elements in a
<main-navbar></main-navbar>
component:Template:
Using it: