Bộ định tuyến UI AngularJS - thay đổi url mà không cần tải lại trạng thái


134

Hiện tại dự án của chúng tôi đang sử dụng mặc định $routeProvidervà tôi đang sử dụng "hack" này để thay đổi urlmà không cần tải lại trang:

services.service('$locationEx', ['$location', '$route', '$rootScope', function($location, $route, $rootScope) {
    $location.skipReload = function () {
        var lastRoute = $route.current;
        var un = $rootScope.$on('$locationChangeSuccess', function () {
            $route.current = lastRoute;
            un();
        });
        return $location;
    };
    return $location;
}]);

và trong controller

$locationEx.skipReload().path("/category/" + $scope.model.id).replace();

Tôi đang nghĩ đến việc thay thế routeProviderbằng ui-routercác tuyến đường lồng nhau, nhưng không thể tìm thấy điều này trong ui-router.

Có thể - làm tương tự với angular-ui-router?

Tại sao tôi cần cái này? Hãy để tôi giải thích với một ví dụ:
Tuyến để tạo danh mục mới là /category/new sau khi clickingTIẾT KIỆM tôi hiển thị success-alertvà tôi muốn thay đổi tuyến /category/newthành /caterogy/23(23 - là id của mục mới được lưu trữ trong db)


1
trong ui-router bạn không phải xác định URL cho mỗi trạng thái, bạn có thể điều hướng từ trạng thái này sang trạng thái khác mà không thay đổi URL
Jonathan de M.

Bạn có muốn cập nhật toàn bộ URL hoặc chỉ đường dẫn tìm kiếm không? Tôi đã tìm kiếm một giải pháp cập nhật đường dẫn tìm kiếm và tìm thấy nó ở đây: stackoverflow.com/questions/21425378/ Kẻ
Florian Loch

@johnathan Thật sao? Tôi rất thích làm điều đó, chỉ hiển thị một URL duy nhất, nhưng $urlRouterProvider.otherwisedường như hoạt động trên một URL, không phải là trạng thái. Hmmm, có lẽ tôi có thể sống với 2 URL hoặc tìm một số cách khác để chỉ ra rằng đó là một URL không hợp lệ.
Mawg nói rằng phục hồi Monica

Câu trả lời:


164

Đơn giản là bạn có thể sử dụng $state.transitionTo thay vì $state.go . $state.go gọi $state.transitionTo nội bộ nhưng tự động đặt tùy chọn thành { location: true, inherit: true, relative: $state.$current, notify: true } . Bạn có thể gọi $state.transitionTo và thiết lập notify: false . Ví dụ:

$state.go('.detail', {id: newId}) 

có thể được thay thế bởi

$state.transitionTo('.detail', {id: newId}, {
    location: true,
    inherit: true,
    relative: $state.$current,
    notify: false
})

Chỉnh sửa: Theo đề xuất của fracz, nó có thể chỉ đơn giản là:

$state.go('.detail', {id: newId}, {notify: false}) 

16
Không phải là "giữ url khi tải lại trạng thái" thay vì "thay đổi url mà không tải lại trạng thái"?
Peter Hedberg

25
Đối với tôi, nó hoạt động với các mục sau: $state.transitionTo('.detail', {id: newId}, { location: true, inherit: true, relative: $state.$current, notify: false }) vì vậy về cơ bản, đặt thông báo thành sai và vị trí thành đúng
Arjen de Vries

6
@ArjendeVries Có nó hoạt động như mong đợi nhưng tôi thấy một hành vi không mong muốn. Sau khi chơi xung quanh với nhiều lệnh gọi phương thức chuyển đổi (không tải lại) khi cuối cùng bạn chuyển sang trạng thái mới (ví dụ: url), nó sẽ khởi chạy lại bộ điều khiển cũ.
Premframra Singh

2
@Premframra Singh: Có cùng một vấn đề ở đây. Khi rời trạng thái, bộ điều khiển cũ sẽ được khởi tạo lại. Tôi gặp vấn đề tương tự với giải pháp được chấp nhận từ wiherek. Xem thêm tại đây github.com/angular-ui/ui-router/issues/64
martinoss 28/12/14

15
Thậm chí đơn giản hơn : $state.go('.detail', {id: newId}, {notify: false}).
fracz

48

Ok, đã giải quyết :) Bộ định tuyến UI góc có phương thức mới này, $ urlRouterProvider.deferIntercept () https://github.com/angular-ui/ui-router/issues/64

về cơ bản nó đi xuống này:

angular.module('myApp', [ui.router])
  .config(['$urlRouterProvider', function ($urlRouterProvider) {
    $urlRouterProvider.deferIntercept();
  }])
  // then define the interception
  .run(['$rootScope', '$urlRouter', '$location', '$state', function ($rootScope, $urlRouter, $location, $state) {
    $rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl) {
      // Prevent $urlRouter's default handler from firing
      e.preventDefault();

      /** 
       * provide conditions on when to 
       * sync change in $location.path() with state reload.
       * I use $location and $state as examples, but
       * You can do any logic
       * before syncing OR stop syncing all together.
       */

      if ($state.current.name !== 'main.exampleState' || newUrl === 'http://some.url' || oldUrl !=='https://another.url') {
        // your stuff
        $urlRouter.sync();
      } else {
        // don't sync
      }
    });
    // Configures $urlRouter's listener *after* your custom listener
    $urlRouter.listen();
  }]);

Tôi nghĩ rằng phương pháp này hiện đang chỉ bao gồm trong tổng thể phiên bản của router ui góc, là với các thông số tùy chọn (trong đó là tốt đẹp quá, btw). Nó cần được nhân bản và xây dựng từ nguồn với

grunt build

Các tài liệu cũng có thể truy cập được từ nguồn

grunt ngdocs

(chúng được tích hợp vào thư mục / site) // thêm thông tin trong README.MD

Dường như có một cách khác để làm điều này, bằng các tham số động (mà tôi chưa sử dụng). Nhiều tín dụng cho natablesele.


Là một sidenote, đây là các tham số tùy chọn trong $ stateProvider của Angular UI Router, mà tôi đã sử dụng kết hợp với ở trên:

angular.module('myApp').config(['$stateProvider', function ($stateProvider) {    

  $stateProvider
    .state('main.doorsList', {
      url: 'doors',
      controller: DoorsListCtrl,
      resolve: DoorsListCtrl.resolve,
      templateUrl: '/modules/doors/doors-list.html'
    })
    .state('main.doorsSingle', {
      url: 'doors/:doorsSingle/:doorsDetail',
      params: {
        // as of today, it was unclear how to define a required parameter (more below)
        doorsSingle: {value: null},
        doorsDetail: {value: null}
      },
      controller: DoorsSingleCtrl,
      resolve: DoorsSingleCtrl.resolve,
      templateUrl: '/modules/doors/doors-single.html'
    });

}]);

những gì nó làm là cho phép giải quyết một trạng thái, ngay cả khi một trong các thông số bị thiếu. SEO là một mục đích, dễ đọc khác.

Trong ví dụ trên, tôi muốn cửa ra vào là một tham số bắt buộc. Không rõ làm thế nào để xác định những người. Nó hoạt động tốt với nhiều tham số tùy chọn, vì vậy không thực sự là một vấn đề. Cuộc thảo luận ở đây https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090


Có vẻ như điều tham số tùy chọn của bạn không hoạt động. Error: Both params and url specicified in state 'state'. Nó nói trong các tài liệu rằng đây là sử dụng không hợp lệ quá. Một chút thất vọng.
Rhys van der Waerden

1
Bạn đã xây dựng từ chủ? Xin lưu ý rằng tại thời điểm tôi thêm giải pháp, các tham số tùy chọn chỉ được bao gồm trong phiên bản phải được xây dựng thủ công từ nguồn. điều này sẽ không được bao gồm trong bản phát hành cho đến khi v.0.3 không còn nữa.
wiherek

1
Chỉ là một ghi chú nhanh. Nhờ có natispele, các tham số tùy chọn có sẵn trong v0.2.11 vừa được phát hành vài ngày trước.
rich97

điều này rất khó hiểu: Làm cách nào để sử dụng $ urlRouterProvider.deferIntercept (); để tôi có thể cập nhật các thông số mà không cần tải lại bộ điều khiển? Điều này không thực sự cho tôi thấy điều đó. Trong chức năng chạy, bạn có thể đánh giá một câu lệnh if để đồng bộ hóa hoặc không đồng bộ hóa nhưng tất cả những gì tôi phải làm việc là url cũ và mới. Làm thế nào để tôi biết rằng đây là một ví dụ mà tất cả những gì tôi muốn làm là cập nhật các thông số chỉ với hai url để làm việc? Logic sẽ là .... (nếu trạng thái cũ và trạng thái mới giống nhau thì không tải lại bộ điều khiển?) Tôi bối rối.
btm1

đúng, tôi nghĩ trường hợp sử dụng này là với các trạng thái lồng nhau, tôi không muốn tải lại trạng thái con, do đó đã bị chặn. Tôi nghĩ bây giờ tôi muốn làm điều đó với các chế độ xem nhắm mục tiêu tuyệt đối và xác định chế độ xem theo trạng thái mà tôi biết sẽ không thay đổi. Dù sao, điều này vẫn còn tốt. Bạn nhận được các url đầy đủ, đó là bạn có thể đoán trạng thái từ url. Và cả các tham số đường dẫn và truy vấn, v.v. Nếu bạn tạo ứng dụng của mình xung quanh các trạng thái và url, đó là một thông tin. Trong khối chạy, bạn cũng có thể truy cập các dịch vụ, vv Điều đó có trả lời câu hỏi của bạn không?
wiherek

16

Sau khi dành nhiều thời gian cho vấn đề này, đây là những gì tôi đã làm việc

$state.go('stateName',params,{
    // prevent the events onStart and onSuccess from firing
    notify:false,
    // prevent reload of the current state
    reload:false, 
    // replace the last record when changing the params so you don't hit the back button and get old params
    location:'replace', 
    // inherit the current params on the url
    inherit:true
});

Các giải pháp khác có liên quan đến nhà cung cấp tuyến đường. Giải pháp này hoạt động cho trường hợp, như của tôi, nơi tôi đang sử dụng $ stateProvider và không sử dụng $ routeProvider.
eeejay

@eeejay về cơ bản câu hỏi chỉ được hỏi ui-router, làm thế nào bạn có thể nói rằng, các giải pháp khác đang hoạt động $routerProvider, $routeProvider$stateProviderhoàn toàn khác biệt về kiến ​​trúc ..
Pankaj Parkar 20/07/2016

Điều gì xảy ra nếu chúng ta không muốn nút quay lại hoạt động với điều này? Ý tôi là, khi quay lại, hãy chuyển sang trạng thái / url trước đó, không chuyển sang trạng thái tương tự với các thông số khác nhau
gaurav5430

Tôi đoán, bởi giải pháp này, trình duyệt trở lại sẽ không hoạt động, vì chúng tôi đang thay đổi trạng thái mà không cho biết về nó thành bộ định tuyến
Pankaj Parkar

2
Tôi đã thử điều này và có vẻ như $onInit()nó được gọi trên thành phần của tôi mỗi khi tôi sử dụng $state.go. Có vẻ như 100% ok với tôi.
Carrm

8

Gọi điện thoại

$state.go($state.current, {myParam: newValue}, {notify: false});

vẫn sẽ tải lại bộ điều khiển.

Để tránh nó, bạn phải khai báo tham số là động:

$stateProvider.state({
    name: 'myState',
    url: '/my_state?myParam',
    params: {
        myParam: {
          dynamic: true,
        }
    },
    ...
});

Sau đó, bạn thậm chí không cần notify, chỉ cần gọi

$state.go($state.current, {myParam: newValue})

đủ. Neato!

Từ tài liệu :

Khi dynamictrue, thay đổi giá trị tham số sẽ không khiến trạng thái được nhập / thoát. Các độ phân giải sẽ không được tải lại, cũng như các khung nhìn sẽ được tải lại.

[...]

Điều này có thể hữu ích để xây dựng UI nơi thành phần tự cập nhật khi giá trị param thay đổi.


7

Thiết lập này đã giải quyết các vấn đề sau cho tôi:

  • Bộ điều khiển đào tạo không được gọi hai lần khi cập nhật url từ .../đến.../123
  • Bộ điều khiển đào tạo sẽ không được gọi lại khi điều hướng đến trạng thái khác

Cấu hình nhà nước

state('training', {
    abstract: true,
    url: '/training',
    templateUrl: 'partials/training.html',
    controller: 'TrainingController'
}).
state('training.edit', {
    url: '/:trainingId'
}).
state('training.new', {
    url: '/{trainingId}',
    // Optional Parameter
    params: {
        trainingId: null
    }
})

Gọi các trạng thái (từ bất kỳ bộ điều khiển nào khác)

$scope.editTraining = function (training) {
    $state.go('training.edit', { trainingId: training.id });
};

$scope.newTraining = function () {
    $state.go('training.new', { });
};

Kiểm soát viên đào tạo

var newTraining;

if (!!!$state.params.trainingId) {

    // new      

    newTraining = // create new training ...

    // Update the URL without reloading the controller
    $state.go('training.edit',
        {
            trainingId : newTraining.id
        },
        {
            location: 'replace', //  update url and replace
            inherit: false,
            notify: false
        });     

} else {

    // edit

    // load existing training ...
}   

Tôi đã cố gắng sử dụng một chiến lược tương tự nhưng bộ điều khiển của tôi không bao giờ nhận được giá trị của trainigId từ trang chỉnh sửa, bất cứ điều gì tôi có thể thiếu ở đó? Tôi đã thử truy cập trang chỉnh sửa trực tiếp từ URL và bằng cách sử dụng ui-sref. Mã của tôi chính xác như của bạn.
Nikhil Bhandari

Điều này làm việc cho tôi, và cho đến nay là giải pháp rõ ràng nhất
OoDeLally

3

Nếu bạn chỉ cần thay đổi url nhưng ngăn trạng thái thay đổi:

Thay đổi vị trí bằng (thêm .replace nếu bạn muốn thay thế trong lịch sử):

this.$location.path([Your path]).replace();

Ngăn chặn chuyển hướng đến trạng thái của bạn:

$transitions.onBefore({}, function($transition$) {
 if ($transition$.$to().name === '[state name]') {
   return false;
 }
});

2

tôi đã làm điều này nhưng từ lâu trong phiên bản: v0.2.10 của bộ định tuyến UI giống như thế này ::

$stateProvider
  .state(
    'home', {
      url: '/home',
      views: {
        '': {
          templateUrl: Url.resolveTemplateUrl('shared/partial/main.html'),
          controller: 'mainCtrl'
        },
      }
    })
  .state('home.login', {
    url: '/login',
    templateUrl: Url.resolveTemplateUrl('authentication/partial/login.html'),
    controller: 'authenticationCtrl'
  })
  .state('home.logout', {
    url: '/logout/:state',
    controller: 'authenticationCtrl'
  })
  .state('home.reservationChart', {
    url: '/reservations/?vw',
    views: {
      '': {
        templateUrl: Url.resolveTemplateUrl('reservationChart/partial/reservationChartContainer.html'),
        controller: 'reservationChartCtrl',
        reloadOnSearch: false
      },
      'viewVoucher@home.reservationChart': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'),
        controller: 'viewVoucherCtrl',
        reloadOnSearch: false
      },
      'addEditVoucher@home.reservationChart': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'),
        controller: 'voucherCtrl',
        reloadOnSearch: false
      }
    },
    reloadOnSearch: false
  })

0

Hãy thử một cái gì đó như thế này

$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})

-6

Tôi không nghĩ bạn cần ui-router cho việc này. Tài liệu có sẵn cho dịch vụ định vị $ nói trong đoạn đầu tiên, "... thay đổi đối với vị trí $ được phản ánh vào thanh địa chỉ trình duyệt." Nó tiếp tục sau đó để nói, "Nó không làm gì? Nó không gây ra tải lại toàn bộ trang khi URL trình duyệt bị thay đổi."

Vì vậy, với ý nghĩ đó, tại sao không chỉ đơn giản là thay đổi $ location.path (vì phương thức vừa là getter vừa là setter) với nội dung như sau:

var newPath = IdFromService;
$location.path(newPath);

Các tài liệu ghi chú rằng con đường nên luôn luôn bắt đầu với một dấu gạch chéo, nhưng điều này sẽ thêm nó nếu nó mất tích.


Khi tôi sử dụng ui-routervà sử dụng $location.path(URL_PATH), trang sẽ tự động hiển thị lại?!
Kousha

Vâng, nó tái hiện. Tôi đã thử với event.preventDefault () trên $ locationChangeStart, điều đó cũng không hoạt động - tức là nó dừng trạng thái hiển thị lại, nhưng nó cũng ngăn url cập nhật.
wiherek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.