Establezca dinámicamente el valor de ui-sref Angularjs

79

He buscado una pregunta similar, pero las que surgieron parecen ligeramente diferentes. Estoy intentando cambiar el ui-sref = '' de un enlace dinámicamente (este enlace apunta a la siguiente sección de un formulario de asistente y la siguiente sección depende de la selección realizada en la lista desplegable). Simplemente estoy tratando de establecer el atributo ui-sref dependiendo de alguna selección en un cuadro de selección. Puedo cambiar el ui-sref vinculándome a un atributo de alcance que se establece cuando se realiza una selección. sin embargo, el enlace no funciona, ¿es posible? Gracias

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

y luego en mi controlador, configuro el parámetro url de esta manera

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

Alternativamente, utilicé directivas para que funcione generando el hipervínculo con el atributo ui-sref deseado de acuerdo con la opción seleccionada en el cuadro de selección (menú desplegable).

Sin embargo, esto significa que tengo que volver a crear el enlace cada vez que se selecciona una opción diferente en el cuadro de selección, lo que provoca un efecto de parpadeo no deseado. Mi pregunta es esta, ¿es posible cambiar el valor de ui-sref como intenté hacer anteriormente simplemente cambiando el valor de url en mi controlador o tengo que volver a crear todo el elemento usando una directiva cada vez que una selección? se hace como he hecho a continuación? (solo mostrando esto para completar)

Directiva de opción de selección (esta directiva genera la directiva de enlace)

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

Directiva de hipervínculo

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

})

Plantilla de hipervínculo

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

Respuestas:

68

Parece que esto es posible después de todo.

Una ruta de navegación en GitHub por uno de los autores de ui-router me llevó a probar lo siguiente:

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

Luego, en tu controlador:

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

Resulta que esto funciona como un encanto con el cambio de valores de alcance y todo. Incluso puede hacer que la cadena 'state-name' haga referencia constante a un valor de ámbito y eso también actualizará el href en la vista :-)

CuervoHursT
fuente
1
La solución más limpia, y lo que vale la pena mencionar, "independiente de la biblioteca del enrutador".
Radek Anuszewski
1
Esto obliga a que getLinkUrl () se procese en cada ciclo de resumen, que es MUY frecuente. Coloque un console.log en getLinkUrl () y simplemente mueva el mouse por la página y observe cómo se dispara su registro. No digo que este método no funcione, simplemente es complicado.
Suamere
2
Esta fue una solución que se escribió en Angular 1.4 hace más de 3 años. Era la única opción viable para lograr la funcionalidad deseada en ese entonces, con esa versión de Angular. Simplemente criticar y rechazar una respuesta, sin siquiera molestarse en dar una solución alternativa (si existe), no ayuda a nadie.
RavenHursT
1
Es absolutamente ayuda. Sugiere a los futuros espectadores que esta puede no ser la mejor solución y deberían buscar en otra parte. No había nada "poco constructivo" en ese comentario. Si algo no fue constructivo, fue su respuesta, arremetiendo contra un usuario por una crítica válida (y asumiendo hechos que no están en evidencia, como el voto negativo relacionado con el comentario).
Cody Gray
1
La respuesta de @ FabioPicheli proporciona una solución nativa más actualizada, asegúrese de revisarla.
phazei
61

Hay un plunker que funciona . La forma más fácil parece ser utilizar una combinación de:

  • $state.href() (doc aquí ) y
  • ng-href (doc aquí )

Estos juntos podrían usarse como:

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

Entonces, (siguiendo este plunker ) tener estados como estos:

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

Podemos cambiar estos valores para generar dinámicamente el href

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

Compruébalo en acción aquí

ORIGINAL:

Hay un ejemplo práctico de cómo lograr lo que necesitamos. Pero no con dinámica ui-sref.

Como podemos comprobar aquí: https://github.com/angular-ui/ui-router/issues/395

P: ¿[A] los atributos dinámicos de ui-sref no son compatibles?
A: Correcto.

Pero podemos usar diferentes características de ui-router:[$state.go("statename")][5]

Entonces, esto podría ser el tema del controlador:

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

Y aquí está la plantilla HTML:

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

El ejemplo de trabajo

NOTA: hay un enlace más actualizado a la definición de $ state.go , pero el obsoleto es un poco más claro para mí

Radim Köhler
fuente
1
En mi humilde opinión, esto no logra completamente el comportamiento deseado. ui-sref en realidad establece el atributo href de la etiqueta de anclaje al inicializar la directiva ui-sref. Lo que normalmente hago para las etiquetas de ancla controladas dinámicamente es algo como lo siguiente: <a ui-sref="{{scopedStateName}}( { 'some-state-param': '{{scopedParamValue}}' })">My Dynamic Link</a> el único problema con este enfoque, obviamente, es que ui-sref no observa los cambios en las variables de ámbito, por lo que el href no cambia en los cambios de valor posteriores . AFAIK, ui-sref no admite esto.
RavenHursT
Después de investigar un poco más, creo que encontré una manera de lograr atributos href generados dinámicamente que hacen referencia a estados con parámetros de estado. Por favor vea mi respuesta a continuación.
RavenHursT
¿Cómo está evitando una actualización de página dura mientras usa ng-href? Actualmente tengo problemas con esta solución porque ng-href no usa el $state.go()método ui-routers .
Andre
27

Eche un vistazo a este número 2944 .

El ui-srefno ve la expresión de estado, puede usar ui-statey ui-state-paramspasar la variable.

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

También una demostración rápida proporcionada en el boleto.

Fabio Picheli
fuente
1
Excelente. No requiere lógica adicional y también admite ui-sref-active
Julia
2

Me las arreglé para implementarlo de esta manera (aunque estoy usando la variante controllerAs, no a través de $ scope).

Modelo

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

Controlador

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

También vea la documentación a ui-srefdonde puede pasar parámetros:

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

codepushr
fuente
No, se buttoncomporta de la misma manera que ala directiva en realidad crea un hrefatributo en el botón en el momento de la compilación, pero nunca lo actualiza. Y un clic en el botón siempre irá al estado del hrefvalor del pero no al actualizadoui-sref
Jens
2

Después de probar varias soluciones, encontré el problema en el angular.ui.routercódigo.

El problema proviene del hecho de que el updatemétodo ui.router se activa con el, lo ref.stateque significa que no es posible actualizar el valor del hrefutilizado cuando se hace clic en el elemento.

Aquí hay 2 soluciones para resolver el problema:

Directiva personalizada

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

El HTML para usarlo es bastante simple:

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

Corregir el código de ui.router:

En angular.router.js encontrará la directiva $StateRefDirective(línea 4238 para la versión 0.3).

Cambie el código de la directiva a:

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);
            });
        }
    };
}
Linvi
fuente
2
A menos que cree su propia bifurcación, angular-ui-routerle sugiero que no lo haga . Tu compañero de trabajo podría intentar actualizarlo y boom , nadie sabe de dónde vino el error.
Rafael Herscovici
1

Vine a responder eso para siempre :)

Afortunadamente, no necesita usar un botón para ng-click o usar una función dentro de un ng-href para lograr lo que busca. En lugar;

Puede crear una $scopevar en su controlador y asignar la ui-srefcadena en ella y usarla en su vista, como ui-srefatributo.

Me gusta esto:

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

Eso funciona perfectamente para mí.

ilter
fuente
1
He intentado su enfoque, puedo ver ui-srefpero hrefno generar
Cualquier
No mencioné "href" en ninguna parte de mi respuesta. ¿Qué tan relevante es eso?
2015
1
ui-sref es una directiva que establece un atributo href en un elemento si el estado proporcionado tiene una URL (pero en este caso aparentemente no).
askmike
2
@ilter esto funciona la primera vez, pero no hay ningún enlace bidireccional: el enlace no se actualizará cuando cambie el alcance.
Ben
1
Solo puedo hablar por mí mismo, pero en mi caso es porque la respuesta no funciona para la pregunta. Cuando el menú desplegable cambie, ya no tendrá un enlace válido.
Josh Gagnon
1

El mejor enfoque es hacer uso de uiRouter's $state.go('stateName', {params})la ng-clickdirectiva de on button . Y deshabilite el botón si no se selecciona ninguna opción.

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>

Controlador

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

Estado

$stateProvider.state('wizard', {
    url: '/wizard/:param1/:param2', // or '/wizard?param1&param2'
    templateUrl: 'wizard.html',
    controller: 'Controller as ctrl'
});
rynangeles
fuente
1
esto no funciona con ui-sref-active o ui-sref-active-eq
KFE
1
<a ng-click="{{getLinkUrl({someParam: someValue})}}">Dynamic Link</a>

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

}

Devuelve un objeto

bklups
fuente
0

esto solo funciona para mí

en controlador

$scope.createState = 'stateName';

en vista

ui-sref="{{ createState }}"
Abou-Emish
fuente
4
como lo señaló @ben en las otras respuestas: esto funciona la primera vez, pero no hay ningún enlace bidireccional: el enlace no se actualizará cuando cambie el alcance
Jens
0

Para administrar múltiples parámetros dinámicos usando ui-sref, aquí mi solución:

Html: ('Mi página.html')

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

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

¡Disfruta!

Emidomenge
fuente
Me da un error que indica que no puedo navegar a myState desde el estado actual
Mani
@Mani ¿Estás usando ui-router ? ¿Es posible ver tu código?
Emidomenge
Sí, estoy usando ui-router. <ion-item class = "accordion-item" ng-repeat = "item in group.items" ng-show = "isGroupShown (group)" ui-sref = "{{item.state}}" ui-sref-active = "iactive"> {{item.title}} </ion-item>
Mani
try ui-sref="item.state"
Emidomenge
su trabajo con tirantes florales solamente. Lo siento. Hubo otro lugar donde cometí un error. Lo siento.
Mani
0
<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>

Si alguien solo quiere establecer dinámicamente los $ stateParams de ui-sref en Angularjs. Nota: En el elemento inspeccionar, seguirá apareciendo como "comprar ({myPairIndex: $ index})" pero se obtendrá $ index en ese estado.

Ryan Augustine
fuente
-1

O simplemente algo como esto:

<a ui-sref="{{ condition ? 'stateA' : 'stateB'}}">
  Link
</a>
Karens
fuente
3
como lo señaló @ben en las otras respuestas: esto funciona la primera vez, pero no hay ningún enlace bidireccional: el enlace no se actualizará cuando cambie el alcance
Jens
Estoy probando este método, pero no pude hacer ninguna sugerencia o
Aswin