Dynamically set the value of ui-sref Angularjs

2019-01-07 05:26发布

问题:

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)

Select option directive (this directive generates the link directive)

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);
            })
        }
    }
})

Hyperlink directive

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) { }
    }

})

Hyperlink template

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

回答1:

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 :-)



回答2:

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



回答3:

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.



回答4:

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



回答5:

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);
            });
        }
    };
}


回答6:

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.



回答7:

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'
});


回答8:

this is just working for me

in controller

$scope.createState = 'stateName';

in view

ui-sref="{{ createState }}"


回答9:

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 !



回答10:

<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.



回答11:

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

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

}

It returns an object



回答12:

Or just something like this:

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