I began working with angularjs and have a problem.
app.js (just the relevant route)
$routeProvider.when('/search', {templateUrl: 'partials/search-results.html', controller: 'SearchController', title: 'Product Search'});
search-results.html
<div product-list></div>
index.html
<div class="page_content_offset p_bottom_0">
<div class="container mh600">
<div ng-view></div>
</div>
</div>
product-list.html
<div ng-repeat="item in items">
<p>{{item.address_components[0].long_name}}</p>
</div>
controller.js just relevant code:
$scope.search = function() {
$scope.loading = true;
$scope.items = {};
ProductSearchService.searchItems($scope.term).then(function(data) {
$scope.items = data;
$scope.loading = false;
});
};
directives.js (just relevant code)
directive('productList', function() {
return {
restrict: 'EA',
templateUrl: 'partials/list/product-list.html'
};
My Problem now is:
The ProductSearchService loads the data. But the directive not rendering as expected.
If i move the directive code from search-results.html to my index.html like this:
<div class="page_content_offset p_bottom_0">
<div class="container mh600">
<div product-list></div>
<div class="clearfix"></div>
</div>
</div>
evrything is rendered nicely. So i think i included my directive wrongly or forgot something important.
I've created a plunkr similar to my setup:
http://plnkr.co/edit/60dvyFnz74yrsK12J2J4?p=preview
Everything works fine in that.
In my local application i changed the "product-list.html" model property access to this
<div ng-repeat="item in $parent.items">
<p>{{item.address_components[0].long_name}}</p>
</div>
Update controllers.js
angular.module('myApp.controllers', [])
.controller('SearchController', ['$scope','$http','ProductSearchService', function($scope, $http, ProductSearchService) {
$scope.items = {};
$scope.search = function() {
$scope.loading = true;
ProductSearchService.searchItems($scope.term).then(function(data) {
$scope.items = data;
$scope.loading = false;
});
};
}]);
Update 2:
I now have a plnkr where the problem can be shown:
http://plnkr.co/edit/QgU1cf3JeJYKu6Fkl9zv?p=preview
You did not set any scope attribute to your directive.
This means that your directive use the defining/containing scope as the directive own scope.
Thus , the scope that is used in product-list.html is the same as the one used by search-result.html (and so by the SearchController).
The only reason , you could have to use the $parent.items would be if you specified scope:{}, in your directive.You can even test it in your plunker (I did).
Can you double check the directive scope attribute ?
Thx
directive('productList', function() {
return {
restrict: 'EA',
replace: true,
templateUrl: '<section data-ng-include=" 'partials/list/product-list.html' "></section>'
};
});
try this one as the directive :)
Related
I am using angularJs to write a simple web application and I am facing a weird issue where the 1st defined directive is always working while others are not.
I have 3 directives defined (hostTableArea, hostGraphArea and hostSummeryArea) these are the details of those directives
var app = angular.module("app-directive", []);
app.directive("hostTableArea", function() {
return {
restrict:'E',
scope: {
value: '='
},
templateUrl : "/js/directive/host-table-area/host-table-area.html",
link: function ($scope) {
console.log($scope.value);
}
};
});
var app = angular.module("app-directive", []);
app.directive("hostGraphArea", function() {
return {
restrict:'E',
scope: {
val2: '='
},
templateUrl : "/js/directive/host-graph-area/host-graph-area.html",
link: function ($scope) {
console.log($scope.val2);
}
};
});
var app = angular.module("app-directive", []);
app.directive("hostSummeryArea", function() {
return {
restrict:'E',
scope: {
val3: '='
},
templateUrl : "/js/directive/host-summery-area/host-summery-area.html",
link: function ($scope) {
console.log($scope.val3);
}
};
});
I have corresponding HTML files for each directive and the HTML file is defined in the templateURL location. All the directives are used in the index.html page
<div class="tab-content area-content">
<div class="tab-pane" id="tableView" >
<host-table-area data-value="'text-1'"></host-table-area>
</div>
<div class="tab-pane active" id="graphView">
<host-graph-area data-val2="'text-2'"></host-graph-area>
</div>
<div class="tab-pane active" id="summaryView">
<host-summery-area val3="'text-3'"></host-summery-area>
</div>
</div>
And all the 3 directives are defined at the index.html page in the following order
<script src="/js/directive/host-summery-area/host-summery-area.js"></script>
<script src="/js/directive/host-graph-area/host-graph-area.js"></script>
<script src="/js/directive/host-table-area/host-table-area.js"></script>
The problem here is, the 1st defining directive will always be displayed while the other 2 does not. If the order is changed, it always displays the 1st defined directive in this list.
What is the reason and how to fix this?
This syntax that you use:
var app = angular.module("app-directive", []);
creates a new module each time.Create the module once and then register each directive like this:
angular.module("app-directive").directive("hostTableArea", function() {};
I have an angularjs sample code snippet here where i can bind the html tags using ng-bind-html directive. But how can I include some other tags like angularjs ng-click, id tag etc inside ngBindHtml directive like
Test
My sample code is here:
var app = angular.module("myApp", ['ngSanitize']);
app.controller("myCtrl", function($scope) {
$scope.myText = "<a href='#' ng-click='someFunction()'>Test</a>";
$scope.someFunction = function(){
alert("Link Clicked");
};
});
FYI, the data is loaded dynamically from server side script and i have to use ng-bind-html inside ng-repeat directive and i have to pass respective id's to click events something like ng-click="myFunction(x.id)" as in sample 2.
As suggested #Dr Jones, you need use $compile directive.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.myText = "<button ng-click='someFunction(1)'>{{text}}</button>";
$scope.text = "Test";
$scope.someFunction = function(val) {
console.log(val);
};
})
.directive('bindHtmlCompile', function($compile) {
return {
restrict: "A",
scope: {
bindHtmlCompile: "="
},
link: function(scope, elem) {
scope.$watch("bindHtmlCompile", function(newVal) {
elem.html('');
var newElem = angular.element(newVal);
var compileNewElem = $compile(newElem)(scope.$parent);
elem.append(compileNewElem);
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<h3>
Write code for test button
</h3>
<textarea cols="100" ng-model="myText"></textarea>
<div bind-html-compile="myText">
</div>
</div>
</div>
I'm new to Angular and I have been working on small project. I have a question about directives and ng-click.
Every time I click on the div tag to setoff the setlock() function it never fires. Could this be caused by my directive and controller being in separate files? And is there a way to make this work using link: ?
Thanks.
Directive.js
(function(){
'use strict';
angular
.module('widget.ballslot')
.directive('ballSlot', ballSlot);
function ballSlot(){
var directive = {
restrict: 'E',
scope: true,
templateUrl: 'app/widgets/ballslot.html',
controller: 'Ballslot',
}
return directive;
}
})();
Controller.js
(function(){
'use strict';
angular
.module('widget.ballslot')
.controller('Ballslot', Ballslot);
function Ballslot() {
var vm = this;
vm.locked = true;
function setlock() {
vm.locked = !vm.locked;
};
};
})();
page.html
<div data-ng-controller='Ballslot as vm' class='ball'>
<div id='background-purple' ng-click='vm.setlock()'>
<i class="fa fa-lock" ng-hide="vm.locked"> </i>
<i class="fa fa-unlock-alt" ng-show="vm.locked"></i>
</div>
</div>
Looks like the setLock function is not accessible to html. This is because it's a private function and not exposed through vm.
Try changing
function setlock() {
vm.locked = !vm.locked;
};
TO
vm.setlock = function() {
vm.locked = !vm.locked;
};
I have a directive with isolate scope which takes a scope variable by reference
angular.module('myApp')
.directive('myDirective', function() {
return {
scope: {
items: '='
},
templateUrl: 'template.html',
replace: true,
controller: 'myDirectiveCtrl',
controllerAs: 'ctrl'
};
})
.controller('myDirectiveCtrl', function($scope) {
this.items = $scope.items;
});
This is passed in like so:
<div my-directive items='items'></div>
In the external controller data is asynchronously loaded and the scope items passed into the directive updated:
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.setItems = function() {
$scope.items = [
'Here',
'There',
'Everywhere'
];
};
});
When the data is loaded, the scope outside my directive updates, but inside it doesn't
My html:
<div my-directive items='items'></div> <!-- this doesn't update -->
Outside directive
<ul ng-repeat='i in items'> <!-- this does update -->
<li>{{i}}</lu>
</ul>
<button ng-click="setItems()">Set items</button>
How can I get my scope inside my directive to update? Do I
Plunker here
When Angular first runs your directive's controller function, your $scope.items === undefined, so when you do this.items = $scope.items, your this.items === undefined too.
That's it. After that there is nothing that changes this.items.
This is unlike $scope.items. $scope.items is two-way bound to the outer scope, so whenever Angular detects a change externally, it sets the isolated scope variable.
The easiest way (and most suitable, in my opinion) is to use the $scope property directly in the directive:
<div>
Inside directive
<ul ng-repeat="i in items">
<li>{{ i }}</li>
</ul>
</div>
If you want to use your controller as ViewModel instead of scope (I don't know why you would), you could do:
$scope.$watchCollection("items", function(newVal, oldVal) {
ctrl.items = newVal;
});
EDIT:
In Angular 1.3 you can also do bindToController: true in the directive's definition, so that the controller property "items" will get the two-way binding that $scope.items gets. Then, you don't even need to do this.items = $scope.items;:
Your forked plunker to illustrate.
If it is isolated scope you cannot change what is inside the directive after you create a separate variable inside the directive controller.
Here is the updated plunker which removes the controller for the directive.
'use strict';
angular.module('myApp')
.directive('myDirective', function() {
return {
scope: {
items: '='
},
templateUrl: 'template.html',
replace: true
};
});
Try putting your items in an object. See this example at Plunker
index.html
<div my-directive items='allItems'></div>
Outside directive
<ul ng-repeat='i in allItems.items'>
<li>{{i}}</lu>
</ul>
<button ng-click="setItems()">Set items</button>
</div>
directive.js:
'use strict';
angular.module('myApp')
.directive('myDirective', function() {
return {
scope: {
items: '='
},
templateUrl: 'template.html',
replace: true,
controller: 'myDirectiveCtrl',
controllerAs: 'ctrl'
};
})
.controller('myDirectiveCtrl', function($scope) {
this.items = $scope.items;
});
template.html:
<div>
Inside directive
<ul ng-repeat="i in ctrl.items.items">
<li>{{ i }}</li>
</ul
</div>
script.js:
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.allItems={};
$scope.setItems = function() {
$scope.allItems.items = [
'Here',
'There',
'Everywhere'
];
};
});
There is a better explanation here:
Angular - ngModel not updating when called inside ngInclude
I would like the ng-click to change the value of the controller scope variable 'controllerLabel'. What's the best way of achieving this without using a controller scope function?
HTML:
<div ng-app="app">
<div ng-controller="Ctrl">
<p>{{controllerLabel}}</p>
<my-template></my-template>
</div>
<!-- my-template.html -->
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="controllerLabel = {{clickLabel.text}}">{{clickLabel.text}}</label>
</div>
</script>
</div>
JavaScript:
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
JSFiddle
You can add link to directive and write like:
.directive('myTemplate', function() {
return {
restrict: 'E',
link: function (scope) {
scope.onClick = function (clickLabel) {
scope.controllerLabel = clickLabel.text;
}
},
templateUrl: 'my-template.html'
};
});
HTML
<script type="text/ng-template" id="my-template.html">
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="onClick(clickLabel)">{{clickLabel.text}}</label>
</div>
</script>
Actually you can write like #Alborz posted but I think to add method into link and call from HTML will be clearer and easy to debug.
Demo Fiddle
I updated your fiddle;
Updated fiddle
You need to use controllerLabel as an object property to have a shared object with controller.
angular.module('app', [])
.controller('Ctrl', function Ctrl1($scope) {
$scope.label = {};
$scope.label.controllerLabel = 'Default text';
$scope.clickLabels = [
{'text':'Hello'},
{'text':'World'},
];
})
.directive('myTemplate', function() {
return {
restrict: 'E',
templateUrl: 'my-template.html'
};
});
Template:
Note to label.controllerLabel = clickLabel.text
<div ng-repeat="clickLabel in clickLabels">
<label ng-click="label.controllerLabel = clickLabel.text">{{clickLabel.text}}</label>
</div>