43

I would like to use the same HTML template in 3 places, just each time with a different model. I know I can access the variables from the template, but there names will be different.

Is there a way to pass a model to the ngInclude?

This is what I would like to achieve, of course the attribute add-variable does not work now. Then in my included template, I would acces the detailsObject and its properties.

<pane title="{{projectSummary.ProjectResults.DisplayName}}">
    <h2>{{projectSummary.ProjectResults.DisplayName}}</h2>
    <ng-include src="'Partials/SummaryDetails.html'" init-variable="{'detailsObject': projectSummary.ProjectResults}"></ng-include>
</pane>

<pane  title="Documents" header="true"></pane>

<pane ng-repeat="document in projectSummary.DocumentResults" title="{{document.DisplayName}}">
    <h2>{{document.DisplayName}}</h2>
    <ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': document}"></ng-include>
</pane>

<pane ng-repeat="header in [1]" title="Languages" header="true"></pane>

<pane ng-repeat="language in projectSummary.ResultsByLanguagePairs" title="{{language.DisplayName}}">
    <h2>{{document.DisplayName}}</h2>
    <ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': language}"></ng-include>
</pane>

If I took a bad approach with using ng-include, is there something else I should try?

2
  • It seems like you need a directive ? Commented Nov 16, 2012 at 19:19
  • A solution is create a new directive, as i said in this answer: stackoverflow.com/a/36916276/2516399 Commented Apr 28, 2016 at 13:34

6 Answers 6

60

There is a rather simple solution, although I must admit, it's not what Misko would recommend. But if creating a directive is an overkill for you and getting Brice's patch is not feasible then the following will help you.

<div ng-repeat="name in ['A']" ng-include="'partial.html'"></div>
<div ng-repeat="name in ['B']" ng-include="'partial.html'"></div>

<script type="text/ng-template" id="partial.html">
   <div>{{ name }}</div>
</script>

It's quite evident why it works. See an example here: http://jsfiddle.net/Cndc6/4/

Sign up to request clarification or add additional context in comments.

5 Comments

That is really quite brilliant and requires no modifications to angular or new directives.
Image
This worked very well for me with complex data structures as well.
This is the best hack by far until Angular adds support passing params to partials.
Truly brilliant, and not that much of a hack either.
Thank you for this! If my mind had gone down this route, I probably would've first tried to use 'ngInit', but this automatically makes a "transcluded" scope which 'ngInit' does not do.
21

NOTE: this is not my original answer but this is how I'd do this after using angular for a bit.

I would create a directive with the html template as the markup passing in the dynamic data to the directive as seen in this fiddle.

Steps/notes for this example:

  1. Define a directive with markup in the templateUrl and attribute(s) used to pass data into the directive (named type in this example).
  2. Use the directive data in the template (named type in this example).
  3. When using the directive in the markup make sure you pass in the data from the controller scope to the directive (<address-form type="billing"></address-form> (where billing is accessing an object on the controller scope).
  4. Note that when defining a directive the name is camel cased but when used in the markup it is lower case dash delimited (ie it's named addressForm in the js but address-form in the html). More info on this can be found in the angular docs here.

Here is the js:

var myApp = angular.module('myApp',[]);

angular.module('myApp').directive('addressForm', function() {
    return {
        restrict: 'E',
        templateUrl: 'partials/addressform.html', // markup for template
        scope: {
            type: '=' // allows data to be passed into directive from controller scope
        }
    };
});

angular.module('myApp').controller('MyCtrl', function($scope) {
    // sample objects in the controller scope that gets passed to the directive
    $scope.billing = { type: 'billing type', value: 'abc' };
    $scope.delivery = { type: 'delivery type', value: 'def' };
});

With markup:

<div ng-controller="MyCtrl">
    <address-form type="billing"></address-form>
    <address-form type="delivery"></address-form>
</div>

ORIGINAL ANSWER (which is completely different than using a directive BTW).

Note: The fiddle from my original answer below doesn't appear to work anymore due to an error (but keeping it here in case it is still useful)

There was a discussion about this on the Google Group you can see it here.

It looks like this functionality is not supported out of the box but you can use Brice's patch as described in this post.

Here is the sample code from his jsfiddle:

<script id="partials/addressform.html" type="text/ng-template">
    partial of type {{type}}<br>
</script>

<div ng-controller="MyCtrl">
  <ng-include src="'partials/addressform.html'" onInclude="type='billing'"></ng-include>
  <ng-include src="'partials/addressform.html'" onLoad="type='delivery'"></ng-include>
</div>

9 Comments

Thank you. I managed to achieve it without a new directive, with a ng-init. In this ng-init, I create the objectDetails variable and init it to what I want.
Nowadays you can use your own directive and in it use the templateUrl property.
using type='delivery' just gives me the word delivery, not the delivery value from the controller. -1
@Imray I've changed the answer to use a directive instead which should fix the issue you were having with just passing a word instead of the value from the controller. Thanks for letting me know the existing answer was not quite working anymore.
@Gloopy I need to add some other data along with billing also. Let say some $scope.name="Ankur" and I cannot afford to do type="name" as it will generate the view again. What to do in this regard?
|
13

There is a pull to fix this but it looks like it's dead: https://github.com/angular/angular.js/pull/1227

Without modifying the Angular source code this will solve the problem in a reusable not-too-hacky-feeling way:

directive('newScope', function() {
    return {
        scope: true,
        priority: 450,
    };
});

And an example:

<div new-scope ng-init="myVar = 'one instance'" ng-include="'template.html'"></div>
<div new-scope ng-init="myVar = 'another instance'" ng-include="'template.html'"></div>

Here is a Plunker of it in action: http://plnkr.co/edit/El8bIm8ta97MNRglfl3n

1 Comment

Works great, thank you. For me it looks nicer if I get rid of ng-init and just have <div new-scope="myVar = 'one instance'" ng-include="'template.html'"></div>. (I just copied two lines of code from ng-init to your directive to get this to work)
6
<div new-scope="myVar = 'one instance'" ng-include="'template.html'"></div>

directive('newScope', function () {
    return {
        scope: true,
        priority: 450,
        compile: function () {
            return {
                pre: function (scope, element, attrs) {
                    scope.$eval(attrs.newScope);
                }
            };
        }
    };
});

This is a directive that combines new-scope from John Culviner's answer with code from Angular's ng-init.

For completeness, this is the Angular 1.2 26 ng-init source, you can see the only change in the new-scope directive is the addition of scope: true

{
  priority: 450,
  compile: function() {
    return {
      pre: function(scope, element, attrs) {
        scope.$eval(attrs.ngInit);
      }
    };
  }
}

Comments

5

Quick'n'dirty solution:

<div ng-init="details=document||language||projectSummary.ProjectResults">

5 Comments

Not a bad workaround so your div is outside each ng-include and you are setting each one explicitly to either documents, language, or projectSummary.ProjectResults?
Actually i put this inside the html template, because I am lazy. But writing it before each ng-Include div would be cleaner.
Would this init the value in correct scope? I.e. does it set the value in the scope of the include?
This line is actually inside the inner html template, so yes, the scope is correct.
This is what i was literally looking for i have no idea why i didn't know this already lol. Though this is the best answer on this question so far.
1

I hear you! ng-include is not that reusable because it has access to the global scope. It's a little weird.

There should be a way to set local variables. Using a new directive instead of ng-include is a cleaner solution.

The ideal usage looks like:

<div ng-include-template="'Partials/SummaryDetails.html'" ng-include-variables="{ 'detailsObject': language }"></div>

The directive is:

.directive(
  'ngIncludeTemplate'
  () ->
    {
      templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
      restrict: 'A'
      scope: {
        'ngIncludeVariables': '&'
      }
      link: (scope, elem, attrs) ->
        vars = scope.ngIncludeVariables()
        for key, value of vars
          scope[key] = value
    }
)

You can see that the directive doesn't use the global scope. Instead, it reads the object from ng-include-variables and add those members to its own local scope.

It's clean and generic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.