Plunker Demo
I created two custom directives for template usage in a ng-repeater.
Outside of the ng-repeater, the directives work. Once I try to dynamically load the directives in a ng-repeater, the directives do not load at all. In fact - when use "Inspect Element" - the {{expressions}} do not dynamically update.
Here is how my ng-repeater looks:
<div ng-repeat="section in content">
<section class="section-{{ section.block }}" block="{{ section.block }}">{{ section.block }}</section>
</div>
I am calling the directives via restrict: C
Here is one of the directives:
.directive('sectionHeader', function() {
return {
restrict: 'EAC',
scope: {
block: '='
},
templateUrl: 'sectionHeader.html'
};
})
Here is the templateURL:
<div class="container">
<figure class="icon">
<img class="img-fluid" ng-src="{{ block.icon }}" title="App Icon">
</figure>
<h1>{{ block.title }}</h1>
<figure class="hero">
<img class="img-fluid" ng-src="{{ block.image }}" title="App">
</figure>
</div>
And a snippet of the JSON:
$scope.content = [
{
block: 'header',
icon: 'http://www.placehold.it/128x128?text=PLACEHOLpng',
title: 'Header Title',
image: 'http://www.placehold.it/1200x675?text=PLACEHOLDER'
},...
];
Now, I believe The templateUrls are not loading because it seems like the directives are being executed before ng-repeat get its content. What I believe I need to do is to execute the directives after ng-repeat receives the content. I am not sure where to start.
Thanks.
You can achieve that using $compile function.
See example on plunker.
Create general directive with $compile.
.directive('section', function($compile) {
return {
restrict: 'EAC',
scope: {
block: '='
},
link:function(scope,elem){
if(scope.block)
$compile(elem.html('<section-'+scope.block.block+' block="block">'))(scope);
}
};
})
Create two different directive.
.directive('sectionHeader', function() {
return {
restrict: 'EAC',
scope: true,
templateUrl: 'sectionHeader.html'
};
})
.directive('sectionHundred', function() {
return {
restrict: 'EAC',
scope: true,
templateUrl: 'sectionHundred.html'
};
});
And usage
<div ng-repeat="section in content">
<section block="section"></section>
</div>
JSON data:
[
{
block: 'header',
icon: 'http://www.placehold.it/128x128?text=PLACEHOLpng',
title: 'Header Title',
image: 'http://www.placehold.it/1200x675?text=PLACEHOLDER'
},
{
block: 'hundred',
icon: 'http://www.placehold.it/128x128?text=PLACEHOLpg',
title: 'Hundred Title',
content: 'Hundred Content',
link: {
copy: 'Link Copy',
title: 'Link Title',
url: '#'
},
image: 'http://www.placehold.it/1200x675?text=PLACEHOLDER'
}
];
You cannot use directive by passing their name dynamically.
But a possible workaround is to use ng-switch instead :
<div ng-repeat="section in content">
<div ng-switch="section.block">
<section ng-switch-when="header" class="section-header" block="{{ section.block }}">{{ section.block }}</section>
... another ng-switch...
</div>
See this post for more details
Though #stepan-kasyanenko gave me a correct answer - I further removed extra lines of code not needed to minimize the build.
New Plunker Link with answer added.
home.html
Old:
<div ng-repeat="section in content">
<section block="section"></section>
</div>
New:
<section ng-repeat="section in content" block="section"></section>
I placed the ng-repeat in the <section> that calls the directive
directives
.directive('sectionHeader', function() {
return {
restrict: 'EAC',
replace: true,
scope: true,
templateUrl: 'sectionHeader.html'
};
});
I added replace: true, to both directives because the ng-repeat builds out another <section> inside of the <section ng-repeat> element
Thank you #stepan-kasyanenko
Related
I have this function:
$scope.link = function (){
return [{
value: 'https://www.google.it',
text: 'go to details'
}
]
}
i want pass this function in parent controller to the child, i tried to do this:
.directive('card', function(){
var TPL = `<div class="card" style="width: 18rem">
<div class="card-body">
<p>{{c.paragrafo}}</p>
<h4 class="card-title">{{c.title}}</h4>
<h6 class="card-subtitle mb-2 text-muted">{{c.paragrafo2}}</h6>
<p class="card-text"></p>
<a class="card-link">{{c.link()}}</a>
</div>
</div>`
var directive = {
restrict: 'E',
template: TPL,
scope: {
paragrafo: '#',
title: '#',
paragrafo2: '#',
link: '&'
},
controller: ctrlFn,
controllerAs: 'c',
bindToController: true
};
return directive;
function ctrlFn(){
}
})
and in index.html:
<body ng-app="app" ng-cloak>
<div ng-controller="MainCtrl">
<div class="container">
<div class="row">
<div class="col" ng-repeat="demo in demos">
<card title="{{demo.title}}"
paragrafo="{{demo.paragrafo}}"
paragrafo2="{{demo.paragrafo2}}"
link="link()"
></card>
</div>
</div>
</div>
</div>
</body>
But the body of the function returns to me, how can i fix this?
I want to see the link text "go to details" and when I click on it it takes me to the google page
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.
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 this directive:
App.directive('typeahead', function($timeout) {
return {
restrict: 'AEC',
scope: {
items: '=',
prompt:'#',
title: '#',
subtitle:'#',
model: '=',
selindex: '=',
onSelect:'&'
},
link:function(scope,elem,attrs){
scope.handleSelection=function(selectedItem){
scope.model=selectedItem;
scope.current=0;
scope.selected=true;
$timeout(function(){
scope.onSelect();
},200);
};
scope.current=0;
scope.selected=true;
scope.isCurrent=function(index){
return scope.current==index;
};
scope.setCurrent=function(index){
scope.current=index;
};
},
templateUrl: 'templates/templateurl.html'
}
});
I have this html:
<typeahead items="items" prompt="Start typing a US state" title="name" subtitle="id" model="name" on-select="onItemSelected()"/>
Directice is using this template:
<input type="text" ng-model="model" placeholder="{{prompt}}" ng-keydown="selected=false"/><br/>
<div class="items" ng-hide="!model.length || selected">
<div class="item" ng-repeat="item in items | filter:model track by $index" ng-click="handleSelection(item[subtitle])" style="cursor:pointer" ng-class="{active:isCurrent($index)}" ng-mouseenter="setCurrent($index)">
<p class="title">{{item[title]}}</p>
<p class="subtitle">{{item[subtitle]}}</p>
</div>
This calls the function in the directive:
scope.handleSelection=function(selectedSubtitle){
What I try to achieve is to call:
handleSelection(item[title], item[subtitle])
And pick it up in the directive like this:
scope.handleSelection=function(selectedTitle, selectedSubtitle){
However, the selectedSubtitle in the directive remains empty. How can I pass the additional parameter to the directive?