IT

AngularJS에서 컨트롤러를 다시로드하지 않고 경로를 변경할 수 있습니까?

lottoking 2020. 5. 16. 10:23
반응형

AngularJS에서 컨트롤러를 다시로드하지 않고 경로를 변경할 수 있습니까?


그것은 이전에 요청되었으며 대답에서 좋지 않습니다. 이 샘플 코드를 고려하여 물어보고 싶습니다 ...

내 앱은 제공하는 서비스에서 현재 항목을로드합니다. 항목을 다시로드하지 않고 항목 데이터를 조작하는 여러 컨트롤러가 있습니다.

내 컨트롤러는 아직 설정되지 않은 경우 항목을 다시로드합니다. 그렇지 않으면 컨트롤러간에 서비스에서 현재로드 된 항목을 사용합니다.

문제 : Item.html을 다시로드하지 않고 컨트롤러마다 다른 경로를 사용하고 싶습니다.

1) 가능합니까?

2) 이것이 가능하지 않은 경우, 내가 생각해 낸 것에 비해 컨트롤러 당 경로를 갖는 더 나은 접근 방법이 있습니까?

app.js

var app = angular.module('myModule', []).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/items', {templateUrl: 'partials/items.html',   controller: ItemsCtrl}).
      when('/items/:itemId/foo', {templateUrl: 'partials/item.html', controller: ItemFooCtrl}).
      when('/items/:itemId/bar', {templateUrl: 'partials/item.html', controller: ItemBarCtrl}).
      otherwise({redirectTo: '/items'});
    }]);

Item.html

<!-- Menu -->
<a id="fooTab" my-active-directive="view.name" href="#/item/{{item.id}}/foo">Foo</a>
<a id="barTab" my-active-directive="view.name" href="#/item/{{item.id}}/bar">Bar</a>
<!-- Content -->
<div class="content" ng-include="" src="view.template"></div>

controller.js

// Helper function to load $scope.item if refresh or directly linked
function itemCtrlInit($scope, $routeParams, MyService) {
  $scope.item = MyService.currentItem;
  if (!$scope.item) {
    MyService.currentItem = MyService.get({itemId: $routeParams.itemId});
    $scope.item = MyService.currentItem;
  }
}
function itemFooCtrl($scope, $routeParams, MyService) {
  $scope.view = {name: 'foo', template: 'partials/itemFoo.html'};
  itemCtrlInit($scope, $routeParams, MyService);
}
function itemBarCtrl($scope, $routeParams, MyService) {
  $scope.view = {name: 'bar', template: 'partials/itemBar.html'};
  itemCtrlInit($scope, $routeParams, MyService);
}

해결.

상태 : 허용 된 답변에서 권장되는 검색어를 사용하면 메인 컨트롤러를 다시로드하지 않고도 다른 URL을 제공 할 수있었습니다.

app.js

var app = angular.module('myModule', []).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/items', {templateUrl: 'partials/items.html',   controller: ItemsCtrl}).
      when('/item/:itemId/', {templateUrl: 'partials/item.html', controller: ItemCtrl, reloadOnSearch: false}).
      otherwise({redirectTo: '/items'});
    }]);

Item.html

<!-- Menu -->
<dd id="fooTab" item-tab="view.name" ng-click="view = views.foo;"><a href="#/item/{{item.id}}/?view=foo">Foo</a></dd>
<dd id="barTab" item-tab="view.name" ng-click="view = views.bar;"><a href="#/item/{{item.id}}/?view=foo">Bar</a></dd>

<!-- Content -->
<div class="content" ng-include="" src="view.template"></div>

controller.js

function ItemCtrl($scope, $routeParams, Appts) {
  $scope.views = {
    foo: {name: 'foo', template: 'partials/itemFoo.html'},
    bar: {name: 'bar', template: 'partials/itemBar.html'},
  }
  $scope.view = $scope.views[$routeParams.view];
}

directives.js

app.directive('itemTab', function(){
  return function(scope, elem, attrs) {
    scope.$watch(attrs.itemTab, function(val) {
      if (val+'Tab' == attrs.id) {
        elem.addClass('active');
      } else {
        elem.removeClass('active');
      }
    });
  }
});

내 부분의 내용은 ng-controller=...


당신이 좋아하는 URL을 사용하지 않는 경우 #/item/{{item.id}}/foo#/item/{{item.id}}/bar#/item/{{item.id}}/?foo#/item/{{item.id}}/?bar대신을 위해, 당신은 당신의 경로를 설정할 수 있습니다 /item/{{item.id}}/가지고 reloadOnSearch에 세트를 false( https://docs.angularjs.org/api/ngRoute/provider/$routeProvider ). URL의 검색 부분이 변경되면 AngularJS가 뷰를 다시로드하지 않도록 지시합니다.


경로를 변경해야하는 경우 앱 파일에서 .config 다음에 경로를 추가하십시오. 그런 다음 $location.path('/sampleurl', false);다시로드를 막을 수 있습니다

app.run(['$route', '$rootScope', '$location', function ($route, $rootScope, $location) {
    var original = $location.path;
    $location.path = function (path, reload) {
        if (reload === false) {
            var lastRoute = $route.current;
            var un = $rootScope.$on('$locationChangeSuccess', function () {
                $route.current = lastRoute;
                un();
            });
        }
        return original.apply($location, [path]);
    };
}])

크레딧은 내가 찾은 가장 우아한 솔루션 을 위해 https://www.consolelog.io/angularjs-change-path-without-reloading으로 이동합니다 .


왜 ng-controller를 한 단계 높이 올리지 않겠습니까?

<body ng-controller="ProjectController">
    <div ng-view><div>

경로에 컨트롤러를 설정하지 마십시오.

.when('/', { templateUrl: "abc.html" })

그것은 나를 위해 작동합니다.


컨트롤러를 다시로드하지 않고 path () 변경이 필요한 사람들을 위해-여기 플러그인이 있습니다 : https://github.com/anglibs/angular-location-update

용법:

$location.update_path('/notes/1');

https://stackoverflow.com/a/24102139/1751321 기반

추신 :이 솔루션 https://stackoverflow.com/a/24102139/1751321 path (, false) 호출 후 버그가 포함되어 있습니다-path (, true)가 호출 될 때까지 브라우저 탐색을 뒤로 / 앞으로 중단합니다


이 게시물은 오래되었지만 대답이 수락되었지만 reloadOnSeach = false를 사용해도 매개 변수뿐만 아니라 실제 경로를 변경 해야하는 사람들에게는 문제가 해결되지 않습니다. 고려해야 할 간단한 해결책은 다음과 같습니다.

ng-view 대신 ng-include를 사용하고 템플릿에 컨트롤러를 할당하십시오.

<!-- In your index.html - instead of using ng-view -->
<div ng-include="templateUrl"></div>

<!-- In your template specified by app.config -->
<div ng-controller="MyController">{{variableInMyController}}</div>

//in config
$routeProvider
  .when('/my/page/route/:id', { 
    templateUrl: 'myPage.html', 
  })

//in top level controller with $route injected
$scope.templateUrl = ''

$scope.$on('$routeChangeSuccess',function(){
  $scope.templateUrl = $route.current.templateUrl;
})

//in controller that doesn't reload
$scope.$on('$routeChangeSuccess',function(){
  //update your scope based on new $routeParams
})

단점은 resolve 속성을 사용할 수 없지만 해결하기가 쉽다는 것입니다. 또한 해당 URL이 변경됨에 따라 경로가 컨트롤러 내에서 변경 될 때 $ routeParams 기반 논리와 같이 컨트롤러의 상태를 관리해야합니다.

예를 들면 다음과 같습니다. http://plnkr.co/edit/WtAOm59CFcjafMmxBVOP?p=preview


이 솔루션을 사용합니다

angular.module('reload-service.module', [])
.factory('reloadService', function($route,$timeout, $location) {
  return {
     preventReload: function($scope, navigateCallback) {
        var lastRoute = $route.current;

        $scope.$on('$locationChangeSuccess', function() {
           if (lastRoute.$$route.templateUrl === $route.current.$$route.templateUrl) {
              var routeParams = angular.copy($route.current.params);
              $route.current = lastRoute;
              navigateCallback(routeParams);
           }
        });
     }
  };
})

//usage
.controller('noReloadController', function($scope, $routeParams, reloadService) {
     $scope.routeParams = $routeParams;

     reloadService.preventReload($scope, function(newParams) {
        $scope.routeParams = newParams;
     });
});

이 방법은 뒤로 버튼 기능을 유지하며 내가 본 다른 방법과 달리 항상 템플릿에 현재 routeParam이 있습니다.


There is simple way to change path without reloading

URL is - http://localhost:9000/#/edit_draft_inbox/1457

Use this code to change URL, Page will not be redirect

Second parameter "false" is very important.

$location.path('/edit_draft_inbox/'+id, false);

Answers above, including the GitHub one, had some issues for my scenario and also back button or direct url change from browser was reloading the controller, which I did not like. I finally went with the following approach:

1. Define a property in route definitions, called 'noReload' for those routes where you don't want the controller to reload on route change.

.when('/:param1/:param2?/:param3?', {
    templateUrl: 'home.html',
    controller: 'HomeController',
    controllerAs: 'vm',
    noReload: true
})

2. In the run function of your module, put the logic that checks for those routes. It will prevent reload only if noReload is true and previous route controller is the same.

fooRun.$inject = ['$rootScope', '$route', '$routeParams'];

function fooRun($rootScope, $route, $routeParams) {
    $rootScope.$on('$routeChangeStart', function (event, nextRoute, lastRoute) {
        if (lastRoute && nextRoute.noReload 
         && lastRoute.controller === nextRoute.controller) {
            var un = $rootScope.$on('$locationChangeSuccess', function () {
                un();
                // Broadcast routeUpdate if params changed. Also update
                // $routeParams accordingly
                if (!angular.equals($route.current.params, lastRoute.params)) {
                    lastRoute.params = nextRoute.params;
                    angular.copy(lastRoute.params, $routeParams);
                    $rootScope.$broadcast('$routeUpdate', lastRoute);
                }
                // Prevent reload of controller by setting current
                // route to the previous one.
                $route.current = lastRoute;
            });
        }
    });
}

3. Finally, in the controller, listen to $routeUpdate event so you can do whatever you need to do when route parameters change.

HomeController.$inject = ['$scope', '$routeParams'];

function HomeController($scope, $routeParams) {
    //(...)

    $scope.$on("$routeUpdate", function handler(route) {
        // Do whatever you need to do with new $routeParams
        // You can also access the route from the parameter passed
        // to the event
    });

    //(...)
}

Keep in mind that with this approach, you don't change things in the controller and then update the path accordingly. It's the other way around. You first change the path, then listen to $routeUpdate event to change things in the controller when route parameters change.

This keeps things simple and consistent as you can use the same logic both when you simply change path (but without expensive $http requests if you like) and when you completely reload the browser.


Here's my fuller solution which solves a few things @Vigrond and @rahilwazir missed:

  • When search params were changed, it would prevent broadcasting a $routeUpdate.
  • When the route is actually left unchanged, $locationChangeSuccess is never triggered which causes the next route update to be prevented.
  • If in the same digest cycle there was another update request, this time wishing to reload, the event handler would cancel that reload.

    app.run(['$rootScope', '$route', '$location', '$timeout', function ($rootScope, $route, $location, $timeout) {
        ['url', 'path'].forEach(function (method) {
            var original = $location[method];
            var requestId = 0;
            $location[method] = function (param, reload) {
                // getter
                if (!param) return original.call($location);
    
                # only last call allowed to do things in one digest cycle
                var currentRequestId = ++requestId;
                if (reload === false) {
                    var lastRoute = $route.current;
                    // intercept ONLY the next $locateChangeSuccess
                    var un = $rootScope.$on('$locationChangeSuccess', function () {
                        un();
                        if (requestId !== currentRequestId) return;
    
                        if (!angular.equals($route.current.params, lastRoute.params)) {
                            // this should always be broadcast when params change
                            $rootScope.$broadcast('$routeUpdate');
                        }
                        var current = $route.current;
                        $route.current = lastRoute;
                        // make a route change to the previous route work
                        $timeout(function() {
                            if (requestId !== currentRequestId) return;
                            $route.current = current;
                        });
                    });
                    // if it didn't fire for some reason, don't intercept the next one
                    $timeout(un);
                }
                return original.call($location, param);
            };
        });
    }]);
    

Add following inside head tag

  <script type="text/javascript">
    angular.element(document.getElementsByTagName('head')).append(angular.element('<base href="' + window.location.pathname + '" />'));
  </script>

This will prevent the reload.


Since about version 1.2, you can use $location.replace():

$location.path('/items');
$location.replace();

참고URL : https://stackoverflow.com/questions/14974271/can-you-change-a-path-without-reloading-the-controller-in-angularjs

반응형