I want to create a dashboard comprising of several smaller "apps". I have the following markup (simplified):
<div id="rowcontainer">
<div ng-repeat="appRow in dashboard.appRows">
<div class="row">
<div ng-repeat="app in appRow.apps">
{{app}}
</div>
</div>
</div>
</div>
dashboard.appRows
keeps my rows which consist of my "apps" (appRow.apps
).
When I click a button I want to dynamically add a directive by replacing the {{app}}
placeholder with a directive-tag and then compiling ($compile
) this very tag I just inserted.
The kicker is, in the click handler, when I updated my model, the DOM is not updated (obviously) in the function and $()
will fail to find the DOM node.
For clarification:
I want to make this:
<div ng-repeat="app in appRow.apps">
{{app}}
</div>
become this:
<div ng-repeat="app in appRow.apps">
<my-directive></my-directive>
</div>
and finally $compile
the above to this:
<div ng-repeat="app in appRow.apps">
<my-directive>CONTENTS OF MY TEMPLATE</my-directive>
</div>
Attempting this all in the same function does not work, calling $scope.$apply()
throws an error ($digest
already running) and I don't know if there is a callback. And finding the DOM element does not work. Thank you for your help or alternative suggestions.
Further explanation:
The directive <my-directive>
could be any directive (<my-directive>
, <your-directive>
, ...). In my template there is a a place-holder {{app}}
which I will replace at runtime with any of those directives mentioned (assume you click a button and the directive is replacing {{app}}
). And then I want my directive to be working.
A way I can think of is using a container directive to hold the compiled directive.
angular.module('test', [])
.controller('Test', TestController)
.directive('appLoader', appLoaderDirective)
.directive('apple', appleDirective)
.directive('banana', bananaDirective);
function TestController($scope) {
$scope.apps = ['apple', 'banana']
}
function appLoaderDirective($compile) {
return {
scope: { app: '=' },
link: function(scope, element, attr) {
// you might have to do camel case to kebab case conversion here, depending on your input
element.append($compile("<" + scope.app + "></" + scope.app + '>')(scope));
}
}
}
function appleDirective() {
return {
template: '<div>i am apple!</div>'
}
}
function bananaDirective() {
return {
template: '<div>i am banana!</div>'
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app='test' ng-controller='Test'>
<div ng-repeat='app in apps'>
<app-loader app='app'></app-loader>
</div>
</div>