override angular ui-router for links to page anchors - angularjs

I have links to anchors on my page (a href="#idea"). ui-router obviously overrides them. Is there a way to use both in harmony? Thank you.
https://gist.github.com/anonymous/2880f2f2c9f91b0b695f#file-gistfile1-txt

Well you aren't supposed to use it on a href this way, instead you should add it like the following:
...
You can see an example of this at the bottom of the $anchorScroll API here.

If you want to combine ui-router with anchor tag, I solved my problem using this code
<a ui-sref="routename({'#': 'ownerfeatures'})">Link</a>

Hi I had similar problem with anchors.
Since I want to go on using regular HTML syntax (href="#may-hash") I've made a directive which looks if a link is an anchor and if yes perform scroll operations. See detailed code :
/**
* Ressource : https://docs.angularjs.org/api/ng/service/$anchorScroll
*/
.directive('a', ['$location', '$anchorScroll', function ($location, $anchorScroll) {
return {
restrict: 'E',
link: function (scope, elem, attrs)
{
// href should be defined and not empty
if(typeof attrs.href!=='undefined' && attrs.href.length > 0)
{
// href should be an anchor
if(attrs.href.substring(0, 1) === '#')
{
elem.on('click', function (e)
{
// if current hash is different from requested hash
// then set new hash
// no need to call $anchorScroll()
// since scroll is auto if you've not used $anchorScroll.disableAutoScrolling()
if ( '#' + $location.hash() !== attrs.href)
{
$location.hash(attrs.href);
}
// if hash is the same, force scroll
else
{
$anchorScroll();
}
});
}
}
}
};
}])

Better way to do this is this way
say you are on state app.state1.child and your anchor tag is #list, so say that you a tag use this code
<a ui-sref="app.state1.child#list">Link</a>

I know is an old topic but always get stuck looking a better solution to do the anchoring in my apps.
Today I was looking to scroll in the same page and tested a lot of differents solutions. Most of the time this one is the general recommendation:
<a ui-sref="routename({'#': 'ownerfeatures'})">Link</a>
The problem with this one is works to transition to a new state and go to the anchor but doesn't work well if you want to just scroll in the same page, because it only works the first click.
Then I realize that the use of $anchorScroll should be great for this and I made this directive maybe can help someone. Note I didn't use location.hash, because I don't want to add the hash to my address bar and also there where some situations in my flow that make the screen scroll when not required.
function anchorScroll($anchorScroll){
return {
restrict: 'A',
scope: {
target: '=anchorScroll'
},
link: function ( scope, element, attrs ) {
element.bind('click', function(event) {
console.log(scope.target)
$anchorScroll(scope.target);
})
}
}
}
But the also there is a very simplest way, just using $anchorScroll in the template. In your controller:
$scope.anchorScroll = $anchorScroll;
And in the view:
Scroll to ID

Related

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)

Dynamic id inside AngularJS template

I'm wrapping a jQuery plugin inside a AngularJS directive. The way I would like to call the directive is for example:
<my-dialog data-trigger-id="myTriggerId">My dialog content...</my-dialog>
Inside my directive template it looks like this:
<button id="{{triggerId}}">Button text...</button>
I attach the event for the jQuery plugin (where you specify the trigger selector) inside the link function of my directive. My problem is that it works if I hardcode the id of the button inside the directive template like this:
<button id="myTriggerId">Button text...</button>
The generated html looks fine in the browser, which means that rendering an element with a dynamic id works. It's just that the jQuery plugin cannot find this element if I use the dynamic id but it works with the hardcoded version.
I also looked up AngularJS compile because it looks like at the point where the jQuery plugin wants to initialize the element doesn't exist yet.
Is there a gotcha I'm missing? Thanks!
Edit: I finally managed to simplify it down and create a jsfiddle example. If you run the example, you will see in the console that the element doesn't exist at the time I'm logging it but if you inspect the DOM, you will see that it's there and has the correct id.
However if you hardcode the id in the template (id=test instead of id={{elemId}}), the console log will show that one element could be found. I hope this helps to find a solution.
http://jsfiddle.net/a1nxyv8u/7/
The digest has not yet rendered in the DOM by the time you are calling you $("#test").length.
You need to add in a $timeout so that the digest will complete, then call your method
var app = angular.module('app', []);
app.directive('myDialog', ['$timeout', function ($timeout) {
return {
restrict: 'E',
template: '<button id="{{elemId}}" class="{{elemClass}}">Open dialog</button>',
link: function (scope, element, attrs) {
var selector = scope.elemSelector,
elemClass = (selector.indexOf('.') > -1) ? selector.substr(1) : '',
elemId = (selector.indexOf('#') > -1) ? selector.substr(1) : '';
scope.elemClass = elemClass;
scope.elemId = elemId;
$timeout(function() {
console.log('elem: ', $('#test').length);
});
// jQuery plugin init here but element doesn't seem to exist yet.
},
scope: {
elemSelector: '#'
}
}
}]);
Although it should be noted that you should try and alleviate any Id's at all and just use $(element) instead unless your jQuery absolutely needs the Id.

Is it possible to set ng-view to replace: true?

I'm integrating Angular into a web application and I noticed that the wrapper is messing with some of the CSS on the page. After researching into directives, I saw that custom directives can have a property called 'replace' to be set to true so that the templateUrl directly replaces instead of being wrapped in the directive's tags.
Is there a way to do the same with ng-view, or more generally any Angular directive? Thanks for the help!
I think your best bet is to decorate the original ng-view directive and add the replace:true switch to it. Note, replace is going to be - hah - replaced in the next major version of angularjs, but for now, this will work:
app.config(function($provide) {
$provide.decorator('ngViewDirective', function($delegate) {
var directive = $delegate[0];
directive.replace = true;
return $delegate;
});
});
And of course, the jsFiddle: Here
Is there a way to do the same with ng-view
I can't think of a way of doing that without editing the source of Angular or something. replace is for custom directives, not (necessarily) the built in ones.
or more generally any Angular directive? Thanks for the help!
Same thing. But you wouldn't want to do it with any directive, seeing how you could have multiple directives in a single element. I suppose you may want to do it with all template'd directives (since I think you can only have one per element), but, again, without editing the source of the directive I can't think of a way to do that.
Also, note that Angular has stated that replace "will be removed in next major release", so it's probably not best to rely on it.
You can, however, if you wish, create you own ngInclude/ngView-esque directive that renders templates without a wrapping tag.
A naive and probably problematic one (or at least inefficient) might look like:
app.directive('myRender', function ($compile, $http) {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var newScope;
var newElement;
attrs.$observe('url', function (value) {
if (value) {
$http.get(value).then(function (result) {
if (newElement !== undefined) {
newElement.remove();
}
newElement = angular.element(result.data);
if (newScope !== undefined) {
newScope.$destroy();
}
newScope = scope.$new();
$compile(newElement)(newScope);
element.parent().append(newElement);
});
}
})
}
}
});
http://plnkr.co/edit/Vm96f2rQsT2PX1C3rgK8?p=preview
My first advice would be to adapt your css to angular if you want to use angular.
My other advice would to do your entire application in angular, and not add it after the fact.

Angular navigate to a specific tab on a page

I would like to navigate to a page of my single page application using a link like /#/invoices/1?tab=2. This page contains tabs, so I would also like to send the user directly to the tab I wish through the url.
Could you provide some pointers on how to implement this in the cleanest way?
Use $anchorScroll method, passing a class name on it you can go directly to that element.
Just an update to above answer.
Few examples with custom directives
http://nadeemkhedr.com/angularjs-scroll-to-element-using-directives/
angular.module('MyApp').directive('scrollToBookmark', function() {
return {
link: function(scope, element, attrs) {
var value = attrs.scrollToBookmark;
element.click(function() {
scope.$apply(function() {
var selector = "[scroll-bookmark='"+ value +"']";
var element = $(selector);
if(element.length) {
window.scrollTo(0, element[0].offsetTop – 100);
// Don’t want the top to be the exact element, -100 will go to the top for a little bit more
}
});
});
}
};
});

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