Sajith
Sajith

Reputation: 2852

How to prevent redirecting <a href="#something"> in Angular Js1.2

I was using AngularJs-1.0.7 and Bootstrap in my application. Recently I migrated from AngularJs-1.0.7 to AngularJs-1.2. I am using Bootstrap's Accordions and Tabs.

Html code for Tab contains <a href="#id_for_content"> as shown below.

<ul id="myTab" class="nav nav-tabs">
    <li class="active"><a href="#firstTab" data-toggle="tab">Home</a></li>
    <li><a href="#secondTab" data-toggle="tab">Profile</a></li>
</ul>

<div id="myTabContent" class="tab-content">
    <div class="tab-pane fade in active" id="firstTab">
      <p>Content for first tab.</p>
    </div>
    <div class="tab-pane fade" id="secondTab">
      <p>Content For second tab.</p>
    </div>
</div>

In Angular's old versions, route change happens only if we give anchor tag like <a href="#/firstTab">. But AngularJs-1.2 redirects <a href="#firstTab">. It doesn't consider the / in between # and firstTab. Hence while clicking on Tab it redirects to http://web_url/#/firstTab . How to solve this issue?


My Solution

I found a solution for this issue. I wrote a directive for a tag. In that directive I checked for href attribute. If it matches prevent from its default behavior. Check the following code.

app.directive('a', function() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            if(attrs.href === '#firstTab'|| attrs.href === '#secondTab'){
                elem.on('click', function(e){
                    e.preventDefault();
                });
            }
        }
   };
}); 

But the problem with this method is, I have to check each and every tab ids or accordion ids here. If I use dynamic Ids for them, its not possible to check in directive.

If you can find better solution, let us all know about it.

Upvotes: 25

Views: 36031

Answers (9)

soreal
soreal

Reputation: 264

If you don't success with previous answers, you could try use $timeout... In this case I have used AngularUI tabs...

vm.removeUserTab = function (user) {
    $timeout(function () {
        vm.activeTab = 0;
        var index = vm.userTabs.indexOf(user);
        vm.userTabs.splice(index, 1);
    });
};

Upvotes: 0

Eliezer Rhamzfer
Eliezer Rhamzfer

Reputation: 1

credits to https://prerender.io/js-seo/angularjs-seo-get-your-site-indexed-and-to-the-top-of-the-search-results/

My solution: - inside app.js try adding "$locationProvider.hashPrefix('!');"

    .config(['$stateProvider', '$urlRouterProvider', '$httpProvider', '$locationProvider',
    function($stateProvider, $urlRouterProvider, $httpProvider, $locationProvider) {
    $stateProvider
    .state('menu', {
        url: '/',
        cache: false,
        abstract: true,
        templateUrl: 'static/www/templates/menu.html'
    })
    .state('menu.main_page', {
    url: 'app/main',
    cache: false,
    views: {
        'menuContent': {
          templateUrl: 'static/www/templates/mainpage.html',
          controller: 'MainCtrl'
        }
    }
    })
    $locationProvider.hashPrefix('!');
    $urlRouterProvider.otherwise(function ($injector, $location) {
        var $state = $injector.get('$state');
        $state.go('menu.mainpage');
    });
   }])
  • inside index.html try adding

    <meta name="fragment" content="!">

and as Marcel said above

.directive('a', function () {
return {
    restrict: 'E',
    link: function (scope, elem, attrs) {
        if (attrs.href && attrs.href.indexOf('#') > -1) {
            elem.on('click', function (e) {
                e.preventDefault();
            });
        }
    }
};
})

that's all! works for me!

Upvotes: 0

PrimosK
PrimosK

Reputation: 13918

I would just like to let you know that there is another solution to solve this issue (without defining function in controllers).

You can create a directive that will prevent default browser's behaviour (which is actually changing URL):

var PreventDefault = function () {

    var linkFn = function (scope, element, attrs) {
        $(element).on("click", function (event){
            event.preventDefault();
        });
    };

    return {
        restrict: 'A',
        link: linkFn
    }
};

and then just add the directive to each a element responsible for toggling a tab like this:

<ul id="myTab" class="nav nav-tabs">
    <li class="active"><a href="#firstTab" prevent-default data-toggle="tab">Home</a></li>
    <li><a href="#secondTab" prevent-default data-toggle="tab">Profile</a></li>
</ul>

<div id="myTabContent" class="tab-content">
    <div class="tab-pane fade in active" id="firstTab">
      <p>Content for first tab.</p>
    </div>
    <div class="tab-pane fade" id="secondTab">
      <p>Content For second tab.</p>
    </div>
</div>

Upvotes: 11

Vassilis Pits
Vassilis Pits

Reputation: 3848

Just add this to your links:

target="_self"

And you're ready ;)

Upvotes: 49

Marcel
Marcel

Reputation: 8539

Your solution in your question does work, but to prevent having to check for each anchor's id, change

if (attrs.href === '#firstTab'|| attrs.href === '#secondTab')

to

if (attrs.href && attrs.href.indexOf('#') > -1)

Directive:

.directive('a', function () {
    return {
        restrict: 'E',
        link: function (scope, elem, attrs) {
            if (attrs.href && attrs.href.indexOf('#') > -1) {
                elem.on('click', function (e) {
                    e.preventDefault();
                });
            }
        }
    };
})  

Upvotes: 0

Sonymon Mishra
Sonymon Mishra

Reputation: 210

try data-target="#something", it works fine for me in both development and production environment.

Upvotes: 21

Tom Spink
Tom Spink

Reputation: 46

I've been having this problem for a while now too, and I only just stumbled across this question (and your own answer). It turns out that you are almost correct with your solution, just a slight modification makes it work as you'd like.

In addition to the issue you were having with tabs, I was also encountering this problem with dropdown links -- they were navigating away from the current page when clicked, instead of dropping down the menu -- and in fact, this problem is present on any link that was designed to toggle something, i.e. with the data-toggle attribute set.

So, a slight modification to your code to check for the presence of the 'toggle' attribute solves it for me:

app.directive('a', function() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            if(attrs.toggle){
                elem.on('click', function(e){
                    e.preventDefault();
                });
            }
        }
   };
});

Hope this helps!

Upvotes: 1

Stefan
Stefan

Reputation: 446

In general you can define functions on the prototype of all scopes by using the rootscope. Add ng-click on your anchor tags and pass the $event to the rootscope handlers. Angular can send the originating event as well with ng-click:

<a ng-click="tabclick($event)">

in a run block of your module you can then set a tabClick function on $rootScope

$rootScope.tabclick = function ($event) { $event.preventDefault(); }

This will work for any scopes and you won't have to check against specific element ids.

Sample fiddle: http://jsfiddle.net/stto3703/wQe6P/

Cheers

Upvotes: 6

Yaron Schwimmer
Yaron Schwimmer

Reputation: 5357

You can use ng-click to call a function in your JS. In that function, use the event's preventDefault method, and use $location's hash method to change the hash in your URL.

Upvotes: 2

Related Questions