Angular.js directive dynamic templateURL

169

Tengo una etiqueta personalizada en una routeProviderplantilla que requiere una directiveplantilla. El versionatributo será poblado por el ámbito que luego requiere la plantilla correcta.

<hymn ver="before-{{ week }}-{{ day }}"></hymn>

Existen múltiples versiones del himno en función de qué semana y día es. Estaba anticipando usar la directiva para llenar la .htmlporción correcta . La variable no está siendo leída por el templateUrl.

emanuel.directive('hymn', function() {
    var contentUrl;
    return {
        restrict: 'E',
        link: function(scope, element, attrs) {
            // concatenating the directory to the ver attr to select the correct excerpt for the day
            contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
        },
        // passing in contentUrl variable
        templateUrl: contentUrl
    }
});

Hay varios archivos en el directorio extractos que están etiquetados before-1-monday.html, before-2-tuesday.html, ...

Alen Giliana
fuente
1
posible duplicado de Dynamic templateUrl - AngularJS
Nick Grealy
si está utilizando AngularJS 1.5+, consulte esta elegante solución: stackoverflow.com/a/41743424/1274852
hkong

Respuestas:

184

Puedes usar la ng-includedirectiva.

Intenta algo como esto:

emanuel.directive('hymn', function() {
   return {
       restrict: 'E',
       link: function(scope, element, attrs) {
           scope.getContentUrl = function() {
                return 'content/excerpts/hymn-' + attrs.ver + '.html';
           }
       },
       template: '<div ng-include="getContentUrl()"></div>'
   }
});

UPD para ver el veratributo

emanuel.directive('hymn', function() {
   return {
       restrict: 'E',
       link: function(scope, element, attrs) {
           scope.contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
           attrs.$observe("ver",function(v){
               scope.contentUrl = 'content/excerpts/hymn-' + v + '.html';
           });
       },
       template: '<div ng-include="contentUrl"></div>'
   }
});
pgregory
fuente
1
Su gran solución. ¿Hay alguna manera de escribirlo que pueda manejar múltiples instancias? Actualmente, una vez que se establece el alcance, no reconoce nuevos atributos.
Alen Giliana
1
¿Quieres decir que quieres ver los vercambios de atributos y volver a entregar la directiva?
pgregory
1
Gracias por la aclaración. Si declara la directiva de la manera publicada en upd., Su caso de uso cuando usa múltiples <hymn ...>debería funcionar bien. ¿O tal vez es hora de construir un prototipo en jsfilddle ?
pgregory
1
Hola @AlenGiliana, ve take a look at your site, and changed [JSFiddle](http://jsfiddle.net/JQgG5/6/). All you need is alcance: {} `en la declaración de directiva - aislamiento del alcance . También te recomiendo que uses la última versión de angular. <script type="text/ng-template" id="...">- es una alternativa local a las páginas html
pgregory
1
¿Quieres decir usar Angular 1.2.1? Gracias por la ayuda, esta curva de aprendizaje es una locura :)
Alen Giliana
313
emanuel.directive('hymn', function() {
   return {
       restrict: 'E',
       link: function(scope, element, attrs) {
           // some ode
       },
       templateUrl: function(elem,attrs) {
           return attrs.templateUrl || 'some/path/default.html'
       }
   }
});

Para que pueda proporcionar templateUrl a través de marcado

<hymn template-url="contentUrl"><hymn>

Ahora solo tenga cuidado de que la propiedad contentUrl se complete con la ruta generada dinámicamente.

Andrej Kaurin
fuente
44
Bien, pero ... ¿puedo acceder a los atributos de alcance desde la función templateUrl? TemplateUrl depende de un valor de alcance, pero no puedo acceder a él :(
josec89
1
Me alegra que hayas encontrado la solución. NO recomendaría que la directiva tenga dependencia de su padre a menos que el controlador esté configurado en requerir parte de la directiva.
Andrej Kaurin
11
¡Finalmente! ¡Exactamente lo que estaba buscando! No me di cuenta de que tenía acceso a elem y attrs desde una función templateUrl. ¡GRACIAS!
coryvb123
77
templateUrl se llama una vez por directiva, no se invoca en la inicialización de cada instancia de directiva, ¡tenga cuidado! Aunque puede ser un error en angular ...
Lu4
2
Todavía no lo he verificado, pero según mis últimos hallazgos, probablemente valga la pena mencionar que es así once per $compile phase. En otras palabras, si lo usa ng-repeatcon su directiva y desea establecer una plantilla individual basada en ng-repeatel contexto específico del elemento, no funcionará, porque la $compilefase recorre su directiva una vez antes de que ng-repeatsuceda lo real . Entonces, en ese sentido, se llama una vez ...
Lu4
6

Gracias a @pgregory, pude resolver mi problema usando esta directiva para la edición en línea

.directive("superEdit", function($compile){
    return{
        link: function(scope, element, attrs){
            var colName = attrs["superEdit"];
            alert(colName);

            scope.getContentUrl = function() {
                if (colName == 'Something') {
                    return 'app/correction/templates/lov-edit.html';
                }else {
                    return 'app/correction/templates/simple-edit.html';
                }
            }

            var template = '<div ng-include="getContentUrl()"></div>';

            var linkFn = $compile(template);
            var content = linkFn(scope);
            element.append(content);
        }
    }
})
Shilan
fuente
5

No necesita una directiva personalizada aquí. Solo use el atributo ng-include src. Está compilado para que pueda poner código dentro. Vea plunker con solución para su problema.

<div ng-repeat="week in [1,2]">
  <div ng-repeat="day in ['monday', 'tuesday']">
    <ng-include src="'content/before-'+ week + '-' + day + '.html'"></ng-include>
  </div>
</div>
icem
fuente
2

Tuve el mismo problema y lo resolví de una manera ligeramente diferente a los demás. Estoy usando angular 1.4.4.

En mi caso, tengo una plantilla de shell que crea un panel CSS Bootstrap:

<div class="class-container panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">{{title}} </h3>
    </div>
    <div class="panel-body">
        <sp-panel-body panelbodytpl="{{panelbodytpl}}"></sp-panel-body>
    </div>
</div>

Quiero incluir plantillas de cuerpo de panel según la ruta.

    angular.module('MyApp')
    .directive('spPanelBody', ['$compile', function($compile){
        return {
            restrict        : 'E',
            scope : true,
            link: function (scope, element, attrs) {
                scope.data = angular.fromJson(scope.data);
                element.append($compile('<ng-include src="\'' + scope.panelbodytpl + '\'"></ng-include>')(scope));
            }
        }
    }]);

Luego tengo la siguiente plantilla incluida cuando la ruta es #/students:

<div class="students-wrapper">
    <div ng-controller="StudentsIndexController as studentCtrl" class="row">
        <div ng-repeat="student in studentCtrl.students" class="col-sm-6 col-md-4 col-lg-3">
            <sp-panel 
            title="{{student.firstName}} {{student.middleName}} {{student.lastName}}"
            panelbodytpl="{{'/student/panel-body.html'}}"
            data="{{student}}"
            ></sp-panel>
        </div>
    </div>
</div>

La plantilla panel-body.html de la siguiente manera:

Date of Birth: {{data.dob * 1000 | date : 'dd MMM yyyy'}}

Datos de muestra en el caso de que alguien quiera probar:

var student = {
    'id'            : 1,
    'firstName'     : 'John',
    'middleName'    : '',
    'lastName'      : 'Smith',
    'dob'           : 1130799600,
    'current-class' : 5
}
igasparetto
fuente
0

Tengo un ejemplo sobre esto.

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  </head>

  <body>
    <div class="container-fluid body-content" ng-controller="formView">
        <div class="row">
            <div class="col-md-12">
                <h4>Register Form</h4>
                <form class="form-horizontal" ng-submit="" name="f" novalidate>
                    <div ng-repeat="item in elements" class="form-group">
                        <label>{{item.Label}}</label>
                        <element type="{{item.Type}}" model="item"></element>
                    </div>
                    <input ng-show="f.$valid" type="submit" id="submit" value="Submit" class="" />
                </form>
            </div>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
    <script src="app.js"></script>
  </body>

</html>

angular.module('app', [])
    .controller('formView', function ($scope) {
        $scope.elements = [{
            "Id":1,
            "Type":"textbox",
            "FormId":24,
            "Label":"Name",
            "PlaceHolder":"Place Holder Text",
            "Max":20,
            "Required":false,
            "Options":null,
            "SelectedOption":null
          },
          {
            "Id":2,
            "Type":"textarea",
            "FormId":24,
            "Label":"AD2",
            "PlaceHolder":"Place Holder Text",
            "Max":20,
            "Required":true,
            "Options":null,
            "SelectedOption":null
        }];
    })
    .directive('element', function () {
        return {
            restrict: 'E',
            link: function (scope, element, attrs) {
                scope.contentUrl = attrs.type + '.html';
                attrs.$observe("ver", function (v) {
                    scope.contentUrl = v + '.html';
                });
            },
            template: '<div ng-include="contentUrl"></div>'
        }
    })
ddagsan
fuente