I'm trying really hard, but I can't find where is my problem.
I created a custom directive and it should iterate some HTML to draw the right content on the screen.
The problem is that my ng-repeat does not iterate my array. I search on stackoverflow but what I found didn't help me.
Here is my directive (it's in an external file):
app.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'view/templates/log-tab.html',
replace: true,
controller: ['$scope', 'api', function($scope, api) {
$scope.logState = false;
$scope.logData = [1,2,3,4];
$scope.loadLog = function() {
api.doRequest({
path : $scope.path,
method : "GET",
broadcast : BK_LOG
});
};
var bk = $scope.$on(BK_LOG, function(key, value){
$scope.logState = true;
console.log($scope.logData);
bk();
});
}]
};
});
And here is the directive HTML that will be rendered:
<md-tab ng-click="loadLog()" label="{{i18n['REVISIONS']}}">
<div layout="row" layout-align="center" layout-padding ng-show="!logState">
<div layout="column">
<div>
<md-progress-circular md-mode="indeterminate" md-diameter="130"></md-progress-circular>
</div>
<div>
{{i18n['LOG_LOAD']}}
</div>
</div>
</div>
<div layout="row" ng-show="logState" layout-padding>
<div layout="column">
<div ng-repeat="xyz in logData">
{{xyz}}
SOME CONTENT HERE
</div>
</div>
</div>
</md-tab>
Thanks in advance!
I removed other components from your directive code and kept it at bare minimum. The code is working fine as expected. Please see below.
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body ng-app="myApp" >
<logtab></logtab>
<script src="angular.js" type="text/javascript "></script>
<script src="app.js" type="text/javascript "></script>
</body>
</html>
app.js
var myApp = angular.module('myApp', []);
myApp.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'log-tab.html',
replace: true,
controller: ['$scope', function($scope) {
$scope.logState = false;
$scope.logData = [1,2,3,4];
console.log('hi');
}]
};
});
log-tab.html
<div>
{{logData}}
<div layout="row">
<div layout="column">
<div ng-repeat="xyz in logData">
{{xyz}}
</div>
</div>
</div>
</div>
Please use a couple of console.log's to identify the exact location. I suspect that there must be some thing wrong with the other parts of the directive or may be logState is set to false??
You can always separate the controller from the directive:
app.directive('logtab', function(){
return {
restrict: 'E',
templateUrl: 'view/templates/log-tab.html',
replace: true,
scope: {
log: '='
}
};
});
Then in your controller
app.controller('MyController', ['$scope', 'api', function($scope, api) {
$scope.logTab = {
state: false,
data: [1, 2, 3, 4],
loadLog: function() {
api.doRequest({
path : $scope.path,
method : "GET",
broadcast : BK_LOG
});
}
};
var bk = $scope.$on(BK_LOG, function(key, value){
$scope.logTab.state = true;
console.log($scope.logTab.data);
bk();
});
}]);
Then change your HTML to:
<md-tab ng-click="log.loadLog()" label="{{i18n['REVISIONS']}}">
<div layout="row" layout-align="center" layout-padding ng-show="!log.state">
<div layout="column">
<div>
<md-progress-circular md-mode="indeterminate" md-diameter="130"></md-progress-circular>
</div>
<div>
{{i18n['LOG_LOAD']}}
</div>
</div>
</div>
<div layout="row" ng-show="log.state" layout-padding>
<div layout="column">
<div ng-repeat="xyz in log.data">
{{xyz}}
SOME CONTENT HERE
</div>
</div>
</div>
</md-tab>
And call it with
<logtab log="logTab"></logtab>
Updated my answer based on OP's explanation.
Created a fiddle here: https://jsfiddle.net/frishi/bzbbo5da/14/
I simplified the directive definition a little, removed the api part of it.
I also changed the directive's template to include the ng-repeat part of it.
//....
restrict: 'E',
template: `<p><div ng-repeat="xyz in logData">
{{xyz}}
SOME CONTENT HERE
</div></p>`,
replace: false,
scope: true, // <- needs to be set
//....
You are missing the scope parameter in the directive definition object. You have to set it to scope: true for your directive's template to be able to access a variable defined on the directive controller's scope.
Related
This question already has answers here:
Angular directives - when and how to use compile, controller, pre-link and post-link [closed]
(8 answers)
Closed 5 years ago.
I have a directive that needs to do some HTML rewriting on its contents before it gets compiled. I've found a way to do that but it doesn't seem to work for ng-repeat.
HTML
<body ng-controller="MainCtrl">
<div ng-repeat="source in sources">
<p>
{{ source.name }}
</p>
</div>
<div my-template>
{{ sources }}
<div ng-repeat="source in sources">
<p>
{{ source.name }}
</p>
</div>
</div>
</body>
JS
var app = angular.module('plunker', []);
app.directive("myTemplate", ['$compile', function ($compile) {
return {
restrict: 'A',
link: function ($scope, $element, $attributes) {
var html = $element.html();
console.log(html); // problem?
$element.html(html);
$compile($element.contents())($scope);
}
}
}])
app.controller('MainCtrl', function($scope) {
$scope.sources = [{ name: "source1"}, { name: "source2"}];
});
The output is:
source1
source2
[{"name":"source1"},{"name":"source2"}]
So the sources variable is visible in the directive's scope but ng-repeat fails anyway. console.log(html) shows this in a console:
{{ sources }}
<!-- ngRepeat: source in sources -->
Which hints at ng-repeat being evaluated/compiled before my directive but I don't know where to go further from this.
Plunker: http://plnkr.co/edit/1NJngUK27IRAp0ELVHFc?p=preview
As georgeawg noticed, changing from "link" to "compile" phase helps in this case.
You need to use transclude option in directive, see the example. Important you reference a old version of AngularJS (1.0.8), I use in the example 1.4.1.
https://docs.angularjs.org/api/ng/directive/ngTransclude
angular.module('transcludeExample', [])
.directive('myTemplate', function(){
return {
restrict: 'A',
transclude: true,
template: '<ng-transclude></ng-transclude>'
};
})
.controller('ExampleController', ['$scope', function($scope) {
$scope.sources = [{ name: "source1"}, { name: "source2"}];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<div ng-app="transcludeExample">
<div ng-controller="ExampleController">
<div my-template>
{{ sources }}
<div ng-repeat="source in sources">
<p>
{{ source.name }}
</p>
</div>
</div>
</div>
</div>
I am trying to render html that represents a custom directive using angular so that I nest divs an arbitrary number of times. When I run the below code I do see the tag properly transcluded and the browser in my output shows literally the string text " ". I would like to compile this into html and render and tried doing below with
$compile(element.contents())(scope);
However this results in console errors regarding an orphaned transclusion as well as errors saying it cannot read properties of my objects. It's my thought that this is compiling into something I do not expect it to but am not sure why or how I could see what the result is. Any ideas to get me going here? Is there a better more angular way to do this?
Module with directives
angular.module('myApp.APP', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/testapp', {
templateUrl: 'javascripts/nestedExample/testHTML.html',
controller: 'testController'
});
}])
.controller('testController', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.paneDirective = "<div pane> </div>";
}])
.directive('pane', function($compile) {
return {
scope: {},
template: "<div data-split-pane> <div data-split-pane-component data-width='33%'><p>top</p></div><div data-split-pane-divider data-width='5px'></div> <div data-split-pane-component> <ng-transclude></ng-transclude> <p>bottom</p></div></div>",
transclude: true,
link: function(scope, element, attrs) {
console.log(element.contents())
//$compile(element.contents())(scope);
},
};
});
simplified html
<div ng-controller="testController">
<div pane> {{paneDirective}} </div>
</div>
editing in formatted html code used in template
<div data-split-pane>
<div data-split-pane-component data-width='33%'>
<p>top</p>
</div>
<div data-split-pane-divider data-width='5px'></div>
<div data-split-pane-component>
<ng-transclude></ng-transclude>
<p>bottom</p>
</div>
</div>
Use Angular's ng-transclude directive in your template to specify the insertion point for the transcluded content, like so:
template: '<div ...><div ng-transclude></div> </div>'
I am not sure if this will help, but one way you can approach this is to generate your html string, then just compile the whole thing out. Any other directives you include will get compiled. Check this out and see if its workable for you...
https://plnkr.co/edit/FacA3AwOqJA2QARK28c8?p=preview
angular.module('myApp', [])
.controller('testController', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.paneDirectiveContent = `
<div data-split-pane>
<div data-split-pane-component data-width='33%'>
<p>top</p>
</div>
<div data-split-pane-divider data-width='5px'>
</div>
<sample></sample>
<div data-split-pane-component>
<p>bottom</p>
</div>
</div>
`;
}])
.directive('pane', function($compile) {
return function(scope, element, attrs) {
element.html(scope.$eval(attrs.pane));
$compile(element.contents())(scope);
}
});
Acts on this Html:
<div ng-controller="testController">
<div pane="paneDirectiveContent"></div>
</div>
And any other directives will get rendered:
.directive('sample', function($compile) {
return {
template: `<b>Other directives will be rendered</b>`
}
})
Error: https://docs.angularjs.org/error/ng/areq?p0=ProjectsController&p1=not%20a%20function,%20got%20undefined
I mimicked a ng-repeat that I did earlier, the only difference being I didn't split up the directive, controller, and initializing the app into 3 different javascript files. Also, are you allowed to add multiple ng-apps to the <body> tag? If no, then that may be my problem.
HTML:
<div class="projects">
<h2>Projects</h2>
<div class="row">
<div class="col-lg-12">
<div class="row" ng-controller="ProjectsController">
<div class="project-boxes" ng-repeat="project in projects">
<project-info info="project"></project-info>
</div>
</div>
</div>
</div>
</div>
app.js:
var projectApp = angular.module("projectApp", ['ngAnimate'])
.controller('ProjectsController', ['$scope', function($scope) {
$scope.projects = [
{
link: '#',
img: 'img/box.jpeg',
description: 'Project 1'
}];
}])
.directive('projectsInfo', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'components/projects/projects-template/projectsInfo.html'
};
});
Question: Why doesn't my simple directive have access to its controller when the element is used on certain pages?
Additional Info: The directive works on the main page. The HTML I could not use my directive on is called using ui-view and is included below.
<div ui-view></div>
Directive:
var postcardAppDirectives = angular.module('postcards.directives', ['postcards.controllers']);
postcardAppDirectives.directive('postcardLink', function () {
return {
restrict: "AE",
template: '<div class="well">{{ ctrl.ownerObj.user }}</div>',
controller: 'postcardLinkController as ctrl'
}
});
Controller:
var PostcardsControllers = angular.module('postcards.controllers', ['postcards.factories', 'postcards.services']);
PostcardsControllers.controller('postcardLinkController', [function ()
this.ownerObj = {
"user": 'test_owner',
"pointofthis": 'inane testing'
}
}]);
HTML (Does Not Work):
<div ng-controller="DashboardAccountController as infoCtrl" class="container full-container">
<!------------------ DIRECTIVE REFERENCE HERE -------->
<postcard-link></postcard-link>
<h1>
<small>Welcome</small>
<username-text></username-text>
</h1>
<img ng-show="infoCtrl.user.member_profile.profile_image" ng-src="{{ infoCtrl.user.member_profile.profile_image }}"
style="min-height:100px;height:100px;" alt='profileImage'/>
<button type="button" class="btn btn-link col-md-offset-2" ng-click="infoCtrl.loadPage()">Modify Account</button>
<div class="row">
...
HTML (Working Fine)
<ng-include src="'static/app/carousel/_carousel.html'"></ng-include>
<postcard-link></postcard-link>
<div class="container">
<section style="text-align: center">
<h1>How It Works</h1>
I see you used different module that's why one module don't know another module directive. you should use same module.
Like:
js:
var app = angular.module('postcards', []);//add dependencies in []
app.directive('postcardLink', function () {
return {
restrict: "AE",
template: '<div class="well">{{ ctrl.ownerObj.user }}</div>',
controller: 'postcardLinkController as ctrl'
}
});
app.controller('postcardLinkController', [function () {
this.ownerObj = {
"user": 'test_owner',
"pointofthis": 'inane testing'
}
}]);
use as usual html:
<postcard-link></postcard-link>
I have created two directives and included them in my html. However, only the first one executes and nothing after it.
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<div directive-one=""/>
<div directive-two=""/>
<div> Hello I am {{name}} and I am {{age}} years old!</div>
</div>
<script type="text/ng-template" id="myTemplate.html">
<div>Name: <input type='text' ng-model='name'/></div>
</script>
</div>
Javascript:
var app = angular.module('myApp', []);
app.controller('MyCtrl', function ($scope) {
$scope.name = "Jason";
$scope.age = "20";
});
app.directive('directiveOne', function () {
return {
replace: true,
template: "<div>Age: <input type = 'text' id = 'age' ng-model='age'>
</input></div>"
}
});
app.directive('directiveTwo', function () {
return {
replace: true,
templateUrl: "myTemplate.html"
}
});
Here is the fiddle: DEMO
Can't figure out what the issue is. Any help is appreciated.
You have to close your div tags with the directives in them and you don't need the ="" after either.
<div directive-one></div>
<div directive-two></div>
Here's an updated fiddle with it working.