Directive AngularJs avec contrôleur dynamique et modèle
Je veux créer une directive qui a une vue dynamique avec un contrôleur dynamique. le contrôleur et la vue modèle proviennent du serveur.
La directive
var DirectivesModule = angular.module('BPM.Directives', []);
(function () {
'use strict';
angular
.module('BPM.Directives')
.directive('bpmCompletedTask', bpmCompletedTask);
bpmCompletedTask.$inject = ['$window'];
function bpmCompletedTask ($window) {
// Usage:
// <bpmCompletedTask></bpmCompletedTask>
// Creates:
//
var directive = {
link: link,
restrict: 'E',
scope: {
type: '=',
taskdata: '=',
controllername:'@'
},
template: '<div ng-include="getContentUrl()"></div>',
controller: '@',
name: 'controllername'
};
return directive;
function link(scope, element, attrs) {
scope.getContentUrl = function () {
return '/app/views/TasksViews/' + scope.type + '.html';
}
scope.getControllerName = function ()
{
console.warn("Controller Name is " + scope.type);
return scope.type;
}
}
}
})();
Voici comment j'essaye d'utiliser la directive
<div ng-controller="WorkflowHistoryController as vm">
<h2>Workflow History</h2>
<h3>{{Id}}</h3>
<div ng-repeat="workflowStep in CompletedWorkflowSteps">
<bpm-completed-task controllername="workflowStep.WorkflowTaskType.DataMessageViewViewName" taskdata="workflowStep.WorkflowTaskOutcome.TaskOutcome" type="workflowStep.WorkflowTaskType.DataMessageViewViewName">
</bpm-completed-task>
</div>
</div>
Le problème maintenant est que lorsque la directive obtient le nom du contrôleur, elle l'obtient en tant que chaîne littérale et non en tant que paramètre.
Est-ce faisable? si ce n'est pas faisable, quelle est la meilleure solution pour créer des vues dynamiques avec ses contrôleurs et les afficher dynamiquement dans ng-repeat?
Merci,
Mise à jour du 20 janvier Je viens de mettre à jour mon code au cas où quelqu'un serait intéressé. Tout le crédit va à @Meligy.
La première directive:
(function () {
'use strict';
angular
.module('BPM.Directives')
.directive('bpmCompletedTask', bpmCompletedTask);
bpmCompletedTask.$inject = ['$compile', '$parse'];
function bpmCompletedTask ($compile, $parse) {
var directive = {
link: function (scope, elem, attrs) {
console.warn('in the first directive - before if');
if (!elem.attr('bpm-completed-task-inner'))
{
console.warn('in the first directive');
var name = $parse(elem.attr('controllername'))(scope);
console.warn('Controller Name : ' + name);
elem = elem.removeAttr('bpm-completed-task');
elem.attr('controllernameinner', name);
elem.attr('bpm-completed-task-inner', '');
$compile(elem)(scope);
}
},
restrict: 'A',
};
return directive;
}
})();
La deuxième directive
angular
.module('BPM.Directives')
.directive('bpmCompletedTaskInner',['$compile', '$parse',
function ($window, $compile, $parse) {
console.warn('in the second directive');
return {
link: function (scope, elem, attrs) {
console.warn('in the second directive');
scope.getContentUrl = function () {
return '/app/views/TasksViews/' + scope.type + '.html';
}
},
restrict: 'A',
scope: {
type: '=',
taskdata: '=',
controllernameinner: '@'
},
template: '<div ng-include="getContentUrl()"></div>',
controller: '@',
name: 'controllernameinner'
};
}]);
Le Html
<div ng-repeat="workflowStep in CompletedWorkflowSteps">
<div bpm-completed-task controllername="workflowStep.WorkflowTaskType.DataMessageViewViewName" taskdata="workflowStep.WorkflowTaskOutcome.TaskOutcome"
type="workflowStep.WorkflowTaskType.DataMessageViewViewName">
</div>
</div>
Mise à jour:
Je l'ai fait fonctionner, mais c'est vraiment moche. Vérifier:
Votre exemple a beaucoup de pièces mobiles, donc celle-ci est simple, mais fait ce que vous voulez.
Fondamentalement, vous avez besoin d'une directive wrapper qui prend l'objet JS et se convertit en une propriété de chaîne, vous pouvez alors utiliser votre directive pour tout le reste (modèle, portée, etc.).
.
Mise à jour 2:
Code en ligne:
var app = angular.module('myApp', []).
directive('communicatorInner', ["$parse", "$compile",
function($parse, $compile) {
return {
restrict: 'A',
template: "<input type='text' ng-model='message'/><input type='button' value='Send Message' ng-click='sendMsg()'><br/>",
scope: {
message: '='
},
controller: '@'
};
}
]).
directive('communicator', ['$compile', '$parse',
function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem) {
if (!elem.attr('communicator-inner')) {
var name = $parse(elem.attr('controller-name'))(scope);
elem = elem.removeAttr('controller-name')
elem.attr('communicator-inner', name);
$compile(elem)(scope);
}
}
};
}
]).
controller("PhoneCtrl", function($scope) {
$scope.sendMsg = function() {
alert($scope.message + " : sending message via Phone Ctrl");
}
}).
controller("LandlineCtrl", function($scope) {
$scope.sendMsg = function() {
alert($scope.message + " : sending message via Land Line Ctrl ");
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
<div ng-app="myApp">
<div ng-init="test = {p: 'PhoneCtrl', l: 'LandlineCtrl' }">
<communicator controller-name="test.p" message="'test1'"></communicator>
<communicator controller-name="test.l"></communicator>
</div>
</div>
.
Original (non pertinent maintenant mais peut aider d'autres problèmes connexes)
Oui, ça devrait marcher.
Un test avec Angular 1.3:
Choses à vérifier:
Le contrôleur est-il défini et ajouté au module? Ça ne marchera pas
Si le contrôleur n'est qu'une fonction globale, cela ne fonctionnera pas. Il doit être ajouté via l'
<myModule>.controller("<controllerName>", <functiion>)
APIÇa
ng-controller
marche? Il suffit de l'ajouter au modèleDe même, l'utilisation de ng-controller directement en dehors de la directive fonctionne-t-elle?