Dynamically set the value of ui-sref Angularjs

AngularjsAngular UiAngular Ui-Router

Angularjs Problem Overview


I have searched for a similar question but the ones that came up seem slightly different. I am trying to change the ui-sref='' of a link dynamically (this link points to the next section of a wizard form and the next section depends on the selection made on the dropdown list). I am simply trying to set the ui-sref attribute depending on some selection in a select box. I am able to change the ui-sref by binding to a scope attribute which is set when a selection is made. however the link does not work, is this possible at all? thanks

  <a ui-sref="form.{{url}}" >Next Section</a>

and then in my controller, I set the url parameter this way

switch (option) {
  case 'A': {
    $scope.url = 'sectionA';
  } break;
  case 'B': {
    $scope.url = 'sectionB';
  } break;
}

Alternatively, I used directives to get it to work by generating the hyperlink with the desired ui-sref attribute according to the option selected on the select box (drop down).

Hhowever this means I have to re-create the link each time a different option is selected from the selectbox which causes an undesirable flickering effect. My question is this, is it possible to change the value of the ui-sref as I tried doing above by simpling changing the value of url in my controller or do I have to re-create the entire element using a directive each time a selection is made as I have done below? (just showing this for completeness)

define(['app/js/modules/app', 'app/js/directives/hyperLink'], function (app) {
app.directive('selectUsage', function ($compile) {

    function createLink(scope,element) {
        var newElm = angular.element('<hyper-link></hyper-link>');
        var el = $(element).find('.navLink');
        $(el).html(newElm);
        $compile(newElm)(scope);
    }

    return {

        restrict: 'E',
        templateUrl: '/Client/app/templates/directives/select.html'
       
        ,link: function (scope, element, attrs) {

            createLink(scope, element);

            element.on('change', function () {
                createLink(scope,element);
            })
        }
    }
})
define(['app/js/modules/app'], function (app) {
app.directive('hyperLink', function () {

    return {
        restrict: 'E',
        templateUrl: '/Client/app/templates/directives/hyperLink.html',
        link: function (scope, element, attrs) { }
    }

})
<div>
    <button ui-sref="form.{url}}">Next Section</button>
</div>

Angularjs Solutions


Solution 1 - Angularjs

Looks like this is possible to do after all.

A breadcrumb on GitHub by one of the ui-router authors led me to try the following:

<a ng-href="{{getLinkUrl()}}">Dynamic Link</a>

Then, in your controller:

$scope.getLinkUrl = function(){
  return $state.href('state-name', {someParam: $scope.someValue});
};

Turns out, this works like a charm w/ changing scoped values and all. You can even make the 'state-name' string constant reference a scoped value and that will update the href in the view as well :-)

Solution 2 - Angularjs

There is a working plunker. The most easier way seems to be to use combination of:

  • $state.href() (doc here) and
  • ng-href (doc here)

These together could be used as:

<a ng-href="{{$state.href(myStateName, myParams)}}">

So, (following this plunker) having states like these:

$stateProvider
  .state('home', {
      url: "/home",
      ...
  })
  .state('parent', {
      url: "/parent?param",
      ...
  })
  .state('parent.child', { 
      url: "/child",
      ...

We can change these values to dynamically generate the href

<input ng-model="myStateName" />
<input ng-model="myParams.param" />

Check it in action here

ORIGINAL:

There is a working example how to achieve what we need. But not with dynamic ui-sref .

As we can can check here: https://github.com/angular-ui/ui-router/issues/395

> Q: [A]re dynamic ui-sref attributes not supported?
> A: Correct.

But we can use different feature of ui-router: [$state.go("statename")][5]

So, this could be the controller stuff:

$scope.items = [
  {label : 'first', url: 'first'},
  {label : 'second', url: 'second'},
  {label : 'third', url: 'third'},
];
$scope.selected = $scope.items[0];
$scope.gotoSelected = function(){
  $state.go("form." + $scope.selected.url);
};

And here is the HTML template:

<div>
  choose the url:
  <select
    ng-model="selected"
    ng-options="item.label for item in items"
  ></select>
  
  <pre>{{selected | json}}</pre>
  <br />
  go to selected
  <button ng-click="gotoSelected()">here</button>
  
  <hr />
  <div ui-view=""></div>
</div>

The working example

NOTE: there is more up to date link to $state.go definition, but the deprecated one is a bit more clear to me

Solution 3 - Angularjs

Take a look in this issue #2944.

The ui-sref doesn't watch the state expression, you can use ui-state and ui-state-params passing the variable.

  <a data-ui-state="selectedState.state" data-ui-state-params="{'myParam':aMyParam}">
       Link to page {{selectedState.name}} with myParam = {{aMyParam}}
  </a>

Also a quickly demo provided in the ticket.

Solution 4 - Angularjs

I managed to implement it this way (I'm using the controllerAs variant though - not via $scope).

Template

<button ui-sref="main({ i18n: '{{ ctrlAs.locale }}' })">Home</button>

Controller

var vm = this;
vm.locale = 'en'; // or whatever dynamic value you prepare

Also see the documentation to ui-sref where you can pass params:

https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref

Solution 5 - Angularjs

After trying various solutions I found the problem in the angular.ui.router code.

The problem comes from the fact that ui.router update method is triggered with the ref.state which means that it is not possible to update the value of the href used when the element is clicked.

Here are 2 solutions to solve the problem:

Custom Directive
    module.directive('dynamicSref', function () {
    return {
        restrict: 'A',
        scope: {
            state: '@dynamicSref',
            params: '=?dynamicSrefParams'
        },
        link: function ($scope, $element) {
            var updateHref = function () {
                if ($scope.state) {
                    var href = $rootScope.$state.href($scope.state, $scope.params);
                    $element.attr('href', href);
                }
            };

            $scope.$watch('state', function (newValue, oldValue) {
                if (newValue !== oldValue) {
                    updateHref();
                }
            });

            $scope.$watch('params', function (newValue, oldValue) {
                if (newValue !== oldValue) {
                    updateHref();
                }
            });

            updateHref();
        }
    };
});

The HTML to use it is quite simple :

<a  dynamic-sref="home.mystate"
	dynamic-sref-params="{ param1 : scopeParam }">
	Link
</a>
Fix ui.router code :

In angular.router.js your will find the directive $StateRefDirective (line 4238 for version 0.3).

Change the directive code to :

function $StateRefDirective($state, $timeout) {
    return {
        restrict: 'A',
        require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
        link: function (scope, element, attrs, uiSrefActive) {
            var ref = parseStateRef(attrs.uiSref, $state.current.name);
            var def = { state: ref.state, href: null, params: null };
            var type = getTypeInfo(element);
            var active = uiSrefActive[1] || uiSrefActive[0];
            var unlinkInfoFn = null;
            var hookFn;

            def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {});

            var update = function (val) {
                if (val) def.params = angular.copy(val);
                def.href = $state.href(ref.state, def.params, def.options);

                if (unlinkInfoFn) unlinkInfoFn();
                if (active) unlinkInfoFn = active.$$addStateInfo(ref.state, def.params);
                if (def.href !== null) attrs.$set(type.attr, def.href);
            };

            if (ref.paramExpr) {
                scope.$watch(ref.paramExpr, function (val) { if (val !== def.params) update(val); }, true);
                def.params = angular.copy(scope.$eval(ref.paramExpr));
            }

            // START CUSTOM CODE : Ability to have a 2 way binding on ui-sref directive
            if (typeof attrs.uiSrefDynamic !== "undefined") {
                attrs.$observe('uiSref', function (val) {
                    update(val);

                    if (val) {
                        var state = val.split('(')[0];
                        def.state = state;

                        $(element).attr('href', $state.href(def.state, def.params, def.options));
                    }
                });
            }
            // END OF CUSTOM CODE

            update();

            if (!type.clickable) return;
            hookFn = clickHook(element, $state, $timeout, type, function () { return def; });
            element.bind("click", hookFn);
            scope.$on('$destroy', function () {
                element.unbind("click", hookFn);
            });
        }
    };
}

Solution 6 - Angularjs

Came to answer that for good :)

Fortunately, you don't need to use a button for ng-click, or use a function inside an ng-href to achieve what you seek. Instead;

You can create a $scope var in your controller and assign the ui-sref string in it and use it in your view, as ui-sref attribute.

Like this:

// Controller.js

// if you have nasted states, change the index [0] as needed.
// I'm getting the first level state after the root by index [0].
// You can get the child by index [1], and grandchild by [2]
// (if the current state is a child or grandchild, of course).
var page = $state.current.name.split('.')[0];
$scope.goToAddState = page + ".add";


// View.html
<a ui-sref="{{goToAddState}}">Add Button</a>

That works perfectly for me.

Solution 7 - Angularjs

The best approach is to make use of uiRouter's $state.go('stateName', {params}) on button's ng-click directive. And disable the button if no option is selected.

HTML

<select ng-model="selected" ng-options="option as option.text for option in options"></select>
<button ng-disabled="!selected" type="button" ng-click="ctrl.next()">Next</button>

Controller

function Controller($scope, $state){
    this.options = [{
		text: 'Option One',
		state: 'app.one',
		params: {
			param1: 'a',
			param2: 'b'
		}
	},{
		text: 'Option Two',
		state: 'app.two',
		params: {
			param1: 'c',
			param2: 'd'
		}
	},{
		text: 'Option Three',
		state: 'app.three',
		params: {
			param1: 'e',
			param2: 'f'
		}
	}];

	this.next = function(){
		if(scope.selected){
			$state.go($scope.selected.state, $scope.selected.params || {});
		}
	};
}

State

$stateProvider.state('wizard', {
    url: '/wizard/:param1/:param2', // or '/wizard?param1&param2'
	templateUrl: 'wizard.html',
	controller: 'Controller as ctrl'
});

Solution 8 - Angularjs

<a ng-click="{{getLinkUrl({someParam: someValue})}}">Dynamic Link</a>

$scope.getLinkUrl = function(value){
  $state.go('My.State',{someParam:value});

}

It returns an object

Solution 9 - Angularjs

this is just working for me

in controller

$scope.createState = 'stateName';

in view

ui-sref="{{ createState }}"

Solution 10 - Angularjs

For manage multiple dynamic params using ui-sref, here my solution :

Html : ('MyPage.html')

<button type="button" ui-sref="myState(configParams())">

Controller : ('MyCtrl')

.controller('MyCtrl', function ($scope) {
  $scope.params = {};
  $scope.configParams = function() {
    $scope.params.param1 = 'something';
    $scope.params.param2 = 'oh again?';
    $scope.params.param3 = 'yes more and more!';
    //etc ...

    return $scope.params;
  };
}

stateProvider : ('myState')

 $stateProvider
          .state('myState', {
            url: '/name/subname?param1&param2&param3',
            templateUrl: 'MyPage.html',
            controller: 'MyCtrl'
          });

Enjoy !

Solution 11 - Angularjs

<ul class="dropdown-menu">
  <li ng-repeat="myPair in vm.Pairs track by $index">
     <a ui-sref="buy({myPairIndex:$index})"> 
          <span class="hidden-sm">{{myPair.pair}}</span>
     </a>
  </li>
</ul>

If someone only wants to dynamically set the $stateParams of ui-sref in Angularjs. Note: In inspect element it will still appear as "buy({myPairIndex:$index})" but $index will be fetched in that state.

Solution 12 - Angularjs

I found this solution the most appropriate:

.state('history', {
    url: 'home',
    controller: 'homeCtrl',
    templateUrl: "home.html"
  })
  .state('settings', {
    url: "/settings",
    controller: 'settingsCtrl',
    templateUrl: 'settings.html'
  })
<button ui-sref="{{$ctrl.newProductLink()}}"</button>
ctrl.newProductLink = () => a > b ? 'home' : 'settings';

Solution 13 - Angularjs

Or just something like this:

<a ui-sref="{{ condition ? 'stateA' : 'stateB'}}">
  Link
</a>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser3728830View Question on Stackoverflow
Solution 1 - AngularjsRavenHursTView Answer on Stackoverflow
Solution 2 - AngularjsRadim KöhlerView Answer on Stackoverflow
Solution 3 - AngularjsFabio PicheliView Answer on Stackoverflow
Solution 4 - AngularjscodepushrView Answer on Stackoverflow
Solution 5 - AngularjsLinviView Answer on Stackoverflow
Solution 6 - AngularjsilterView Answer on Stackoverflow
Solution 7 - AngularjsrynangelesView Answer on Stackoverflow
Solution 8 - AngularjsbklupsView Answer on Stackoverflow
Solution 9 - AngularjsAbou-EmishView Answer on Stackoverflow
Solution 10 - AngularjsEmidomengeView Answer on Stackoverflow
Solution 11 - AngularjsRyan AugustineView Answer on Stackoverflow
Solution 12 - AngularjsNikita BasharkinView Answer on Stackoverflow
Solution 13 - AngularjsKarensView Answer on Stackoverflow