Как добиться того, чтобы "ui-sref" был условно выполнен?
Я хочу проверить некоторые условия перед тем, как браузер будет следовать за динамикой, созданной ui-router.
Я смотрел $rootscope.$on('$stateChangeStart', ..)
, но у меня нет доступа к controller.$scope
. Я также должен использовать это в нескольких местах в приложении и будет громоздким.
Имейте в виду, что ui-sref
связан с ui-sref-active
(работать вместе), поэтому я не могу удалить ui-sref
и, скажем, использовать $state.$go('some-state')
внутри функции с ng-click
.
Условие должно быть оценено внутри a $scope function
и на on-click event
(перед переходом с возможностью его отмены)
Мне нужно что-то вроде этого:
<li ui-sref-active="active">
<a ui-sref="somestate" ui-sref-if="model.validate()">Go Somestate</a>
</li>
Я пробовал:
<li ui-sref-active="active">
<a ui-sref="somestate" ng-click="$event.preventDefault()">Go Somestate</a>
</li>
<li ui-sref-active="active">
<a ui-sref="somestate" ng-click="$event.stopImmediatePropagation()">Go Somestate</a>
</li>
и
<li ui-sref-active="active">
<a ui-sref="somestate">
<span ng-click="$event.stopPropagation();">Go Somestate</span>
</a>
</li>
Даже
<li ui-sref-active="active">
<a ui-sref="somestate" onclick="return false;">Go Somestate</a>
</li>
Но не работает.
SANDBOX
Ответы
Ответ 1
Этот ответ вдохновил меня на создание директивы, которая позволяет мне прервать цепочку событий, которые в конечном итоге меняют состояние. Для удобства и других целей также предотвращает выполнение ng-click на одном и том же элементе.
Javascript
module.directive('eatClickIf', ['$parse', '$rootScope',
function($parse, $rootScope) {
return {
// this ensure eatClickIf be compiled before ngClick
priority: 100,
restrict: 'A',
compile: function($element, attr) {
var fn = $parse(attr.eatClickIf);
return {
pre: function link(scope, element) {
var eventName = 'click';
element.on(eventName, function(event) {
var callback = function() {
if (fn(scope, {$event: event})) {
// prevents ng-click to be executed
event.stopImmediatePropagation();
// prevents href
event.preventDefault();
return false;
}
};
if ($rootScope.$$phase) {
scope.$evalAsync(callback);
} else {
scope.$apply(callback);
}
});
},
post: function() {}
}
}
}
}
]);
HTML
<li ui-sref-active="active">
<a ui-sref="somestate" eat-click-if="!model.isValid()">Go Somestate</a>
</li>
PLUNKER
Ответ 2
Вы можете использовать функцию области видимости, которая либо вернется:
- нет состояния
-
существующее состояние
так:
HTML:
<li ui-sref-active="active">
<a ui-sref="{{checkCondition()}}">Go Somestate</a>
</li>
Область JS:
$scope.checkCondition = function() {
return model.validate()
? 'someState'
: '-' // hack: must return a non-empty string to prevent JS console error
}
Атрибут href
будет создан только тогда, когда функция возвращает существующую строку состояния.
В качестве альтернативы вы можете сделать (уродливый):
<li ui-sref-active="active">
<a ui-sref="somestate" ng-if="model.validate()">Go Somestate</a>
<span ng-if="!model.validate()">Go Somestate</span>
</li>
Надеюсь, что это поможет
Ответ 3
Вы всегда можете удвоить элемент и показать/скрыть условно
<li ui-sref-active="active">
<a ng-show="condition1" style="color: grey">Start</a>
<a ng-hide="condition1" ui-sref="start">Start</a>
</li>
http://plnkr.co/edit/ts4yGW?p=preview
Ответ 4
Простейшим обходным путем для условного достижения маршрутизации без использования директив, области и т.д. было обходное решение, которое я нашел здесь - https://github.com/angular-ui/ui-router/issues/1489
<a ui-sref="{{condition ? '.childState' : '.'}}"> Conditional Link </a>
Ответ 5
На основе ответов на Как динамически установить значение ui-sref
вы можете создать функцию в своей области для создания URL-адреса:
$scope.buildUrl = function() {
return $state.href('somestate', {someParam: 'someValue});
};
И затем условно добавьте его к ссылке с ng-href
<a ng-href="{{ someCondition ? buildUrl() : undefined }}">Link</a>
Как вы можете видеть в демонстрационной версии ниже, ng-href
не добавляет атрибут href
, если значение отрицательное.
angular.module('app', [])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<a ng-href="{{ condition ? 'http://thecatapi.com/api/images/get?format=src&type=gif' : undefined}}">This is the link</a>
<br>
<label for="checkbox">
<input type="checkbox" id="checkbox" ng-model="condition">
Link active?
</label>
</div>
Ответ 6
Нет необходимости в сложных директивах или хаках. Следующее работает отлично и позволяет осуществлять определенную обработку при нажатии элементов не sref
:
<a
ng-repeat="item in items" ui-sref="{{item.sref || '-'}}"
ng-click="$ctrl.click(item, $event)"
>...</a>
И в контроллере простой обработчик кликов для элементов, которые не имеют item.sref
:
this.click = function(item, event) {
if (!item.sref) {
event.preventDefault();
//do something else
}
};
Ответ 7
Я знаю, что это старый вопрос, но для будущей ссылки я хотел предложить альтернативное решение, так как я не видел его ни в одном из ответов.
Желаемая:
<li ui-sref-active="active">
<a ui-sref="somestate" ui-sref-if="model.validate()">Go Somestate</a>
</li>
Потенциальное решение (шаблон):
<li ng-class="{ active: state.current.name === 'somestate' }">
<a ng-click="navigateToState()">Go Somestate</a>
</li>
И в контроллере:
$scope.state = $state;
$scope.navigateToState = navigateToState;
function navigateToState() {
if ($scope.model.valid) {
$state.go('somestate');
}
}
Ответ 8
Возможное решение для тех, кому еще нужно ng-click
работать с компонентом ui-sref
или его родителями.
Мое решение состоит в том, чтобы использовать href
вместо ui-sref
и немного изменить директиву Emanuel, чтобы иметь возможность останавливать вызовы href
и ng-click
в отдельности.
Planker.
Хотя у него есть несколько ограничений:
- не будет работать с
ui-sref
- у вас должны быть разные URL-адреса для каждого состояния из-за предыдущего ограничения
-
ui-sref-active
не будет работать.