क्या आप AngularJS में कंट्रोलर को फिर से लोड किए बिना एक रास्ता बदल सकते हैं?


191

यह पहले पूछा गया है, और उत्तरों से यह अच्छा नहीं लगता है। मैं इस नमूना कोड के साथ विचार करना चाहता हूं ...

मेरा ऐप सेवा प्रदान करने वाली वर्तमान वस्तु को लोड करता है। कई नियंत्रक हैं जो आइटम को फिर से लोड किए बिना आइटम डेटा में हेरफेर करते हैं।

मेरे नियंत्रक आइटम को फिर से लोड करेंगे यदि यह अभी तक सेट नहीं है, अन्यथा, यह नियंत्रकों के बीच सेवा से वर्तमान में लोड की गई वस्तु का उपयोग करेगा।

समस्या : मैं 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);
}

संकल्प।

स्थिति : स्वीकार किए गए उत्तर में अनुशंसित खोज क्वेरी का उपयोग करने से मुझे मुख्य नियंत्रक को फिर से लोड किए बिना अलग-अलग यूआरएल प्रदान करने की अनुमति मिली।

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


यह उत्तर मिला: stackoverflow.com/a/18551525/632088 - यह reloadOnSearch का उपयोग करता है: गलत, लेकिन यह खोजकर्ता में url के अपडेट की भी जाँच करता है (जैसे यदि उपयोगकर्ता बटन पर क्लिक करता है और url बदलता है)
robert

जवाबों:


115

आप की तरह 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 को दृश्य को फिर से लोड नहीं करना है।


उसके लिए धन्यवाद। मैं यह पता लगाने की कोशिश कर रहा हूं कि मैं फिर नियंत्रक को कहां बदलूंगा।
कोडर 1

समझ गया। मेरे दृश्य सरणी में एक कंट्रोलर परम जोड़ा गया, फिर निर्देशन में जोड़ा ng-controller="view.controller"गया ng-include
कोडर 1

आप itemCtrlInit में $ location.search () पर एक घड़ी बनाएंगे और खोज पैरामीटर के आधार पर, $ गुंजाइश.view.template अपडेट करेंगे। और फिर उन टेम्पलेट को कुछ इस तरह से लपेटा जा सकता है <div ng-controller="itemFooCtrl">... your template content...</div>। संपादित करें: आपको हल करते हुए देखकर अच्छा लगा, यदि आपने अपने प्रश्न को हल किया, तो शायद यह बहुत अच्छा होगा कि शायद आप jsFiddle के साथ।
एंडर्स एकडल

जब मैं वहां 100% रहूँगा तो इसे अपडेट करूँगा। मैं बच्चे नियंत्रकों और एनजी-क्लिक में गुंजाइश के साथ कुछ quirks कर रहा हूँ। अगर मैं सीधे foo से लोड करता हूं, तो foo स्कोप के भीतर ng-click निष्पादित होता है। मैं तब बार पर क्लिक करता हूं, और उस टेम्पलेट में एनजी-क्लिक पेरेंट स्कोप में निष्पादित हो जाते हैं।
कोडर 1

1
ध्यान दिया जाना चाहिए कि आपको वास्तव में अपने URLS को पुन: स्वरूपित करने की आवश्यकता नहीं है। यह मामला तब हो सकता है जब रिपॉजिट पोस्ट किया गया था, लेकिन अब प्रलेखन ( docs.angularjs.org/api/ngRoute.$routeProvider ) अब कहता है: [reloadOnSearch = true] - [बूलियन =} - केवल $ स्थान होने पर रूट पुनः लोड करें। खोज () या $ location.hash () परिवर्तन।
Rhys van der Waerden

93

यदि आपको पथ बदलने की आवश्यकता है, तो इसे अपने ऐप फ़ाइल में .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 पर जाता है ।


6
महान समाधान। वैसे भी ब्राउज़र के बैक बटन को "सम्मान" देने के लिए यह है?
मार्क बी

6
ध्यान रखें कि यह समाधान पुराने मार्ग को बनाए रखता है, और यदि आप $ मार्ग की मांग करते हैं। वर्तमान में, आपको वर्तमान मार्ग मिलेगा, वर्तमान नहीं। नहीं तो यह बहुत कम हैक है।
jornare

4
बस यह कहना चाहता था कि मूल समाधान "EvanWinstanley" द्वारा अकस्मात यहाँ पोस्ट किया गया था: github.com/angular/angular.js/issues/1699#issuecomment-34841248
Chip2424

2
मैं थोड़ी देर के लिए इस समाधान का उपयोग कर रहा हूं और बस ध्यान दिया है कि कुछ पथ परिवर्तनों पर, एक सही तरीके से पारित होने पर भी झूठ को फिर से लोड करता है। क्या किसी और के पास इस तरह का कोई मुद्दा था?
विल हिचकॉक

2
मुझे $timeout(un, 200);रिटर्न स्टेटमेंट से पहले जोड़ना था क्योंकि कभी-कभी $locationChangeSuccessआग नहीं लगती थी apply(और जब वास्तव में आवश्यकता होती है तो मार्ग नहीं बदला गया था)। मेरा समाधान पथ के आह्वान के बाद चीजों को साफ करता है (..., झूठा)
बर्फानी

17

क्यों न एनजी-नियंत्रक को एक स्तर ऊंचा रखा जाए,

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

और मार्ग में नियंत्रक सेट न करें,

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

इससे मेरा काम बनता है।


महान टिप, यह मेरे परिदृश्य के लिए भी पूरी तरह से काम करता है! आपका बहुत बहुत धन्यवाद! :)
डेवोनकोड

4
यह एक महान "रूसियों ने पेंसिल का उपयोग किया" उत्तर है, मेरे लिए काम करता है :)
इनोल्स्को

1
मैंने भी जल्दी ही बोल दिया। इस वर्कअराउंड में समस्या है कि यदि आपको कंट्रोलर में $ मार्गप्रेम से मानों को लोड करने की आवश्यकता है, तो वे लोड नहीं होंगे, डिफ़ॉल्ट रूप से {}
inolasco

@inolasco: काम करता है अगर आप अपने $ मार्गप्रेम को $ $ मार्ग में पढ़ते हैं। असफल हैंडलर। अधिक बार नहीं, यह शायद आप वैसे भी क्या चाहते हैं। सिर्फ कंट्रोलर में पढ़ने से आपको केवल उस URL के मान मिलेंगे जो मूल रूप से लोड किए गए थे।
plong0

@ plong0 अच्छी बात है, काम करना चाहिए। मेरे मामले में, हालांकि, मुझे केवल URL मान प्राप्त करने की आवश्यकता है, जब कुछ लोड को प्रारंभ करने के लिए, पेज लोड पर नियंत्रक लोड होता है। मैंने $ locationChangeSuccess का उपयोग करके विगॉन द्वारा प्रस्तावित समाधान का उपयोग करके समाप्त किया, लेकिन आपका एक और वैध विकल्प भी है।
इनलास्को

12

उन लोगों के लिए जिन्हें कंट्रोलर लोड के बिना पथ () परिवर्तन की आवश्यकता है - यहां प्लगइन है: https://github.com/anglibs/angular-location-update

उपयोग:

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

Https://stackoverflow.com/a/24102139/1751321 पर आधारित

पुनश्च यह समाधान https://stackoverflow.com/a/24102139/1751321 में पथ के बाद बग (, असत्य) कहा जाता है - यह ब्राउज़र नेविगेशन को तोड़ देगा / आगे तक पथ (, सत्य) कहलाता है


10

हालांकि यह पोस्ट पुरानी है और इसमें एक उत्तर स्वीकार किया गया है, reloadOnSeach = false का उपयोग करके हममें से उन लोगों के लिए समस्या हल नहीं होती है, जिन्हें वास्तविक पथ को बदलने की आवश्यकता है, न कि केवल परम। यहाँ एक आसान उपाय पर विचार किया गया है:

एनजी-व्यू के बजाय एनजी-इनका उपयोग करें और टेम्पलेट में अपने नियंत्रक को असाइन करें।

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

केवल डाउन-साइड यह है कि आप रिज़ॉल्यूशन विशेषता का उपयोग नहीं कर सकते, लेकिन यह बहुत आसान है। इसके अलावा, आपको नियंत्रक की स्थिति का प्रबंधन करना होगा, जैसे तर्क $ $ मार्ग पर आधारित होते हैं क्योंकि नियंत्रक के भीतर मार्ग बदलता है और इसी तरह यूरेनियम बदलता है।

यहाँ एक उदाहरण है: http://plnkr.co/edit/WtAOm59CFcjafMmxBVOP?p=preview


1
यकीन नहीं है कि बैक बटन plnkr में सही ढंग से व्यवहार करेगा ... जैसा कि मैंने उल्लेख किया है, आपको मार्ग परिवर्तन के रूप में अपने दम पर कुछ चीजों का प्रबंधन करना होगा .. यह सबसे कोड-योग्य समाधान नहीं है, लेकिन यह काम करता है
लुकस

2

मैं इस समाधान का उपयोग करता हूं

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

यह दृष्टिकोण बैक बटन कार्यक्षमता को बनाए रखता है, और आपके पास हमेशा टेम्पलेट में वर्तमान मार्गप्रमाण है, कुछ अन्य दृष्टिकोणों के विपरीत जो मैंने देखा है।


1

ऊपर GitHub एक सहित, मेरे परिदृश्य के लिए कुछ समस्याएँ थीं और ब्राउज़र से बैक बटन या डायरेक्ट url परिवर्तन नियंत्रक को पुनः लोड कर रहा था, जो मुझे पसंद नहीं था। मैं अंत में निम्नलिखित दृष्टिकोण के साथ गया:

1. रूट परिभाषाओं में एक संपत्ति को परिभाषित करें, उन मार्गों के लिए 'noReload' कहा जाता है जहां आप नहीं चाहते कि रूट परिवर्तन पर नियंत्रक पुनः लोड हो।

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

2. अपने मॉड्यूल के रन फ़ंक्शन में, उन मार्गों के लिए जांचने वाले तर्क रखें। यह तभी पुनः लोड को रोकेगा जब noReloadकि trueऔर पिछला मार्ग नियंत्रक समान हो।

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. अंत में, कंट्रोलर में $ pathUpdate ईवेंट को सुनें ताकि आप रूट पैरामीटर बदलने पर आपको जो कुछ भी करने की ज़रूरत है वह कर सकें।

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

    //(...)
}

ध्यान रखें कि इस दृष्टिकोण के साथ, आप नियंत्रक में चीजों को नहीं बदलते हैं और फिर उसी के अनुसार पथ को अपडेट करते हैं। यह दूसरा तरीका है। आप पहले पथ बदलते हैं, फिर मार्ग पैरामीटर बदलने पर नियंत्रक में चीजों को बदलने के लिए $ $ रूटअपडेट की घटना को सुनते हैं।

यह चीजों को सरल और सुसंगत रखता है क्योंकि आप एक ही तर्क का उपयोग कर सकते हैं जब आप बस पथ बदलते हैं (लेकिन यदि आप चाहें तो महंगे $ http अनुरोधों के बिना) और जब आप ब्राउज़र को पूरी तरह से पुनः लोड करते हैं।


1

पुन: लोड किए बिना पथ बदलने का सरल तरीका है

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

URL बदलने के लिए इस कोड का उपयोग करें, पृष्ठ पुनर्निर्देशित नहीं किया जाएगा

दूसरा पैरामीटर "झूठा" बहुत महत्वपूर्ण है।

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


0

यहाँ मेरा फुलर समाधान है जो कुछ चीजों को हल करता है @Vigrond और @rahilwazir चूक गया:

  • जब खोज पैरामेट्स बदले गए, तो यह प्रसारण को रोक देगा $routeUpdate
  • जब मार्ग को वास्तव में अपरिवर्तित छोड़ दिया $locationChangeSuccessजाता है , तो कभी ट्रिगर नहीं किया जाता है जो अगले मार्ग अद्यतन को रोकता है।
  • यदि उसी पाचन चक्र में एक और अद्यतन अनुरोध था, तो इस बार पुनः लोड करने के इच्छुक, ईवेंट हैंडलर उस पुनः लोड को रद्द कर देगा।

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

pathअंत में टाइपो होना चाहिए param, यह भी हैश के साथ काम नहीं करता है, और मेरे पते के बार में यूआरएल अपडेट नहीं करता है
deltree

फिक्स्ड। हम्म अजीब बात है यह मेरे लिए हालांकि html5 यूआरएल पर काम करता है। अभी भी किसी को मैं अनुमान लगाने में मदद कर सकता हूं ...
ओड नीव

0

सिर टैग के अंदर निम्नलिखित जोड़ें

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

यह पुनः लोड को रोक देगा।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.