AngularJS: изменение хеша и маршрута без контроллера полной перезагрузки
У меня есть контроллер с таким маршрутом:
#/статьи/1234
Я хочу изменить маршрут без полной перезагрузки контроллера, поэтому я могу сохранить положение другого элемента в постоянной контроллере (списки, которые прокручиваются)
Я могу придумать несколько способов сделать это, но они все довольно уродливые. Есть ли лучший способ сделать что-то подобное? Я попытался поэкспериментировать с reloadOnSearch: false, но он ничего не меняет.
Ответы
Ответ 1
Имел тот же самый вызов,
Найден взломать fooobar.com/questions/139644/..., который сделал трюк
Довольно чистое решение - все, что я сделал, это добавить эти строки в контроллер, который устанавливает $location.path:
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
.. и, конечно, конечно $route впрыснул в контроллер.
Но все же, похоже, что "DoNotFollowRoutesOnPathChange" - это недостающая функция в AngularJS.
/Jens
Обновление: Поскольку прослушивание этого события эффективно убивает дальнейшее использование конфигураций $routeProvider, мне пришлось ограничивать этот catch только текущим путем:
var lastRoute = $route.current;
if ($route.current.$route.templateUrl.indexOf('mycurrentpath') > 0) {
$route.current = lastRoute;
}
Получение уродливого...
Ответ 2
Краткий ответ:
Вы можете использовать метод $location.search()
, как вы упомянули. Вы должны слушать событие "$routeUpdate"
в области видимости вместо других событий маршрута. $route API.
Объяснение:
-
Прежде всего (вы уже знаете) добавьте reloadOnSearch: false
в свой $routeProvider
:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
-
Измените тэг привязки href
или ng-href
на href="#/somewhere?param=value"
, это вызовет событие $routeChangeSuccess
, если часть пути (/somewhere
) не совпадает с текущим местоположением. В противном случае это вызовет событие $routeUpdate
.
-
Прослушать событие по области:
$scope.$on("$routeUpdate", function(event, route) {
// some code here
});
-
Если вы хотите изменить параметры поиска в коде, вы можете использовать метод $location.search()
. $location.search API.
Ответ 3
Если вы установите для reloadOnSearch значение false, вы можете установить ?a=b&c=d
часть URL без перезагрузки. Вы не можете изменить фактическое местоположение красиво, но без перезагрузки.
Ответ 4
редактировать
Улучшенный подход при использовании ngRoute:
/**
* Add skipReload() to $location service.
*
* See https://github.com/angular/angular.js/issues/1699
*/
app.factory('location',
['$rootScope', '$route', '$location',
function($rootScope, $route, $location) {
$location.skipReload = function() {
var lastRoute = $route.current;
var deregister = $rootScope.$on('$locationChangeSuccess',
function(e, absUrl, oldUrl) {
console.log('location.skipReload', 'absUrl:', absUrl, 'oldUrl:', oldUrl);
$route.current = lastRoute;
deregister();
});
return $location;
};
return $location;
}]);
Как использовать:
app.controller('MyCtrl', ['$scope', 'location', function($scope, location) {
$scope.submit = function() {
location.skipReload().path(path);
};
}]);
Старый ответ
Я написал многоразовую фабрику, основанную на Jens X Augustsson:
app.factory('DoNotReloadCurrentTemplate', ['$route', function($route) {
return function(scope) {
var lastRoute = $route.current;
scope.$on('$locationChangeSuccess', function() {
if (lastRoute.$$route.templateUrl === $route.current.$$route.templateUrl) {
console.log('DoNotReloadCurrentTemplate',
$route.current.$$route.templateUrl);
$route.current = lastRoute;
}
});
};
}]);
Работает с AngularJS 1.0.6
Как использовать:
app.controller('MyCtrl',
['$scope', 'DoNotReloadCurrentTemplate',
function($scope, DoNotReloadCurrentTemplate) {
DoNotReloadCurrentTemplate($scope);
}]);
Вызов AngularJS здесь: https://github.com/angular/angular.js/issues/1699
Ответ 5
определите factory для обработки окна HTML5. Подобная история (обратите внимание, что я сохраняю собственный Stack, чтобы он работал на android, так как в нем есть несколько проблем):
.factory('History', function($rootScope) {
var StateQueue = [];
var StatePointer = 0;
var State = undefined;
window.onpopstate = function(){
// called when back button is pressed
State = StateQueue.pop();
State = (State)?State:{};
StatePointer = (StatePointer)?StatePointer-1:0;
$rootScope.$broadcast('historyStateChanged', State);
window.onpopstate = window.onpopstate;
}
return {
replaceState : function(data, title, url) {
// replace current state
var State = this.state();
State = {state : data};
window.history.replaceState(State,title,url);
StateQueue[StatePointer] = State;
$rootScope.$broadcast('historyStateChanged', this.state());
},
pushState : function(data, title, url){
// push state and increase pointer
var State = this.state();
State = {state : data};
window.history.pushState(State,title,url);
StateQueue.push(State);
$rootScope.$broadcast('historyStateChanged', this.state());
StatePointer ++;
},
fakePush : function(data, title, url) {
// call this when you do location.url(url)
StateQueue.push((StateQueue.length - 1 >= 0)?StateQueue[StateQueue.length -1]:{});
StatePointer ++;
$rootScope.$broadcast('historyStateChanged', this.state());
},
state : function() {
// get current state
return (StateQueue[StatePointer])?StateQueue[StatePointer]:{};
},
popState : function(data) {
// TODO manually popState
},
back : function(data) {
// TODO needed for iphone support since iphone doesnt have a back button
}
}
})
добавьте несколько слушателей в зависимые области, и вы будете в порядке:
$scope.$on('historyStateChanged', function(e,v) {
if(v)
$scope.state = v;
else
$scope.state = {}
});
вот как я это делаю.
С моей точки зрения URL-адрес должен меняться только при загрузке нового представления.
Я думаю, что команда Angular планировала это так или иначе.
Если вы хотите направить вперед/назад кнопку на своей модели, попробуйте сделать это с помощью HTML5 window.history
Надеюсь, что смогу помочь.
Приветствия, Генрих
Ответ 6
Использование:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
Это предотвратит перезагрузку контроллера при изменении параметра запроса, но также и при изменении хэша.
Ответ 7
Если вы приземлитесь здесь в 2015 году: реальный ответ здесь заключается в том, чтобы использовать ни один из этих хаков (я смею называть их так, потому что, используя любой из перечисленных выше методов, вы потеряете возможность использовать решение и подобные), но для перехода на ui-router.
Вот удобная презентация о различиях. Реализация должна быть такой же простой, как обмен $route для $state и преобразование состояний в имена.
В настоящее время я перехожу к методу, где я буду ссылаться на href на маршрут, с необязательным параметром get, который изменяет состояние без перезагрузки. Подробнее об этом см. Раздел здесь
Ответ 8
Это простое решение (неожиданно) работает для меня:
$state.params.id = id; //updating the $state.params somehow prevents reloading the state
$location.path('/articles/' + id);
Это не предотвращает перезагрузку состояния на кнопке "Назад" и "вперед".
Примечание. Я использую angularjs 1.2.7 и ui-router 0.0.1 (я знаю это старый).
Ответ 9
Вот плагин: https://github.com/anglibs/angular-location-update
Использование:
$location.update_path('/notes/1');
Ответ 10
Вы можете сделать это без использования тэгов $locationChange~
и HistoryState
, используя route
resolve
вариант обещания.
Предполагая, что у вас есть маршрут article
, где это число изменилось, вы можете это сделать;
$routeProvider.when(
'/article/:number',
{
templateUrl : 'partial.html',
controller : 'ArticleCtrl',
resolve : {
load : ['$q', '$routeParams', function($q, $routeParams) {
var defer = $q.defer();
//number was specified in the previous route parameters, means we were already on the article page
if ($routeParams.number)
//so dont reload the view
defer.reject('');
//otherwise, the number argument was missing, we came to this location from another route, load the view
else
defer.resolve();
return defer.promise;
}]
}
}
);