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;
};
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() {};
TL;DR I solved my problem. Here is plunker with 3 different solutions:
http://plnkr.co/edit/E0ErKs?p=preview
I don`t like slider1 because it stores value in $scope ( {{sliderValue}} ) and according to recommendation from Angular Style Guide we should avoid that.
I don`t like slider2 because it assumes that controler have alias vm in a view (so we create some kind of coupling between view and directive).
Solution 3 looks OK for me. Am I missing something?
How would you write differently this directive to be in complience with Angular philosophy?
INITIAL QUESTION:
I am learning angular and not everything is clear to me yet.
I found this question:
How to use jQuery in AngularJS
So I created working example:
Directive:
(function() {
'use strict';
angular.module('demoApp').directive('slider', function () {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.onSlide = function (e, ui) {
$scope.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.sliderValue,
slide: scope.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
});
})();
Controller:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ '$scope' ];
function DemoAppTestCtrl($scope) {
$scope.sliderValue = 10;
}
})();
And Html page:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{sliderValue}}
<div slider></div>
</div>
Everything works fine. Angular put slider in place of <div slider> and I can move it and I see changing values in {{sliderValue}}.
Then I found this Angular Style Guide
https://github.com/johnpapa/angular-styleguide
In chapter about controllers they recommend to use controllerAs with vm syntax (because $scope is bad or something).
Ex:
function CustomerController() {
var vm = this;
vm.name = {};
vm.sendMessage = function() { };
}
So I changed my controller to this:
(function() {
'use strict';
angular.module('demoApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
DemoAppTestCtrl.$inject = [ ];
function DemoAppTestCtrl($scope) {
var vm = this;
vm.sliderValue = 10;
}
})();
And Html page to:
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
But i don`t know how to fix my directive.
I want the same functionality, when i move the slider i want to set vm.sliderValue inside controler instead $scope.sliderValue inside scope.
EDIT1:
I was able to make it work by adding $scope.vm inside controller and link functions (because my controller sits in scope as vm). But I am not sure if this is right way to do it, because now my directive assume that there is controller in scope under $scope.vm alias.
Is this bad design or normal way of doing things in Angular ?
(function () {
'use strict';
angular
.module('demoApp')
.directive('slider', slider);
slider.$inject = [ ];
function slider() {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
$scope.vm.onSlide = function (e, ui) {
$scope.vm.sliderValue = ui.value;
$scope.$digest();
};
},
link: function (scope, el, attrs) {
var options = {
value: scope.vm.sliderValue,
slide: scope.vm.onSlide
};
// set up slider on load
angular.element(document).ready(function () {
scope.$slider = $(el).slider(options);
});
}
}
}
})();
EDIT2:
I was able to create working Plunker with 3 different versions:
http://plnkr.co/edit/E0ErKs?p=preview
Use scope: false as a option in the directive.
http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/
try something like this:
angular.module('myApp', []);
angular.module('myApp').controller('DemoAppTestCtrl', DemoAppTestCtrl);
function DemoAppTestCtrl() {
var vm = this;
vm.sliderValue = 10;
}
angular.module('myApp').directive('slider', sliderDirective );
function sliderDirective() {
return {
restrict: 'A',
controller: sliderController,
controllerAs: 'sliderCtrl',
template: "<p>{{sliderCtrl.test}}</p>"
}
}
function sliderController() {
var vm = this;
vm.test = "hello";
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="DemoAppTestCtrl as vm">
Value: {{vm.sliderValue}}
<div slider></div>
</div>
</div>
I'm using Angular 1.4.1 and UI Bootstrap 0.13.
I have a directive from which I'm opening a modal. The modal opens fine, but the buttons seemingly don't get bound to their handlers - they don't do anything. I've used this same code in another project just fine, except not from within a directive.
My directive:
(function () {
var app = angular.module('myApp');
app.directive('someDirective', function () {
return {
restrict: 'E',
templateUrl: 'SomeDirective.html',
scope: {
list1: '=list1',
list2: '=list2',
save: '&'
},
controller: ['$scope','$modal','myService', function ($scope,$modal,myService) {
$scope.openModal = function () {
var modalInstance = $modal.open({
templateUrl: 'ModalTemplate.html',
controller: 'modalController',
backdrop: 'static',
size: 'sm',
resolve: {
saveData: function () {
//do save action
}
}
});
modalInstance.result.then(
function (itemToSave) {
//save item
},
function () {
//Cancel
}
);
};
}]
}
});
}());
The modal's controller:
(function() {
var app = angular.module('myApp');
app.controller('modalController', [
'$scope', '$modalInstance', 'saveData',
function($scope, $modalInstance, saveData) {
$scope.saveData = saveData;
$scope.save = function() {
$modalInstance.close($scope.saveData);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
]);
}());
And the template for the modal content:
<div class="modal-header bg-info">
<h3 class="modal-title">Add New Record</h3>
</div>
<div class="modal-body">
<form class="form-horizontal" role="form"></form>
</div>
<div class="modal-footer bg-info">
<button class="btn btn-primary" ng-click="save()">Save</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
My thought is that I'm having issues with scope, but I can't track it down. I have put break points on modalController. The app.controller() call happens when the app loads, I've seen that. But breakpoints within save() and cancel() never get hit.
Can someone help me figure out why the modal's buttons don't do anything?
This turned out to be a stupid mistake. At some point I apparently overwrote the name of one of the other controllers in my project with the same name of the controller I was using for the modal. The other controller did not have save() or cancel() methods so nothing was happening. As soon as I fixed my error and all controllers once again had their proper names this started working again.
I'm new to Angular and attempting to implement this solution into my project.
It looks painfully easy, however, I'm trying to make this into a re-usable element so that I can call it from anywhere and just pass in the content to be shown (otherwise, what's the point?).
So, my specific question is: assuming I already have a controller that's bound to some DOM element and it has a feature that goes and fetches some factory driven $http call and upon the response I wish to notify the user via this dialog of something, how do I combine *this directive and *this controller with my existing one and how do I do it in a way that allows me to then use it again from a totally different controller?
Or is this perhaps a bad example for this use and should I be looking at a different one?
Compared to other options, below given the minimalist approach, using angular factory. See a sample snippet below.
Note: using Angular JS with UI Bootstrap - AngularUI.
Reusable modal view - ConfirmationBox.html
<div class="modal-header">
<h3 class="modal-title">{{title}}</h3>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-warn" data-ng-click="ok(); $event.stopPropagation()">OK</button>
<button type="button" class="btn btn-default" data-ng-click="cancel(); $event.stopPropagation()">Cancel</button>
</div>
Reusable module and shared factory, for handling the reusable modal dialog
angular.module('sharedmodule',['ui.bootstrap', 'ui.bootstrap.tpls'])
.factory("sharedService",["$q", "$modal", function ($q, $modal)
{
var _showConfirmDialog = function (title, message)
{
var defer = $q.defer();
var modalInstance = $modal.open({
animation: true,
size: "sm",
templateUrl: 'ConfirmationBox.html',
controller: function ($scope, $modalInstance)
{
$scope.title = title;
$scope.message = message;
$scope.ok = function ()
{
modalInstance.close();
defer.resolve();
};
$scope.cancel = function ()
{
$modalInstance.dismiss();
defer.reject();
};
}
});
return defer.promise;
}
return {
showConfirmDialog: _showConfirmDialog
};
}]);
Portion of your View, using the shared modal dialog
<a data-ng-click="showConfirm()">Go Back to previous page</a>
Controller of your view, opening your shared reusable modal dialog and handling notifications (Ok and Cancel)
var myModule = angular.module("mymodule", ['sharedmodule', 'ui.bootstrap', 'ui.bootstrap.tpls']);
myModule.controller('myController', ["$scope", "sharedService", "$window",
function ($scope, sharedService, $window)
{
$scope.showConfirm = function ()
{
sharedService.showConfirmDialog(
'Confirm!',
'Any unsaved edit will be discarded. Are you sure to navigate back?')
.then(function ()
{
$window.location = '#/';
},
function ()
{
});
};
}]);
Trying using 'ngDialog' library for popup and modal. Very good library. Later you can create a service which internally calls ngDialog functions. Later this service can be injected in your controllers for use. I have implemented this in one project.
The function in services can accept parameters for initialising the ngDialog modal.
Hope it helps :)
for making it better I would suggest you to modify the code to look something as below
Template:
<div class='ng-modal' ng-show='modalContent != null && modalContent != ""'>
<div class='ng-modal-overlay' ng-click='hideModal()'></div>
<div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-close' ng-click='hideModal()'>X</div>
<div class='ng-modal-dialog-content' ng-transclude></div>
<p>{{ modalContent }}</p>
</div>
</div>
Directive:
app.directive('modalDialog', function() {
return {
restrict: 'E',
scope: {
modalContent: '='
},
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
scope.hideModal = function() {
scope.modalContent= null;
};
},
template: '...' // See below
};
});
and then use the code as below in template
<modal-dialog modal-content='modalMsg' width='750px' height='90%'></modal-dialog>
Once these changes are done you can write a function to set message in variable "modalMsg" and angular will take care of rest
Note: Code is pretty much the same as in the link, the only thing I have changed is the check to display the modal box
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 :)