How to $compile angular template to make it work i

2019-08-14 08:52发布

问题:

I have a custom directive that simply $compiles a template into another.

.directive('staticInclude', function($http, $templateCache, $compile) {
        return function(scope, element, attrs) {
            var templatePath = attrs.staticInclude;
            //
            $http.get(templatePath, {
                cache: $templateCache
            }).success(function(response) {
                var contents = element.html(response).contents();
                $compile(contents)(scope);
            });
        };
    });

I use it like:

<div static-include="components/campaign/details.html"></div>

Because I'm using aliases for the controller (using angular UI router), all model in any of the templates are like:

<p>Delivery Time: <span class="text-medium">{{CtrlAlias.campaign.newsletter.sentDate | date:CtrlAlias.currentUser.params.settings}}</span></p>

How do I make this directive work in multiple templates where CtrlAlias changes?

I tried changing $compile(contents)(scope); into $compile(contents)(scope.newCtrlAlias);

Any ideas?

回答1:

When you $compile and then link, you are free to provide your own scope against which the compiled content is linked. That means that you can have the template content refer to some arbitrary ViewModel name, say vm:

<p>Delivery Time: <span>{{vm.campaign.newsletter.sentDate}}</span></p>

And link against a scope that has vm property:

var scope = { vm: {...} }

It actually might be even useful to use an isolate scope for your compiled content, to make sure that you aren't assuming an existence of scope variables that may or may not be there when the content is linked:

.directive('staticInclude', function($templateRequest, $compile) {
  return {
    link: function(scope, element, attrs){
       var alias = attrs.alias || 'vm';
       var templatePath = attrs.staticInclude;

       var newScope = scope.$new(true); // isolate scope
       newScope.vm = scope[alias];

       // $templateRequest is essentially $http with $templateCache
       $templateRequest(templatePath)
           .then(function(html){
              $compile(html)(newScope, function cloneAttachFn(clone){
                 element.empty();
                 element.append(clone);
              });
           });
    }
  };
});

Then usage is like so:

<div ng-controller="MainCtrl as main">
    <div static-include="components/campaign/details.html" alias="main">
    </div>
</div>


回答2:

Really not sure I understand why you would need to use this so it's not easy to answer. However, one possible solution could be to wrap the template in a <div> to which you can append the desired controller information. It's a bit gross but it might work for you. You would have to pass in the controller name and it's alias but you could perhaps add that to your $state's data properties and access them from that but again it all seems a bit hacky.

DEMO

app.directive('staticInclude', function($http, $templateCache, $compile) {

  return {

    scope: {
      ctrlName    : '@',
      alias       : '@'
    },

    link: link

  };


  function link(scope, element, attrs) {

      var templatePath = attrs.staticInclude;

      $http
        .get(templatePath, {
            cache: $templateCache
        })
        .success(function(response) {

          var ctrlStr   = scope.ctrlName + ' as ' + scope.alias,
              template  = '<div ng-controller="' + ctrlStr + '" >' + response + '</div>',
              contents  = element.html(template).contents();

            $compile(contents)(scope);

        });

  };

});