Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make ng-transclude behave like ng-include (in terms of scope)?

I'd like to transclude content as such that it acts as if I copy-pasted the content into the file where I write my <div data-ng-transclude="">. How do I do this?

I know I can use ng-include to include a template, and I can use script tags to define a template. But this clutters the template cache and pollutes the template namespace.

I want to do this so that I can have one (or more! That's the whole point) file in which I define the way I want to display my items,

<!-- list directive to show the items -->
<div data-item-list="" data-values="vm.items">

    <!-- content to include to display list items -->
    <div class="form-relation-picker-value" ng-bind="item.value.title"></div>
    <div class="form-relation-picker-subtitle" ng-bind="item.value.subTitle"></div>
</div>

and one file in which I define the way the list's structure works.

<div class="list-container">
    <div class="list-item"
         data-ng-click="vm.select(item)"
         data-ng-repeat="item in vm.items | orderBy : vm.orderBy"
         data-selected="{{vm.isSelected(item)}}">

        <div class="flex">
            <div ng-transclude=""></div><!-- item display via transclude -->
            <div class="grid-col flex-icon form-relation-picker-chrome-height-fix">
                <div data-ng-show="vm.isSelected(item)" class="icon check"></div>
            </div>
        </div>
    </div>
</div>

This works if I do something like this:

<div data-item-list="" data-values="vm.items" data-template-to-use="randomhash">
    <script type="text/ng-template" id="randomhash">
        <div class="form-relation-picker-value" ng-bind="item.value.title"></div>
        <div class="form-relation-picker-subtitle" ng-bind="item.value.subTitle"></div>
    </script>
</div>

with a list structure like so...

<div class="list-container">
    <div class="list-item"
         data-ng-click="vm.select(item)"
         data-ng-repeat="item in vm.items | orderBy : vm.orderBy"
         data-selected="{{vm.isSelected(item)}}">

        <div class="flex">
            <div data-ng-include="vm.templateToUse"></div>
            <div class="grid-col flex-icon form-relation-picker-chrome-height-fix">
                <div data-ng-show="vm.isSelected(item)" class="icon check"></div>
            </div>
        </div>
    </div>
</div>

But, like I said, it clutters the template cache.

If I transclude the content, then it stops working, as the transcluded content is evaluated with the scope of the directive that contains the <div data-item-list="". That is, "item" does not exist.

How can I make it so that the transcluded content is evaluated with the scope of the directive that is including the transcluded content?

like image 267
Pimgd Avatar asked Jun 02 '16 15:06

Pimgd


People also ask

Does Ng include create a new scope?

ngInclude creates a new child scope which inherits scope values from the parent controller.

How does ng include work?

The ng-include directive includes HTML from an external file. The included content will be included as childnodes of the specified element. The value of the ng-include attribute can also be an expression, returning a filename. By default, the included file must be located on the same domain as the document.

What is angular Transclusion?

Transclusion is a concept that let's you create a content-slot / ng-content inside your component. So if you have a child component with a content-slot / ng-content, the parent component that uses the child component can put whatever it wants inside the content-slot of the child component.


1 Answers

What you need, is a template function that will extract the content of your template element (the element as it appears in your HTML, before Angular compiles it) and embed it into the directive's template.

As described in the docs, the template property of the DDO can be a function, in which case it receives the template element as an argument (among other things) and is expected to return the directive's template as a string.

You can use something like this:

.directive('itemList', function itemListDirective() {
  // DDO
  return {
    ...
    template: function itemListTmplFn(tElem) {
      var customContent = tElem.html();

      return '...' + customContent + '...';
    }
  };
})

Here is a simple demo that uses the .component() API (introduced in v1.5). There are some minor differences if you want to use a plain old .directive(), but you should be able to adapt it easily.

like image 120
gkalpak Avatar answered Nov 02 '22 21:11

gkalpak