Using directives in angularjs with templateUrl - angularjs

I wrote a code with angularjs that uses a directive to bring a list of categories from show-category.html file and show them on the index page, I did everything as I've learned but still can't get the categories to be displayed when index.html is loaded.
inside app.js file
app.directive('showCategories', function() {
return {
restrict: 'E',
templateUrl: 'show-categories.html'
};
});
you can see the full code on plunker here:
http://plnkr.co/edit/FSsNAq?p=preview

You placed your directive definition right in the middle of the controller, take it outside and it works (barring some other non-existing functions you have there):
app.controller("BookCtrl", function($scope) {
$scope.categories = [{
"id": 0,
"name": "Type"
}, {
"id": 1,
"name": "Date"
}, {
"id": 1,
"name": "Name"
}
];
...
});
app.directive('showCategories', function() {
return {
restrict: 'E',
templateUrl: 'show-categories.html'
};
});
Plunker

Related

Angular Js | Lazy Load Directive based on ViewPort

Is it possible to lazy load directives?
Say we have 20 Directives on a single page, and on scroll, if the directive is in viewport it should get rendered.Otherwise no action should take take.
It should behave as lazy loading of images.
Can we achieve it??
You Question is unspecific but I'm willing to try:
Imagine your HTML like that
<div ng-app="app" ng-controller="myCtrl">
<ul id="myList">
<li ng-repeat="item in data">
<mydirective item="item"></mydirective>
</li>
</ul>
the directive is is doing whatever needed for your item
angular.module("mydirective", []).directive("mydirective", function() {
return {
templateUrl: 'templates/mytemplate.php',
replace: false,
transclude: false,
restrict: 'A',
scope: {
item: "="
}
};
});
The controller would look like that
app.controller("myCtrl", ["$scope", function ($scope) {
$scope.data = [
{id: 1, "name": "img1"},
{id: 2, "name": "img2"},
{id: 3, "name": "img3"}
];
$("#myList").bind("scrollend", function(e){
$scope.loadNextElements();
});
$scope.loadNextElements = function() {
// add new elements
$scope.data.push({id: 4, "name": "img4"});
$scope.data.push({id: 5, "name": "img5"});
$scope.data.push({id: 6, "name": "img6"});
...
}
}]);

Why it not working? following angularjs Example

Here i created some Example.. In this Example 1 I put directly
//{{arr.Description}}
like this its working but via directive its not working what misstake i did
can please explain me?
Thanks friends
var app = angular.module('components', []);
app.directive('subpane', function() {
return {
restrict: 'E',
scope: {
array: '#'
},
template:'<li ng-repeat="arr in array">{{arr.Description}}</li>',
link: function(scope) {
}
};
})
app.controller('tabController', ['$scope', function ($scope) {
$scope.array =[{
"title": 0,
"Description": "Select your option"
},
{
"title": 1,
"Description": "Male"
},
{
"title": 2,
"Description": "Female"
},
{
"title": 3,
"Description": "Unknown"
}];
}])
<script data-require="angular.js#~1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<body ng-app="components" ng-controller="tabController">
<span>Example 1 Working fine</span>
<li ng-repeat="arr in array">{{arr.Description}}</li>
<span>Example 2 Not Working.. Here also same thing only i did via directive.. why this is not working?</span>
<subpane array={{array}}></subpane>
</body>
Use '=' for the scope param and remove '{{}}' in the markup.
var app = angular.module('components', []);
app.directive('subpane', function() {
return {
restrict: 'E',
scope: {
array: '='
},
template:'<li ng-repeat="arr in array">{{arr.Description}}</li>',
link: function(scope) {
}
};
})
app.controller('tabController', ['$scope', function ($scope) {
$scope.array =[{
"title": 0,
"Description": "Select your option"
},
{
"title": 1,
"Description": "Male"
},
{
"title": 2,
"Description": "Female"
},
{
"title": 3,
"Description": "Unknown"
}];
}])
<script data-require="angular.js#~1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<body ng-app="components" ng-controller="tabController">
<span>Example 1 Working fine</span>
<li ng-repeat="arr in array">{{arr.Description}}</li>
<span>Example 2 Not Working.. Here also same thing only i did via directive.. why this is not working?</span>
<subpane array=array></subpane>
</body>
You need to add "plain: true" in the directive if you don't use templateUrl
app.directive('subpane', function() {
return {
restrict: 'E',
scope: {
array: '=' // = not #, = is for object/array ; = is for text/number.
},
template:'<li ng-repeat="arr in array">{{arr.Description}}</li>',
plain: true,
link: function(scope) {
}
};
})
In the html dont put {{ }} in the parameter array
<subpane array="array"></subpane>
camden_kid has posted an acceptable answer but not probably the best one. In your template:
<subpane array={{array}}></subpane>
Must have the quotes:
<subpane array="{{array}}"></subpane>
Please also note that camden_kid suggested to change
scope: {
array: '#'
}
To
scope: {
array: '='
}
This is what is needed for double binding and it is only necessary if you have to edit in any way the original value (in the parent scope) from within the directive's isolated scope.
You probably should read more carefully the docs: https://docs.angularjs.org/guide/directive

Angular Directive param value array

How can I use an array which is given as an attribute of a directive in the linker of the directive
[
{
"state": "bootstrap.formwizard.ethernet",
"placeHolder": "lNetworkConfWizardMenu",
"stepNumber": 1,
"next": "bootstrap.formwizard.datetime"
},
{
"state": "bootstrap.formwizard.datetime",
"placeHolder": "lDateSetWizardMenu",
"previous": "bootstrap.formwizard.ethernet",
"stepNumber": 2,
"next": "bootstrap.formwizard.countrysetting"
},
]
Template
<formprogressbar steps="wizardSteps" />
Directive
angular.module('smaModules.formProgressbar', [])
.directive('formprogressbar', function ($log) {
function linker(scope, element, attrs) {
console.log(scope);
test = attrs.steps[0]
console.log(test);
}
return {
restrict: 'E',
scope: {
steps: '='
},
template: 'Steps: {{ steps[0] }}',
link: linker
};
});
In the template I have access but not in the linker function. What's wrong?
You want to access the steps off of your scope for it to be the same as in your template. Also you should declare your variable for best practice.
.directive('formprogressbar', function ($log, $filter) {
function linker(scope, element, attrs) {
console.log(scope);
var test = scope.steps[0];
console.log(test);
}
return {
restrict: 'E',
scope: {
steps: '='
},
template: 'Steps: {{ steps[0] }}',
link: linker
};
});
attrs.steps[0] was probably giving you the result of 'w', this is because attrs.steps is the string "wizardSteps", the first index of the string being 'w'.

Why is my $watch showing an array that appears to be empty?

I'm working on creating an AngularJS directive in order to use D3 to render a visualization, but I'm running into problems when it comes to setting a $watch. The majority of my stuff looks extremely similar to the AngularJS tutorial. My resources are configured in a file called resources.json, which I'm sure is returning correctly.
Here's the relevant code of what I have so far:
app.js
var resourceApp = angular.module("resourceApp", [
'ngRoute',
'resourceControllers',
'resourceDirectives',
'resourceServices'
]);
/* ... routing config ... */
controllers.js
var resourceControllers = angular.module('resourceControllers', []);
resourceControllers.controller("OverviewCtrl", [ '$scope', 'Resource',
function($scope, Resource) {
$scope.resources = Resource.query();
}
]);
/* ... other controllers ... */
directives.js
var resourceDirectives = angular.module('resourceDirectives', []);
resourceDirectives.directive("resourceVisualization", function() {
return {
restrict: 'E',
scope: {
resources: '='
},
link: function(scope, el, attrs) {
// svg setup is here
scope.$watch("resources", function(nRes, oRes) {
if (nRes) {
// this logs an array of Resource objects
//(once expanded in Firebug)
console.log(nRes);
var cats = nRes.map(function(r) { return r.category; });
// this logs an empty array
console.log(cats);
}
});
}
};
});
overview.html
<ul>
<li ng-repeat="resource in resources">
{{resource.name}}
</li>
</ul>
<resource-visualization resources="resources"></resource-visualization>
resources.json (which is what services.js is configured to pull from)
[
{
"id": 1,
"category": "test",
"type": "sample",
"name": "Test1"
},
{
"id": 2,
"category": "test",
"type": "sample4",
"name": "Test2"
},
{
"id": 3,
"category": "fake",
"type": "sample1",
"name": "Test3"
},
{
"id": 4,
"category": "new",
"type": "sample2",
"name": "Test4"
}]
Now, I know that the REST call is working, because the <ul> is populated. However, in the directive, the logging statements are returning empty arrays. I'm aware of the the async-ness of $resource, but the object that is logged first in the $watch contains $resolved: true.
Am I missing something?
It's all fine. Your call to Resource.query() returns immediately an empty array. If the ajax call returns the real data, your array will be filled with the arrived data. So the first assignment to $scope.resources fires your $watch function with an empty array. You my solve your problem if you are using the $watchCollection function. See http://docs.angularjs.org/api/ng.$rootScope.Scope for further information.

scope of event arguments in angular directive

I have the following angular app to create a menu of sections/products.
at present when rendered and hitting the 'add' button that is rendered within each li I want to add a section/product as a sub of that section however multiple new children are created.
ultimately I wish to display a form which when submitted will create the child but that is the next step. Right now I need to limit the scope to the current section and not have multiple bound clicks.
If you need more information please state and I will post in an edit.
Some sample data data.
{
"sections":[
{
"name":"Flags",
"sections":[
{
"name":"Europe",
"sections":[],
"products":[
{ "name": "France" },
{ "name": "Germany" },
{ "name": "Ireland" },
{ "name": "England" }
]
},
{
"name": "Africa",
"sections":[],
"products":[
{ "name": "Egypt" },
{ "name": "Nigeria" },
{ "name": "Chad" }
]
},
{
"name": "South America",
"sections":[],
"products": [
{ "name": "Brasil" },
{ "name": "Argentina" },
{ "name": "Peru" }
]
}
],
"products":[]
},
{
"name": "Maps",
"sections":[
{
"name": "Africa",
"sections":[],
"products":[
{ "name": "Egypt" },
{ "name": "Nigeria" },
{ "name": "Chad" }
]
},
{
"name": "South America",
"sections":[],
"products": [
{ "name": "Brasil" },
{ "name": "Argentina" },
{ "name": "Peru" }
]
}
],
"products":[]
}
],
"products":[]
}
The app.
'use strict';
var menuApp = angular.module('menuApp', []);
menuApp
.directive('sections', function () {
return {
restrict: "E",
replace: true,
scope: {
sections: '='
},
template: '<ul><section ng-repeat="section in sections" section="section" /></ul>'
};
})
.directive('section', function ($compile) {
return {
restrict: "E",
replace: true,
scope: {
section: '=section'
},
template: '<li class="section">{{section.name}} <button ng-click="addSub(section)">Add</button></li>',
link: function (scope, element, attrs, controller) {
if (angular.isArray(scope.section.sections)) {
element.append("<sections sections='section.sections'></sections>");
$compile(element.contents())(scope);
}
if(angular.isArray(scope.section.products)){
element.append("<products products='section.products'></products>");
$compile(element.contents())(scope);
};
},
controller : function($scope){
console.log($scope);
$scope.addSub = function (section){
//console.log(section,'Adding Sub');
section.sections.push({"name":"Section","sections":[],"products":[]});
};
}
};
})
.directive('products', function () {
return {
restrict: "E",
replace: true,
scope: {
products: '='
},
template: '<ul><product ng-repeat="product in products" product="product"></product></ul>'
};
})
.directive('product', function ($compile) {
return {
restrict: "E",
replace: true,
scope: {
product: '='
},
template: '<li class="product">{{product.name}}</li>'
};
});
menuApp.controller('menuCtrl', function menuCtrl($scope,$http) {
$http.get('/ajax/getvenuesmenu?venueID='+venueMenu.venueId).success(function(resp) {
$scope.sections = resp;
});
$scope.add = function(data){
data.push({"name":"Section","sections":[]});
};
});
Took me a bit to figure it out but here's the basic problem, you are compiling the full contents of section 2 extra times and each compile seems to add a new event handler.
Instead of compiling the contents of element each time you make an append of new template, compile the template itself (outside of the DOM) and then append the compiled template. This way the ng-click handler doesn't get compiled again other than initial scope creation
Here's an abbreviated version with one template appended:
link: function (scope, element, attrs, controller) {
if (angular.isArray(scope.section.sections)) {
/* compile outside of the DOM*/
var subsections = $compile("<sections sections='section.sections'></sections>")(scope);
/* append compilation*/
element.append(subsections);
}
DEMO
Another approach would be to create a complete template string in link by checking for subsections and products, then compiling everything all at once....instead of using template option
Code for alternate approach compiling complete section at once:
.directive('section', function ($compile, $timeout) {
return {
restrict: "E",
scope: {
section: '=section'
},
link: function (scope, element, attrs, controller) {
var template = '<li class="section">{{section.name}} <button ng-click="addSub(section)">Add</button>';
if (angular.isArray(scope.section.sections)) {
template += "<sections sections='section.sections'></sections>";
}
if (angular.isArray(scope.section.products)) {
template += "<products products='section.products'></products>";
};
template += '</li>';
var compiledTemplate = $compile(template)(scope);
element.replaceWith(compiledTemplate);
scope.addSub = function (section) {
section.sections.push({ "name": "Section", "sections": [], "products": []
});
};
}
};
})
DEMO-Alt

Resources