AngularJS UI Router - ubah url tanpa memuat ulang status

134

Saat ini proyek kami menggunakan default $routeProvider, dan saya menggunakan "retasan" ini, untuk berubah urltanpa memuat ulang halaman:

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

dan masuk controller

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

Saya sedang berpikir untuk mengganti routeProviderdengan ui-routeruntuk rute bersarang, tetapi tidak dapat menemukan di ini ui-router.

Apakah mungkin - melakukan hal yang sama angular-ui-router?

Mengapa saya membutuhkan ini? Izinkan saya menjelaskan dengan sebuah contoh:
Rute untuk membuat kategori baru /category/new setelah clickingpada SAVE I show success-alertdan saya ingin mengubah rute /category/newke /caterogy/23(23 - adalah id dari item baru yang disimpan dalam db)

vuliad
sumber
1
di ui-router Anda tidak perlu menentukan URL untuk setiap negara bagian, Anda dapat menavigasi dari satu negara bagian ke negara lain tanpa mengubah URL
Jonathan de M.
Anda ingin memperbarui seluruh URL atau hanya jalur pencarian? Saya sedang mencari solusi memperbarui jalur pencarian dan menemukannya di sini: stackoverflow.com/questions/21425378/…
Florian Loch
@ johnathan Benarkah? Saya ingin melakukannya, hanya menampilkan satu URL, tetapi $urlRouterProvider.otherwisetampaknya beroperasi pada URL, bukan negara. Hmmm, mungkin saya bisa hidup dengan 2 URL, atau menemukan cara lain untuk menunjukkan bahwa itu adalah URL yang tidak valid.
Mawg mengatakan mengembalikan Monica

Jawaban:

164

Cukup Anda bisa menggunakan $state.transitionTo bukan $state.go . $state.go panggilan secara $state.transitionTo internal tetapi secara otomatis mengatur opsi { location: true, inherit: true, relative: $state.$current, notify: true } . Anda dapat menelepon $state.transitionTo dan mengatur notify: false . Sebagai contoh:

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

dapat digantikan oleh

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

Sunting: Seperti yang disarankan oleh fracz itu bisa saja:

$state.go('.detail', {id: newId}, {notify: false}) 
rezKesh
sumber
16
Bukankah itu "tetap url ketika memuat ulang keadaan" bukannya "ubah url tanpa memuat kembali keadaan"?
Peter Hedberg
25
bagi saya itu bekerja dengan yang berikut: $state.transitionTo('.detail', {id: newId}, { location: true, inherit: true, relative: $state.$current, notify: false }) jadi pada dasarnya set notify ke false dan location to true
Arjen de Vries
6
@ArjendeVries Ya itu berfungsi seperti yang diharapkan, tetapi saya menemukan perilaku yang tidak terduga. Setelah bermain-main dengan banyak pemanggilan metode transisiTo (tanpa memuat ulang) ketika Anda akhirnya pindah ke keadaan baru (mis. Url), itu memulai kembali pengendali lama.
Premchandra Singh
2
@Premchandra Singh: Memiliki masalah yang sama di sini. Ketika meninggalkan negara, pengontrol lama akan diinisialisasi lagi. Saya mendapatkan masalah yang sama dengan solusi yang diterima dari wiherek. Lihat juga di sini github.com/angular-ui/ui-router/issues/64
martinoss
15
Bahkan lebih sederhana: $state.go('.detail', {id: newId}, {notify: false}).
fracz
48

Oke, terpecahkan :) Router UI Sudut memiliki metode baru ini, $ urlRouterProvider.deferIntercept () https://github.com/angular-ui/ui-router/issues/64

pada dasarnya turun ke ini:

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

Saya pikir metode ini saat ini hanya termasuk dalam versi master dari router ui sudut, yang dengan parameter opsional (yang bagus juga, btw). Itu perlu dikloning dan dibangun dari sumber dengan

grunt build

Dokumen juga dapat diakses dari sumbernya, melalui

grunt ngdocs

(mereka dibangun ke direktori / situs) // info lebih lanjut di README.MD

Tampaknya ada cara lain untuk melakukan ini, dengan parameter dinamis (yang saya belum pernah gunakan). Banyak kredit untuk nateabele.


Sebagai sidenote, berikut adalah parameter opsional di $ stateProvider Angular UI Router, yang saya gunakan dalam kombinasi dengan yang di atas:

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

}]);

apa yang dilakukan adalah memungkinkan untuk menyelesaikan suatu keadaan, bahkan jika salah satu params hilang. SEO adalah satu tujuan, keterbacaan yang lain.

Dalam contoh di atas, saya ingin doorsSingle menjadi parameter yang diperlukan. Tidak jelas bagaimana mendefinisikannya. Ini berfungsi ok dengan beberapa parameter opsional, jadi tidak terlalu masalah. Diskusi ada di sini https://github.com/angular-ui/ui-router/pull/1032#issuecomment-49196090

wiherek
sumber
Tampaknya parameter opsional Anda tidak berfungsi. Error: Both params and url specicified in state 'state'. Dikatakan dalam dokumen bahwa ini adalah penggunaan yang tidak benar juga. Agak mengecewakan.
Rhys van der Waerden
1
Apakah Anda membangun dari master? Harap dicatat bahwa pada titik ketika saya menambahkan solusi, parameter opsional hanya disertakan dalam versi yang harus dibangun secara manual dari sumber. ini tidak akan disertakan dalam rilis sampai v.0.3 keluar.
wiherek
1
Hanya catatan singkat. Berkat nateabele, parameter opsional tersedia di v0.2.11 yang baru saja dirilis beberapa hari yang lalu.
rich97
ini sangat membingungkan: Bagaimana cara saya menggunakan $ urlRouterProvider.deferIntercept (); sehingga saya dapat memperbarui params tanpa memuat ulang controller? Ini tidak benar-benar menunjukkan kepada saya itu. Dalam menjalankan fungsi Anda dapat mengevaluasi pernyataan if untuk menyinkronkan atau tidak menyinkronkan tetapi semua saya harus bekerja dengan adalah url lama dan baru. Bagaimana saya tahu bahwa ini adalah contoh di mana semua yang ingin saya lakukan adalah memperbarui params dengan hanya dua url untuk bekerja dengan? Akankah logikanya .... (jika keadaan lama dan keadaan baru sama maka jangan memuat ulang pengontrol?) Saya bingung.
btm1
benar, saya pikir ini digunakan dengan negara-negara bersarang, saya tidak ingin memuat ulang kondisi anak, sehingga mencegat. Saya pikir sekarang saya lebih suka melakukannya dengan pandangan penargetan absolut, dan mendefinisikan pandangan tentang keadaan yang saya tahu tidak akan berubah. Bagaimanapun, ini masih bagus. Anda mendapatkan url lengkap, yaitu Anda dapat menebak kondisi dari url. Dan juga parameter kueri dan jalur, dll. Jika Anda membuat aplikasi Anda di sekitar status dan url, itu adalah satu-satunya informasi. Dalam menjalankan blok Anda juga dapat mengakses layanan dll. Apakah itu menjawab pertanyaan Anda?
wiherek
16

Setelah menghabiskan banyak waktu dengan masalah ini, Inilah yang saya dapatkan

$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
});
Pankaj Parkar
sumber
Solusi lain terkait dengan penyedia rute. Solusi ini berfungsi untuk kasus ini, seperti milik saya, di mana saya menggunakan $ stateProvider dan tidak menggunakan $ routeProvider.
eeejay
@eeejay pada dasarnya pertanyaan ui-routerhanya ditanyakan , bagaimana Anda bisa mengatakan itu, solusi lain bekerja $routerProvider, $routeProvider& $stateProvidersangat berbeda dalam arsitektur ..
Pankaj Parkar
bagaimana jika kita tidak ingin tombol kembali bekerja dengan ini? Maksudku, tekan kembali pergi ke negara / url sebelumnya, tidak ke negara yang sama dengan params yang berbeda
gaurav5430
Saya kira, dengan peramban solusi ini kembali tidak akan berfungsi, karena kami mengubah keadaan tanpa memberi tahu tentang hal itu ke ui-router
Pankaj Parkar
2
Saya mencoba ini, dan sepertinya $onInit()dipanggil pada komponen saya setiap kali saya menggunakan $state.go. Sepertinya tidak 100% baik untuk saya.
Carrm
8

Panggilan

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

masih akan memuat ulang controller.

Untuk menghindarinya, Anda harus mendeklarasikan parameter sebagai dinamis:

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

Maka Anda bahkan tidak perlu notify, hanya menelepon

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

cukup. Neato!

Dari dokumentasi :

Saat dynamicini true, perubahan nilai parameter tidak akan menyebabkan status dimasukkan / keluar. Resolusi tidak akan diambil kembali, juga tampilan tidak akan dimuat ulang.

[...]

Ini berguna untuk membangun UI tempat komponen memperbarui sendiri ketika nilai param berubah.

Moritz Ringler
sumber
7

Pengaturan ini memecahkan masalah berikut untuk saya:

  • Kontroler pelatihan tidak dipanggil dua kali saat memperbarui url dari .../ke.../123
  • Kontroler pelatihan tidak dipanggil lagi saat bernavigasi ke negara lain

Konfigurasi keadaan

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

Meminta status (dari pengontrol lain)

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

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

Pengendali Pelatihan

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 ...
}   
martinoss
sumber
Saya mencoba menggunakan strategi yang sama tetapi controller saya tidak pernah mendapatkan nilai trainigId dari halaman edit, apa pun yang mungkin saya lewatkan di sana? Saya mencoba mengakses halaman edit langsung dari URL dan dengan menggunakan ui-sref. Kode saya persis seperti milik Anda.
Nikhil Bhandari
Ini bekerja untuk saya, dan sejauh ini merupakan solusi yang paling jelas
OoDeLally
3

Jika Anda hanya perlu mengubah url tetapi mencegah perubahan status:

Ubah lokasi dengan (tambahkan. Ganti jika Anda ingin mengganti dalam riwayat):

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

Cegah pengalihan ke negara Anda:

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

saya melakukan ini tetapi dahulu di versi: v0.2.10 UI-router seperti ini ::

$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
      },
      '[email protected]': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/viewVoucherContainer.html'),
        controller: 'viewVoucherCtrl',
        reloadOnSearch: false
      },
      '[email protected]': {
        templateUrl: Url.resolveTemplateUrl('voucher/partial/voucherContainer.html'),
        controller: 'voucherCtrl',
        reloadOnSearch: false
      }
    },
    reloadOnSearch: false
  })
Sheelpriy
sumber
0

Coba sesuatu seperti ini

$state.go($state.$current.name, {... $state.params, 'key': newValue}, {notify: false})
Eyad Farra
sumber
-6

Saya tidak berpikir Anda perlu ui-router sama sekali untuk ini. Dokumentasi yang tersedia untuk layanan $ location mengatakan pada paragraf pertama, "... perubahan ke $ location dicerminkan ke dalam bilah alamat browser." Kemudian berlanjut untuk berkata, "Apa yang tidak dilakukannya? Itu tidak menyebabkan satu halaman penuh dimuat kembali ketika URL browser diubah."

Jadi, dengan itu dalam pikiran, mengapa tidak hanya mengubah $ location.path (karena metode ini adalah pengambil dan penyetel) dengan sesuatu seperti berikut:

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

The dokumentasi catatan bahwa jalan harus selalu dimulai dengan garis miring, tapi ini akan menambahkannya jika itu hilang.

Xaniff
sumber
Ketika saya menggunakan ui-router, dan menggunakan $location.path(URL_PATH), halaman itu secara otomatis merender ulang ?!
Kousha
ya, itu merender ulang. Saya mencoba dengan event.preventDefault () di $ locationChangeStart, itu juga tidak berfungsi - yaitu menghentikan negara dari rendering ulang, tetapi juga mencegah url memperbarui.
wiherek