Hi I have a directive like,
mainApp.directive('myMenu',function(){
return {
restrict : 'E',
scope :{menuItems : "=menuItems"},
compile: function(element, attributes) {
var linkFunction = function($scope, element, attributes){
for (i = 0;i<$scope.menuItems.length;i++){
element.append('<li>'+$scope.menuItems[i].name+'</li>');
}
}
return linkFunction;
}
}
});
I am using it like below in my HTML
<my-menu menuItems="menuItems"></my-menu>
But in the console I am getting an error like
TypeError: Cannot read property 'length' of undefined
The problem could be that, then the linking phase is executed the menuitems may not be loaded so $scope.menuItems may be undefined.
A better solution could be
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "="
},
template: '<ul><li ng-repeat="item in menuItems">{{item.name}}</li></ul>'
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
If you can't use template then
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "=menuItems"
},
link: function($scope, element, attributes) {
$scope.$watch('menuItems', function(value) {
element.empty();
angular.forEach(value, function(item) {
element.append('<li>' + item.name + '</li>');
});
});
}
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
The Issue was with the name I used, like menuItems in directive should be equal to menu-items, solved the issue by repalcing the menuItems to menu.
use $scope.$eval(attributes.menuItems) inside compile function to get menuItems
Related
I am trying to insert a node dynamically, but the appended html is not getting compiled correctly.
This is the code on Fiddle.
<body ng-app="app">
<div ng-controller="appController">
{{tittle}}
<div name="name" ng-repeat="d in data" ng-click="click(d.id)" id="div{{d.id}}">{{d.id}} click me</div>
</div>
(function () {
var app = angular.module('app', []);
app.controller('appController', function ($scope, $compile) {
$scope.tittle = "test";
$scope.data = [{ id: 1, child: [{name:'renjith'}] }, { id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }];
$scope.click = function (data, $element) {
debugger;
var scope = { id: "12" };
angular.element(document.querySelector("#div" + data)).append($compile("<div ngTransclude>{{d.id}}</div>")($scope.data));
}
});
})();
Having a problem with nested directives and inheriting the scope from a parent. It happens that the scope I want to inherit from is an isolated scope. I've the code below
test.html
<div ng-controller="testCtrl as test">
<special-select data-selected-item="test.selectedThing">
<special-select-selected-item></special-select-selected-item>
</special-select>
</div>
app.js
angular
.module('testApp', ['special-inputs'])
.controller('testCtrl', [
function() {
this.items = [
{ id: 1, name: 'alex 1', displayName:"alex 1 dn", imageUrl: 'http://placehold.it/505x100' }
];
this.selectedThing = this.items[0];
this.test = function() {
console.log('test fn');
}
}
]);
special-inputs.js
angular
.module('special-inputs', [])
.directive('specialSelect', [
function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="container" ng-transclude></div>',
scope: {
selectedItem: '='
},
link: {
pre: function(scope) {
console.log('parent link pre - ', scope.selectedItem);
},
post: function(scope) {
console.log('parent link post - ', scope.selectedItem);
}
}
}
}
])
.directive('specialSelectSelectedItem', [
function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="selected" ng-transclude></div>',
scope: true,
link: {
pre: function(scope) {
console.log('child link pre - ', scope.selectedItem);
},
post: function(scope) {
console.log('child link post - ', scope.selectedItem);
}
}
}
}
])
As you can see the nested directive is trying to create it's own isolate scope but grab the "selectedItem" from the isolate scope of the parent.
The docs say it should inherit and transclusion is semi prototypical, so I'm wondering why it's not working. Thanks. Any help would be appreciated
UPDATE
I've updated the code to show the problem I'm having now. It seems that the scope item is set in the pre + post link functions on the parent, but never get put down to the child. The console outputs
2015-12-29 15:13:44.258 special-select.js:35 parent link pre - Object {id: 1, name: "alex 1", displayName: "alex 1 dn", imageUrl: "http://placehold.it/505x100"}
2015-12-29 15:13:44.258 special-select.js:64 child link pre - undefined
2015-12-29 15:13:44.260 special-select.js:67 child link post - undefined
2015-12-29 15:13:44.260 special-select.js:38 parent link post - Object {id: 1, name: "alex 1", displayName: "alex 1 dn", imageUrl: "http://placehold.it/505x100"}
I've tried using scope.$watch and attrs.$observe to populate the valu
Factory:
(function(){
angular
.module('projectApp')
.factory('weatherfactory', weatherfactory);
weatherfactory.$inject=['$http'];
function weatherfactory($http){
var cities=[
{name: "Łódź", link: "http://api.openweathermap.org/data/2.5/forecast/daily? q=Lodz&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Warszawa", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Wrocław", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Kraków", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Gdańsk", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Londyn", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"}
];
var weat={};
var service={
getCities: getCities,
GetWeather: _GetWeather
};
return service;
function _GetWeather(link){
return $http.get(link);
}
function getCities(){
return cities;
}
}
})();
In controller i get data from factory:
sa.cities=weatherfactory.getCities();
And in view i am trying to show data in but it does'n work:
<select class="form-control" ng-options="item.name as item.link for item in sa.cities"></select>
I am using controllerAs approach.
ng-options requires ng-model to be specified:
<select class="form-control" ng-model="something" ng-options="item.name as item.link for item in sa.cities"></select>
var app = angular.module('app', []);
app.controller('myController', function($scope) {
var sa = this;
sa.cities = [{
name: "Łódź",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Lodz&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Warszawa",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Wrocław",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Kraków",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Gdańsk",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Londyn",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app' ng-controller='myController as sa'>
<select ng-model="selected" ng-options="item.name as item.link for item in sa.cities"></select>
</div>
In the example from the Angular documentation, a directive can be used by putting its name as an attribute in a <div>. The example given is
<div ng-controller="Controller">
<div my-customer></div>
</div>
with the js looking like
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
However, in a similar example, over here, if I change the html code from <tree> to <div tree>, the code no longer works.
Why not?
The code from JS Fiddle:
<div ng-app="myapp">
<div ng-controller="TreeCtrl">
<tree family="treeFamily">
<p>{{ family.name }}</p>
</tree>
</div>
</div>
var module = angular.module('myapp', []);
module.controller("TreeCtrl", function($scope) {
$scope.treeFamily = {
name : "Parent",
children: [{
name : "Child1",
children: [{
name : "Grandchild1",
children: []
},{
name : "Grandchild2",
children: []
},{
name : "Grandchild3",
children: []
}]
}, {
name: "Child2",
children: []
}]
};
});
module.directive("tree", function($compile) {
return {
restrict: "E",
transclude: true,
scope: {family: '='},
template:
'<ul>' +
'<li ng-transclude></li>' +
'<li ng-repeat="child in family.children">' +
'<tree family="child">{{family.name}}</tree>' +
'</li>' +
'</ul>',
compile: function(tElement, tAttr, transclude) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function(clone, scope) {
iElement.append(clone);
});
};
}
};
});
tree {
margin-left: 20px;
display: block;
}
The restrict option is used to specify how a directive can be invoked on the page. There are four different ways to invoke a directive, so there are four valid options for restrict:
'A' - attribute - <span ng-sparkline></span>
'E' - element - <ng-sparkline></ng-sparkline>
'C' - class - <span class="ng-sparkline"></span>
'M' - comment - <!-- directive: ng-sparkline -->
In your case its not working because it is defined as restrict 'E' - element.
It's because of the restrict option in the directive.
Here it is set to e which means to match only the element name.
More https://docs.angularjs.org/guide/directive
In a controller I have defined two different arrays and I need to pass different arrays to directives, but the same item array is getting passed only.
Actually this is just snippets. In my actual code this directive I am using inside another.
Is that causing problem or any other way?
<div my-sticky tags="items" template_name="test2.html"></div>
<div my-sticky tags="kititems" template_name="test2.html"></div>
JS:
app.controller('MyCtrl', function($scope, $http) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
templateUrl : function(el, attrs) {
return attrs.templateName;
},
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});
Try this one
Working Demo
Inside your templateURL(test2.html) should like this i.e,
<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>
You might try to access items or kititems both are not in current scope because you have used isolated scope here.For clarity template used here instead of templateURL.
HTML Markup:
<div ng-controller="MyCtrl">
MyCtrl
<div my-sticky tags="items"></div>
<hr/>
<div my-sticky tags="kititems"></div>
</div>
angular modules
var app = angular.module("myApp",[]);
app.controller('MyCtrl', function($scope) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
template :'<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>',
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
// console.log($scope);
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});