How to bind events in angular directive - angularjs

I am new to angular and having problem to bind an click event to my directive.
I have an ul-list with a links in each li. When I click a link I want to do a service call that adds or removes the clicked items ID and refresh the list.
When refreshed, each list items will show if an id is "marked" or not.
Can anyone help me?
html view:
<a href="#" class="showbooklist" qtip="12568">
<img src="image.png">
</a>
Directive:
listControllers.directive('qtip', ['boklistorservice', function (boklistorservice) {
return {
restrict: 'A',
controller: ["$scope", "$attrs","$element", "boklistorservice", function ($scope, $attrs,$element, boklistorservice) {
boklistorservice.getdata().then(function (data) { //serice call to gett data
$scope.booklist = data;
$element.qtip({ //use the jquery.tip2.js tooltip plugin
content: {
text: getcurrentbooklist($attrs.qtip, data.barnenskrypin.booklistor)
},
position: {
my: 'bottom center',
at: 'top center'
},
hide: {
fixed: true,
delay: 300
}
});
})
}]
};
}]);
//function returns a string. An ul to show in the toolbox
var getcurrentbooklist = function (bookid, arr) {
var rettext = "<ul>";
$.each(arr, function (item, val) {
item;
var inlist = false;
$.each(val.bookitems, function (i, v) {
if (v.bookid == bookid) {
inlist = true;
return false;
} else {
inlist = false;
}
});
if (inlist) {
rettext += "<li><a (NEED A CLICK EVENT HERE and pass bookid) > " + val.booklistnamn + "-- MARK </a></li>";
} else {
rettext += "<li><a (NEED A CLICK EVENT HERE and pass bookid) >" + val.booklistnamn + "--</a></li>";
}
});
rettext += "</ul>";
return rettext;
};

Use the link function of a directive (code untested):
listControllers.directive('qtip', ['boklistorservice', function (boklistorservice) {
return {
restrict: 'A',
link: function ($scope, element, attrs) {
element.bind('click', function () {
//Do your work here
});
},
controller: ["$scope", "$attrs","$element", "boklistorservice", function ($scope, $attrs,$element, boklistorservice) {
boklistorservice.getdata().then(function (data) { //serice call to gett data
$scope.booklist = data;
$element.qtip({ //use the jquery.tip2.js tooltip plugin
content: {
text: getcurrentbooklist($attrs.qtip, data.barnenskrypin.booklistor)
},
position: {
my: 'bottom center',
at: 'top center'
},
hide: {
fixed: true,
delay: 300
}
});
})
}]
};
}]);

jquery.tip2 adds the html to body so you need to find the content in body and add the click event
$element.qtip();
angular.element(document.body).find('[qtip container id or class or element] a').bind('click', function () {
//Do your work here
});

Related

how to close pop up screen when click out side in screen

I make one pop up screen I open pop up screen on button screen .I want to hide pop up screen when I click outside the screen can is this possible ?
code
http://codepen.io/anon/pen/KpLgpm
angular.module('ionic.example', ['ionic'])
.controller('PopupCtrl', function($scope, $timeout, $q, $ionicPopup) {
$scope.showPopup = function() {
$scope.data = {}
$scope.setDefault = function () {
console.log('Default set', arguments);
$scope.$onClose({ test: 'hello' });
};
$scope.btns = [
{
label: "Hi",
value: "hi"
},
{
label: "Hello",
value: "hello"
}
];
$ionicPopup.show({
template: '',
title: 'Pick a default value',
scope: $scope,
buttons: [
{
text: 'Awesome',
onTap: function(e) { return 'awesome'; }
},
{ text: 'Cool', onTap: function(e) { return 'cool'; } },
{ text: 'Cooler', onTap: function(e) { return 'cooler'; } },
{ text: 'Stuff', onTap: function(e) { return 'stuff'; } }
]
}).then(function(res) {
console.log('Tapped!', res);
}, function(err) {
console.log('Err:', err);
}, function(msg) {
console.log('message:', msg);
});
};
});
angular.element( $window ).on( "click", function( event ) {
if( angular.element( "#popdiv" ).has( event.srcElement || event.target ).length === 0 ) {
// hide popup
}} );
I've found a solution, called "ionic-close-popup".
Install this in your project, add in your index.html, inject in the controller, and register the popup:
https://libraries.io/bower/ionic-close-popup
You can create a directive with below code
angular.module("testapp").directive('clickAnywhereButHere', ["$document", function ($document) {
//click-any-where-but-here
return {
restrict: 'A',
link: function (scope, elem, attr, ctrl) {
var elemClickHandler = function (e) {
e.stopPropagation();
};
var docClickHandler = function () {
scope.$apply(attr.clickAnywhereButHere);
};
elem.on('click', elemClickHandler);
$document.on('click', docClickHandler);
// teardown the event handlers when the scope is destroyed.
scope.$on('$destroy', function () {
elem.off('click', elemClickHandler);
$document.off('click', docClickHandler);
});
}
};
Then in your html element
<div class="mypopup" click-anywhere-but-here="function(){ alert('click out popup event')}"></div>

Trigger directive on ng-click

I using elastic directive for resizing textarea from this answer.
But i using ng-show for textarea, and on click height of textarea is 0.
So i need to use $watch somehow to trigger directive on click, but don't know how.
Html:
<textarea ng-show="showOnClick" elastic ng-model="someProperty"></textarea>
<a ng-click="showOnClick = true"> Show text area </a>
Directive:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
link: function($scope, element) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);
Here is JSFIDDLE
as requested, the solution is to $watch the ngShow attr and run some sort of init function when the value is true.
user produced jsfiddle
example code:
.directive('elastic', [
'$timeout',
function($timeout) {
return {
restrict: 'A',
scope: {
ngShow: "="
},
link: function($scope, element, attr) {
$scope.initialHeight = $scope.initialHeight || element[0].style.height;
var resize = function() {
element[0].style.height = $scope.initialHeight;
element[0].style.height = "" + element[0].scrollHeight + "px";
};
if (attr.hasOwnProperty("ngShow")) {
function ngShow() {
if ($scope.ngShow === true) {
$timeout(resize, 0);
}
}
$scope.$watch("ngShow", ngShow);
setTimeout(ngShow, 0);
}
element.on("input change", resize);
$timeout(resize, 0);
}
};
}
]);

Angular how to correctly destroy directive

I have a 'regionMap' directive that includes methods for rendering and destroying the map. The map is rendered inside of a modal and upon clicking the modal close button the 'regionMap' destroy method is called, which should remove the element and scope from the page. However, when returning to the modal page, that includes the 'region-map' element, the previous 'region-map' element is not removed, resulting in multiple maps being displayed. What is the correct way to remove the regionMap directive from the page when the modal is closed?
// directive
(function(){
'use strict';
angular.module('homeModule')
.directive('regionMap', regionMap);
function regionMap() {
var directive = {
restrict: 'E',
template: '',
replace: true,
link: link,
scope: {
regionItem: '=',
accessor: '='
}
}
return directive;
function link(scope, el, attrs, controller) {
if (scope.accessor) {
scope.accessor.renderMap = function(selectedRegion) {
var paper = Raphael(el[0], 665, 245);
paper.setViewBox(0, 0, 1100, 350, false);
paper.setStart();
for (var country in worldmap.shapes) {
paper.path(worldmap.shapes[country]).attr({
"font-size": 12,
"font-weight": "bold",
title: worldmap.names[country],
stroke: "none",
fill: '#EBE9E9',
"stroke-opacity": 1
}).data({'regionId': country});
}
paper.forEach(function(el) {
if (el.data('regionId') != selectedRegion.name) {
el.stop().attr({fill: '#ebe9e9'});
} else {
el.stop().attr({fill: '#06767e'});
}
});
}
scope.accessor.destroyMap = function() {
scope.$destroy();
el.remove();
}
}
}
}
})();
// controller template:
<region-map accessor="modalvm.accessor" region-item="modalvm.sregion"></region-map>
// controller:
vm.accessor = {};
...
function showMap() {
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap) {
vm.accessor.renderMap(vm.sregion);
}
});
function closeMap() {
if (vm.accessor.destroyMap) {
vm.accessor.destroyMap();
}
$modalInstance.dismiss('cancel');
}
The issue is related to loading a template with a directive inside of it. Fixed it by adding a var to check if the map has previously been rendered:
vm.accessor.mapRendered = false;
$rootScope.$on('$includeContentLoaded', function(event) {
if (vm.accessor.renderMap && !vm.accessor.mapRendered) {
vm.accessor.renderMap(vm.selectedRegions);
vm.accessor.mapRendered = true;
}
});

How to get ng-model value from directive to controller in Angular?

I created a custom directive in which I have given text box and I want to take that model value in my controller.
I tried to find it, but didn't success.
here is my directive code
app.directive("bhAddCategory", ["$rootScope", "$timeout", "CategoryFactory", "ArticleFactory", "RecentArticleFactory", "focus", function ($rootScope, $timeout, CategoryFactory, ArticleFactory, RecentArticleFactory, focus) {
return {
scope: {
display: '=bhCategoryToggle',
imageOverflow: '=bhImageOverflow',
textBoxCss: '#bhTextBoxCss',
rmText: '=bhRmText'
},
replace: true,
template: '<div>' +
'<div class="pull-left forDrop"><input type="text" focus-on="focusMe" ng-class="myColonyList" class="effect1" placeholder="Add a colony" data-ng-model="newCategoryName" data-ng-trim="true" ng-keypress="pressEnter($event)"></div>' +
'<div class="pull-right"><img src="/images/greyplus.png" ng-class="{imageoverflow: imageOverflow}" ng-show="loadplus" data-ng-click="addCategory()" alt="add category"><img src="/images/loader.gif" ng-class="{imageoverflow: imageOverflow}" alt="" ng-show="loadgif" class="colonyloder"></div >' +
'</div>',
link: function (scope, element, attrs) {
scope.loadplus = true;
scope.pressEnter = function (keyEvent) {
if (keyEvent.which === 13)
scope.addCategory();
};
scope.resetNewCategoryName = function () {
if (scope.rmText) {
scope.newCategoryName = '';
}
};
scope.addCategory = function () {
scope.display = false;
var addNewCat = scope.newCategoryName;
commonNotification($rootScope, true, false, '', '');
var categoryData = {
category_name: addNewCat,
category_type: 0
};
if (addNewCat !== undefined && addNewCat !== '') {
scope.loadgif = true;
var category_details = CategoryFactory.nameExists(addNewCat);
if (!category_details.exist) {
CategoryFactory.addAtPostion(categoryData, category_details.mid)
.then(function (category) {
scope.loadgif = false;
scope.resetNewCategoryName();
commonNotification($rootScope, false, true, true, category.success);
$timeout(function () {
$rootScope.newStatus = false;
}, 3000);
}, function (error) {
commonNotification($rootScope, false, true, true, error.message);
$timeout(function () {
$rootScope.newStatus = false;
}, 3000);
});
} else {
scope.loadgif = false;
commonNotification($rootScope, false, true, true, 'Category name already exists!');
$timeout(function () {
$rootScope.newStatus = false;
}, 2000);
}
} else {
commonNotification($rootScope, false, true, true, 'Category name is required!');
$timeout(function () {
$rootScope.newStatus = false;
}, 2000);
}
};
}
};
}]);
And here is my controller
app.controller('bookmarkCtrl', ["$scope", "$http", "$rootScope", "$timeout", "RecentArticleFactory", "CategoryFactory", "ArticleFactory", "focus", "debounce", "userFactory", function ($scope, $http, $rootScope, $timeout, RecentArticleFactory, CategoryFactory, ArticleFactory, focus, debounce, userFactory) {
}]);
Can I get any working demo, so that I can understand and implement in my code
Thanks in Advance
Use a parent scope excecution parameter on your directive.This is a scope function which is declared on your parent controller and is invoked from within your directive whenever you want to pass something to your controller.
To accomplish this we declare a scope parameter in our directive with the '&' prefix
See this example which i created for another answer
http://jsfiddle.net/jwd3gywz/26/
JS
angular.module('components', []).controller('Composer', function Composer($scope, $http) {
// adding snippet to composed text
$scope.composed_text = '';
$scope.updateCaretPosition=function(pos){
$scope.caret_position=pos;
console.log('called'+pos);
}
$scope.$watch('caret_position',function(){
console.log($scope.caret_position);
})
}).directive('caretPosition', function() {
return {
scope:{updateCaretPosition:'&'},
link: function(scope, element, attrs) {
element.bind('keyup click', function(e){
var caret_position = element[0].selectionStart;
scope.updateCaretPosition({pos:caret_position});
console.log('my current position: ' + caret_position);
});
}
}
});
angular.module('myApp', ['components'])
HTML
<!doctype html>
<html ng-app="myApp">
<body>
<div ng-controller="Composer">
<textarea class="form-control composed_text" ng-model="composed_text" update-caret-position="updateCaretPosition(pos)" caret-position="" rows="20"></textarea>
</div>
</body>
</html>

Directive not updateing (re-render) with data change

I can't get my angularJS module to update when the provider keeping the data is updated.
The console.logs are being written out, but html doesn't update.
Directive:
.directive('logBar', ['loggingService', function(loggingService) {
var template = '<div class="floatLeft" ><div class="arrow"></div></div>' +
'<ul class="floatLeft">' +
'<li class="logHolder" tabindex="-1" ng-repeat="log in logs | reverse">' +
'<label class="label label-{{log.type}}" ng-click="alert(log.message);" tabindex="-1">{{log.date | date:"HH:mm, dd.MMM" }} - {{log.message}}</label>' +
'</li>' +
'</ul>';
return {
restrict: 'E',
template: template,
scope: {},
link: function($scope, $elem, $attrs) {
$scope.$watch(function() {
console.log("logs updating");
$scope.logs = loggingService.getLogs();
console.log($scope.logs);
});
}
};
}]);
provider:
provider('loggingService', function() {
var logs = [{"date": Date.now(), "origin": "Logbar", "message": "Welcome", "type": "info"}];
var maxLogs = 15;
var types = "default,primary,success,info,warning,danger";
this.resetLog = function() {
logs = [];
};
this.Removelog = function(message, type, alert) {
};
this.$get = function() {
return {
logs: logs,
getLogs: function() {
return logs;
},
log: function(message, type, alert) {
if (types.indexOf(type) === -1) {
if (type !== "error") {
type = "default";
} else {
type = "danger";
}
}
logs.push({"message": message, "type": type, "date": Date.now(), origin: "User"});
if (logs.length > maxLogs) {
logs.shift();
}
}
};
};
})
controller:
Works with applys, but without them, it does not. Is there anyway to insert the $apply into the provider so I don't have to write them everywhere?
$http.post("/competition/" + $scope.key, angular.toJson($scope.draws)).success(function(data) {
$scope.fixDraws();
loggingService.log("Saved", "success", false);
$scope.$apply();
}).error(function(data) {
loggingService.log("Error", "danger", true);
$scope.fixDraws();
$scope.$apply();
});
From what I read, you're trying to update your scope from the watchExpression, not from the $watch listener.
Try this:
link: function($scope, $elem, $attrs) {
$scope.$watch(
// watch expression
function() {
return loggingService.getLogs();
},
// listener
function(newVal){
$scope.logs = newVal;
}
);
}
Refer to the docs.
It was fixed by adding the $apply to the http calls. And I could not reproduce the error in another project.

Resources