event.preventDefault() on $locationChangeStart doesn't work as expected - angularjs

I have a strange problem with calling the e.preventDefault inside handler for a $locationChangeStart event. I have something like this:
var unregisterCallback = _this.$rootScope.$on('$locationChangeStart', function (e) {
e.preventDefault();
});
This is happens in a link function for one of the directivies. The problem is that when I click an anchor element with some path, it actually navigates to it and then back.
I'm using component router from Angular 1.5. This pretty much makes this usless, as I'm trying to show a confirmation dialog before user nagivates away without saving changes. The thing is, due to this re-navigation he looses all changes anyways. Any idea what's going on?

In the code you are trying to prevent the default event in the $locationChangeStart, but you don't want to navigate to a new page on click of a anchor() tag. In that case you can remove the href ="#" in the anchor tag and follow the below method
Link
Another method to prevent the default action for all the anchor tags you can have directive to do so
app.directive('a', function() {
return {
restrict: 'E', // restricts to html elment
link: function(scope, elm, attrs) {
if(attrs.ngClick || attrs.href === '' || attrs.href === '#'){
elm.on('click', function(ev){
ev.preventDefault(); // prevents the default functionality of the tag
});
}
}
};
});

Related

How to hook to "OnOpen" listener for uib-popover?

Context: I am using angular 1 and this UIB Popover control.
Since there is a text field in the popover template I called, my target is to focus on that text field whenever the popover is opened.
Unfortunately, there is no popover listener/event for "onOpen".
So I tried to do a
scope.$watch(()=>{return scope.isOpen}, (obj) ={
// where scope.isOpen is the local var in the popover-is-open
// expecting to write some code here to manipulate the element
// to realise the focus operation
// but there is no popover element yet when this is called
})
I was just wondering what other options I might have?
Thanks
I found nothing on the documentation talked about events and found this issue on the ui-bootstrap github stating that they do not support events nor do they ever plan to implement them. https://github.com/angular-ui/bootstrap/issues/5060
If you're looking for a different option that would give you access to the events would be to implement your own popover directive that simply wraps bootstrap popovers. In theory, they can function the same as the ui-bootstrap and allows you to tap directly into the events provided by bootstrap.
HTML
<div my-popover="Hello World" popover-title="Title" popover-shown="myCallback()">...</div>
JavaScript ('my-popover.directive.js')
angular
.module('myModule')
.directive('myPopover', myPopover);
function myPopover() {
return {
scope: {
popoverTitle: '#',
popoverShown: '&'
},
restrict: 'A',
link: function(scope, elem, attr) {
$(elem).popover({
title: scope.popoverTitle,
content: attr.myPopover
});
$(elem).on('shown.bs.popover', function () {
if(scope.popoverShown && typeof scope.popoverShown === 'function'){
scope.popoverShown();
}
});
}
};
}
Similar to uib-popover, you can add support for additional configurations by adding additional scoped properties.

Get Rendered Form into $StateChangeStart

Is there any way that I can get the rendered form into
$rootScope.on("$stateChangeStart", function (){
})
I tried two things.
First: Using $template Request I got the template using templateURL and compiled that but it renders predefined template not the DOM's rendered.
See the code
if (fromState.name.length > 0) {
$templateRequest(fromState.templateUrl)
.then(function (html) {
var compiledElement = $compile(html)($rootScope);
var compliedForm = compiledElement.find('form');
}
}
then Secondly, I tried using
angular.element('document').find('form');
But it gives me list of attribute and all. But how to get check form is valid or not.
Document
I think what you are trying to achieve, is to block a state change when a form in the current view is not valid. I would make a directive for this, something like:
app.directive("formValidStateCheck", function($rootScope) {
return {
restrict: "A",
require: "ngForm",
link: function(scope, element, attrs, ngFormCtrl) {
$rootScope.$on("$stateChangeStart", function(event) {
if (ngFormCtrl.$invalid) {
// prevent routing
if (!confirm("Are you sure"))
event.preventDefault();
}
}
});
}
}
});
Than put the directive on your forms:
<form ng-form="myForm" form-valid-state-check>
</form>
.find() method will not work with selectors and tag names. you need to get it by form id(for this have a id to the form).
Then use angular.element(document.getElementById("#form_id"));

Raise anchor ng-click on enter keypress for accessibility in angular

I am working on a problem for accessibility issues in my project. One scenario I am struggling is, we have anchor tag with ng-click event. When someone is hitting tab and reached anchor tag on the page using keyboard, they are hitting enter key, and they think it should do something. But, unfortunately enter key and click events works well with button control but not with anchor. Anyone has any suggestions how to solve this problem?
thanks in advance.
I faced a similar issue, and resolved it be creating a simple directive which would intercept the keydown/keypress events on a link, and then call a function associated with the directive.
Here is the directive:
// Allows for the interception of the enter key, and then calling a passed in function
(function () {
'use strict';
angular
.module('app.directives')
.directive('convEnter', convEnter);
function convEnter() {
var directive = {
restrict: 'A',
link: link
};
return directive;
////////////////////////////////////////////////////////////////////////
function link(scope, element, attrs) {
element.bind('keydown keypress', function (event) {
if (event.which === 13) {
scope.$apply(function () {
scope.$eval(attrs.convEnter, { 'event': event });
});
event.preventDefault();
}
});
}
}
})();
To use it, you would simply apply it to your link, ie:
<a conv-enter="vm.click()" ng-click="vm.click()">Click Link</a>
Of course this is a bit repetitive as you are specifying "vm.click()" twice; you could optimize this if desired by having the directive simply call the "click" function on the element itself, as long as you always wanted the click and the enter key to invoke the same function. (In my scenario, I sometimes needed to distinguish between the two, and hence i allowed passing in a separate function)

How to use a different combination of triggers for uib-popover?

The official documentation at :https://angular-ui.github.io/bootstrap/#/popover says that the following trigger combos can be passed as param to the popover-trigger attribute :
mouseenter: mouseleave
click: click
outsideClick: outsideClick
focus: blur
none
I want to use a combination of
mouseenter: outsideClick
How to achieve this without using the popover-is-open attribute?
You can't, the docs state
The outsideClick trigger will cause the popover to toggle on click, and hide when anything else is clicked.
"anything else" includes the element itself, so toggeling the element using outsideClick on or off and will interfere with the natural behavior of other triggers.
for example if state your triggers like so popover-trigger="mouseleave outsideClick"
, the trigger mouseleave will hide the popover instead of showing it if you have already clicked the element, otherwise it will just show it on leave. (plunk).
If you can hack it using popover-is-open then continue doing so, if it bothers you too much you can always request a feature.
popover-trigger="mouseenter outsideClick" for the uib-popover directive does not seem to work as one would think.
Initially, I thought it meant the following:
On mouse enter show the popover
On mouse leave hide the popover
On click keep popover open in an active state
On outside click close popover if it is in an active state
Since it does not I needed a manual approach, the following is stated in the documentation.
For any non-supported value, the trigger will be used to both show and hide the popover. Using the 'none' trigger will disable the internal trigger(s), one can then use the popover-is-open attribute exclusively to show and hide the popover.
So I created some HTML like:
<span class="glyphicon glyphicon-info-sign"
ng-class="{'text-primary' : isInfoPopoverClicked}"
ng-click="toggleInfoPopoverClicked()"
ng-mouseenter="enterInfoPopover()"
ng-mouseleave="leaveInfoPopover()"
custom-click-outside="closeInfoPopover()"
uib-popover-template="'info.html'"
popover-trigger="'none'"
popover-is-open="isInfoPopoverOpen()"
popover-placement="auto top"
popover-append-to-body="true" >
</span>
The JS in the controller:
// Toggle popover's clicked active state
$scope.toggleInfoPopoverClicked = function() {
$scope.isInfoPopoverClicked = !$scope.isInfoPopoverClicked;
};
// Close the popover, used for outside click and close action inside the template
$scope.closeInfoPopover = function() {
delete $scope.isInfoPopoverClicked;
};
// On mouse enter, show the popover
$scope.enterInfoPopover = function() {
$scope.isInfoPopoverMouseEnter = true;
};
// On mouse leave, close the popover.
// If clicked active state is false set to undefined.
// This supports when the user clicks the icon to close,
// that mouse enter does not immediately display the popover again.
$scope.leaveInfoPopover = function() {
$scope.isInfoPopoverMouseEnter = false;
if(false === $scope.isInfoPopoverClicked) {
delete $scope.isInfoPopoverClicked;
}
};
// Expression used in the popover-is-open attribute
$scope.isInfoPopoverOpen = function() {
if($scope.isInfoPopoverClicked) {
return true;
} else if(false === $scope.isInfoPopoverClicked){
return false;
}
return $scope.isInfoPopoverMouseEnter;
};
The template for the uib-popover-template I used:
<div custom-stop-event="click" class="pull-right">
<span ng-click="closeInfoPopover()" class="glyphicon glyphicon-remove"></span>
<section>{{info}}</section>
</div>
Now the trickier part was that this solution required me to create two more directives.
One to close the popover when clicking outside the element.
Another to stop the click event fired inside the pop-up. Preventing it from closing the popover.
The custom-click-outside directive:
angular.module('LSPApp').directive('customClickOutside', ['$document', function ($document) {
return {
restrict: 'A',
scope: {
clickOutside: '&customClickOutside'
},
link: function (scope, element) {
var handler = function (event) {
if (element !== event.target && !element[0].contains(event.target)) {
scope.$applyAsync(function () {
scope.clickOutside();
});
}
};
// Might not work on elements that stop events from bubbling up
$document.on('click', handler);
// Clean up event so it does not keep firing after leaving scope
scope.$on('$destroy', function() {
$document.off('click', handler);
});
}
};
}]);
The custom-stop-event directive called from the template's HTML:
angular.module('LSPApp').directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.on(attr.stopEvent, function (e) {
e.stopPropagation();
});
}
};
});
Hopefully, this helps someone, my final solution had all this encapsulated in it's own directive to promote reuse.

Dynamically change the action of href using angularjs

I'm a newbie to angular, and I'm playing around with it to try and understand how things work. I have an href as part of the template of a directive and an action associated with clicking the link. I would like to know how I can change the action when the user clicks on the link. I tried using a link function in my template, but I couldn't even get it to fire a message to the console.
Here is my link function:
var linkFunction = function(scope) {
scope.$watch(scope.loggedin, function() {
console.log('Here');
});
};
Any pointers? Or is there a better way.
TIA
Link function is part of directive. You can use an ng-click directive in the anchor tag in the template and provide its implementation in the linking function of the directive.
//template
Click Me
//Link function in directive
function(scope) {
scope.doThis = function() {
console.log("doing this);
}
}

Resources