次のようなルートを持つコントローラーがあります。
#/articles/1234
コントローラーを完全にリロードせずにルートを変更したいので、コントローラー内の他のものの位置を一定に保つことができます(スクロールするリスト)
これを行う方法はいくつか考えられますが、どれもかなり醜いです。このようなことをするためのベストプラクティスはありますか? reloadOnSearch: false で実験してみましたが、何も変わらないようです。
次のようなルートを持つコントローラーがあります。
#/articles/1234
コントローラーを完全にリロードせずにルートを変更したいので、コントローラー内の他のものの位置を一定に保つことができます(スクロールするリスト)
これを行う方法はいくつか考えられますが、どれもかなり醜いです。このようなことをするためのベストプラクティスはありますか? reloadOnSearch: false で実験してみましたが、何も変わらないようです。
まったく同じ挑戦をした、
トリックを行った別の StackOverflow 応答でハックを見つけました
かなりクリーンなソリューション - $location.path を設定するコントローラーに次の行を追加するだけです。
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function(event) {
$route.current = lastRoute;
});
..もちろん、 $route がコントローラーに挿入されていることを確認しました。
それでも、「DoNotFollowRoutesOnPathChange」は AngularJS に欠けている機能のように感じます。
/イェンス
更新:このイベントをリッスンすると、$routeProvider 構成のさらなる使用が効果的に停止されるため、このキャッチを現在のパスのみに制限する必要がありました。
var lastRoute = $route.current;
if ($route.current.$route.templateUrl.indexOf('mycurrentpath') > 0) {
$route.current = lastRoute;
}
醜くなる…
あなたが述べたように、あなたは方法を使用することができます$location.search()
。"$routeUpdate"
他のルート イベントではなく、スコープでイベントをリッスンする必要があります。$ルート 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 .
reloadOnSearch を false に設定する?a=b&c=d
と、URL の一部をリロードせずに設定できます。ただし、リロードせずに実際の場所をきれいに変更することはできません。
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
HTML5 の window.history を次のように処理するファクトリを定義します (いくつかの問題があるため、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 を使用してみてください。
私が助けてくれることを願っています。乾杯、ハインリッヒ
使用する:
$routeProvider.when('/somewhere', {
controller: 'SomeCtrl',
reloadOnSearch: false
})
これにより、クエリ パラメータの変更時だけでなく、ハッシュの変更時にもコントローラがリロードされなくなります。
これは、 s promise オプションを使用したハックなしで実行でき$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;
}]
}
}
);
ここにプラグインがあります: https://github.com/anglibs/angular-location-update
使用法:
$location.update_path('/notes/1');
この簡単な解決策は(驚くべきことに)私にとってはうまくいきます:
$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 を使用しています (古いことはわかっています)。