Hai Anders, pertanyaan bagus!
Saya memiliki kasus penggunaan yang hampir sama dengan Anda, dan ingin melakukan hal yang sama! Pencarian pengguna> dapatkan hasil> Pengguna menavigasi ke hasil> Pengguna menavigasi kembali> BOOM dengan cepat kembali ke hasil , tetapi Anda tidak ingin menyimpan hasil spesifik yang dinavigasi oleh pengguna.
tl; dr
Anda perlu memiliki kelas yang mengimplementasikan RouteReuseStrategy
dan menyediakan strategi Anda di ngModule
. Jika Anda ingin mengubah kapan rute disimpan, ubah shouldDetach
fungsinya. Saat kembali true
, Angular menyimpan rute tersebut. Jika Anda ingin mengubah saat rute dipasang, ubah shouldAttach
fungsinya. Saat shouldAttach
mengembalikan nilai true, Angular akan menggunakan rute yang disimpan sebagai pengganti rute yang diminta. Ini Plunker untuk Anda mainkan.
Tentang RouteReuseStrategy
Dengan menanyakan pertanyaan ini, Anda sudah memahami bahwa RouteReuseStrategy memungkinkan Anda memberi tahu Angular untuk tidak menghancurkan komponen, tetapi sebenarnya menyimpannya untuk dirender ulang di lain waktu. Itu keren karena memungkinkan:
- Panggilan server menurun
- Kecepatan ditingkatkan
- DAN komponen membuat, secara default, dalam keadaan yang sama seperti saat ditinggalkan
Yang terakhir itu penting jika Anda ingin, misalnya, meninggalkan halaman sementara meskipun pengguna telah memasukkan banyak teks ke dalamnya. Aplikasi perusahaan akan menyukai fitur ini karena terlalu banyaknya formulir!
Inilah yang saya pikirkan untuk menyelesaikan masalah. Seperti yang Anda katakan, Anda perlu memanfaatkan yang RouteReuseStrategy
ditawarkan oleh @ angular / router di versi 3.4.1 dan lebih tinggi.
MELAKUKAN
Pertama, Pastikan proyek Anda memiliki @ angular / router versi 3.4.1 atau lebih tinggi.
Selanjutnya , buat file yang akan menampung kelas Anda yang mengimplementasikan RouteReuseStrategy
. Saya menelepon milik saya reuse-strategy.ts
dan meletakkannya di /app
folder untuk diamankan. Untuk saat ini, kelas ini akan terlihat seperti:
import { RouteReuseStrategy } from '@angular/router';
export class CustomReuseStrategy implements RouteReuseStrategy {
}
(jangan khawatir tentang kesalahan TypeScript Anda, kami akan menyelesaikan semuanya)
Selesaikan dasar dengan menyediakan kelas untuk Anda app.module
. Perhatikan bahwa Anda belum ditulis CustomReuseStrategy
, tetapi harus pergi ke depan dan import
dari reuse-strategy.ts
semua sama. Jugaimport { RouteReuseStrategy } from '@angular/router';
@NgModule({
[...],
providers: [
{provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
]
)}
export class AppModule {
}
Bagian terakhir adalah menulis kelas yang akan mengontrol apakah rute dilepas, disimpan, diambil, dan dipasang kembali. Sebelum kita sampai ke copy / paste lama , saya akan melakukan penjelasan singkat tentang mekanika di sini, seperti yang saya pahami. Referensi kode di bawah ini untuk metode yang saya jelaskan, dan tentu saja, ada banyak dokumentasi dalam kode tersebut .
- Saat Anda menavigasi,
shouldReuseRoute
tembak. Yang ini agak aneh bagi saya, tetapi jika kembali true
, maka itu benar-benar menggunakan kembali rute Anda saat ini dan tidak ada metode lain yang diaktifkan. Saya hanya mengembalikan false jika pengguna menavigasi keluar.
- Jika
shouldReuseRoute
kembali false
, shouldDetach
tembak. shouldDetach
menentukan apakah Anda ingin menyimpan rute atau tidak, dan mengembalikan boolean
indikasi sebanyak itu. Di sinilah Anda harus memutuskan untuk menyimpan / tidak menyimpan jalur , yang akan saya lakukan dengan memeriksa larik jalur yang ingin Anda simpan route.routeConfig.path
, dan mengembalikan false jika path
tidak ada dalam larik.
- Jika
shouldDetach
pengembalian true
, store
dipecat, yang merupakan kesempatan bagi Anda untuk menyimpan informasi apa pun yang Anda inginkan tentang rute tersebut. Apa pun yang Anda lakukan, Anda harus menyimpan DetachedRouteHandle
karena itulah yang digunakan Angular untuk mengidentifikasi komponen yang Anda simpan nanti. Di bawah, saya menyimpan the DetachedRouteHandle
dan the ActivatedRouteSnapshot
ke dalam variabel lokal untuk kelas saya.
Jadi, kita telah melihat logika untuk penyimpanan, tapi bagaimana dengan menavigasi ke sebuah komponen? Bagaimana Angular memutuskan untuk mencegat navigasi Anda dan meletakkan yang disimpan di tempatnya?
- Sekali lagi, setelah
shouldReuseRoute
kembali false
, shouldAttach
berjalan, yang merupakan kesempatan Anda untuk mengetahui apakah Anda ingin membuat ulang atau menggunakan komponen dalam memori. Jika Anda ingin menggunakan kembali komponen yang disimpan, kembalikan true
dan Anda sudah siap!
- Sekarang sudut akan meminta Anda, "yang komponen yang Anda ingin kami untuk digunakan?", Yang Anda akan menunjukkan dengan kembali bahwa komponen
DetachedRouteHandle
dari retrieve
.
Cukup banyak logika yang Anda butuhkan! Dalam kode untuk reuse-strategy.ts
, di bawah ini, saya juga meninggalkan Anda fungsi bagus yang akan membandingkan dua objek. Saya menggunakannya untuk membandingkan dengan masa depan ini route.params
dan route.queryParams
dengan disimpan seseorang. Jika semuanya cocok, saya ingin menggunakan komponen yang disimpan daripada membuat yang baru. Tapi bagaimana Anda melakukannya, itu terserah Anda!
reuse-strategy.ts
/**
* reuse-strategy.ts
* by corbfon 1/6/17
*/
import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle } from '@angular/router';
/** Interface for object which can store both:
* An ActivatedRouteSnapshot, which is useful for determining whether or not you should attach a route (see this.shouldAttach)
* A DetachedRouteHandle, which is offered up by this.retrieve, in the case that you do want to attach the stored route
*/
interface RouteStorageObject {
snapshot: ActivatedRouteSnapshot;
handle: DetachedRouteHandle;
}
export class CustomReuseStrategy implements RouteReuseStrategy {
/**
* Object which will store RouteStorageObjects indexed by keys
* The keys will all be a path (as in route.routeConfig.path)
* This allows us to see if we've got a route stored for the requested path
*/
storedRoutes: { [key: string]: RouteStorageObject } = {};
/**
* Decides when the route should be stored
* If the route should be stored, I believe the boolean is indicating to a controller whether or not to fire this.store
* _When_ it is called though does not particularly matter, just know that this determines whether or not we store the route
* An idea of what to do here: check the route.routeConfig.path to see if it is a path you would like to store
* @param route This is, at least as I understand it, the route that the user is currently on, and we would like to know if we want to store it
* @returns boolean indicating that we want to (true) or do not want to (false) store that route
*/
shouldDetach(route: ActivatedRouteSnapshot): boolean {
let detach: boolean = true;
console.log("detaching", route, "return: ", detach);
return detach;
}
/**
* Constructs object of type `RouteStorageObject` to store, and then stores it for later attachment
* @param route This is stored for later comparison to requested routes, see `this.shouldAttach`
* @param handle Later to be retrieved by this.retrieve, and offered up to whatever controller is using this class
*/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
let storedRoute: RouteStorageObject = {
snapshot: route,
handle: handle
};
console.log( "store:", storedRoute, "into: ", this.storedRoutes );
// routes are stored by path - the key is the path name, and the handle is stored under it so that you can only ever have one object stored for a single path
this.storedRoutes[route.routeConfig.path] = storedRoute;
}
/**
* Determines whether or not there is a stored route and, if there is, whether or not it should be rendered in place of requested route
* @param route The route the user requested
* @returns boolean indicating whether or not to render the stored route
*/
shouldAttach(route: ActivatedRouteSnapshot): boolean {
// this will be true if the route has been stored before
let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[route.routeConfig.path];
// this decides whether the route already stored should be rendered in place of the requested route, and is the return value
// at this point we already know that the paths match because the storedResults key is the route.routeConfig.path
// so, if the route.params and route.queryParams also match, then we should reuse the component
if (canAttach) {
let willAttach: boolean = true;
console.log("param comparison:");
console.log(this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params));
console.log("query param comparison");
console.log(this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams));
let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params);
let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams);
console.log("deciding to attach...", route, "does it match?", this.storedRoutes[route.routeConfig.path].snapshot, "return: ", paramsMatch && queryParamsMatch);
return paramsMatch && queryParamsMatch;
} else {
return false;
}
}
/**
* Finds the locally stored instance of the requested route, if it exists, and returns it
* @param route New route the user has requested
* @returns DetachedRouteHandle object which can be used to render the component
*/
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
// return null if the path does not have a routerConfig OR if there is no stored route for that routerConfig
if (!route.routeConfig || !this.storedRoutes[route.routeConfig.path]) return null;
console.log("retrieving", "return: ", this.storedRoutes[route.routeConfig.path]);
/** returns handle when the route.routeConfig.path is already stored */
return this.storedRoutes[route.routeConfig.path].handle;
}
/**
* Determines whether or not the current route should be reused
* @param future The route the user is going to, as triggered by the router
* @param curr The route the user is currently on
* @returns boolean basically indicating true if the user intends to leave the current route
*/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
console.log("deciding to reuse", "future", future.routeConfig, "current", curr.routeConfig, "return: ", future.routeConfig === curr.routeConfig);
return future.routeConfig === curr.routeConfig;
}
/**
* This nasty bugger finds out whether the objects are _traditionally_ equal to each other, like you might assume someone else would have put this function in vanilla JS already
* One thing to note is that it uses coercive comparison (==) on properties which both objects have, not strict comparison (===)
* Another important note is that the method only tells you if `compare` has all equal parameters to `base`, not the other way around
* @param base The base object which you would like to compare another object to
* @param compare The object to compare to base
* @returns boolean indicating whether or not the objects have all the same properties and those properties are ==
*/
private compareObjects(base: any, compare: any): boolean {
// loop through all properties in base object
for (let baseProperty in base) {
// determine if comparrison object has that property, if not: return false
if (compare.hasOwnProperty(baseProperty)) {
switch(typeof base[baseProperty]) {
// if one is object and other is not: return false
// if they are both objects, recursively call this comparison function
case 'object':
if ( typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty]) ) { return false; } break;
// if one is function and other is not: return false
// if both are functions, compare function.toString() results
case 'function':
if ( typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString() ) { return false; } break;
// otherwise, see if they are equal using coercive comparison
default:
if ( base[baseProperty] != compare[baseProperty] ) { return false; }
}
} else {
return false;
}
}
// returns true only after false HAS NOT BEEN returned through all loops
return true;
}
}
Tingkah laku
Implementasi ini menyimpan setiap rute unik yang dikunjungi pengguna di router tepat satu kali. Ini akan terus menambah komponen yang disimpan dalam memori selama sesi pengguna di situs. Jika Anda ingin membatasi rute yang Anda simpan, tempat untuk melakukannya adalah shouldDetach
metodenya. Ini mengontrol rute mana yang Anda simpan.
Contoh
Misalnya pengguna Anda menelusuri sesuatu dari beranda, yang mengarahkan mereka ke jalur search/:term
, yang mungkin tampak seperti www.yourwebsite.com/search/thingsearchedfor
. Halaman pencarian berisi banyak hasil pencarian. Anda ingin menyimpan rute ini, jika mereka ingin kembali ke sana! Sekarang mereka mengklik hasil pencarian dan dinavigasi ke view/:resultId
, yang tidak ingin Anda simpan, karena mereka mungkin hanya ada sekali. Dengan implementasi di atas, saya hanya akan mengubah shouldDetach
metode! Seperti inilah tampilannya:
Pertama mari kita buat array jalur yang ingin kita simpan.
private acceptedRoutes: string[] = ["search/:term"];
sekarang, di dalam shouldDetach
kita dapat memeriksa route.routeConfig.path
terhadap array kita.
shouldDetach(route: ActivatedRouteSnapshot): boolean {
// check to see if the route's path is in our acceptedRoutes array
if (this.acceptedRoutes.indexOf(route.routeConfig.path) > -1) {
console.log("detaching", route);
return true;
} else {
return false; // will be "view/:resultId" when user navigates to result
}
}
Karena Angular hanya akan menyimpan satu instance dari sebuah rute, penyimpanan ini akan menjadi ringan, dan kami hanya akan menyimpan komponen yang terletak di search/:term
dan tidak semua yang lain!
Tautan Tambahan
Meskipun belum banyak dokumentasi di luar sana, berikut adalah beberapa tautan ke apa yang ada:
Angular Docs: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrategy-class.html
Artikel Intro: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx
Implementasi default nativescript-angular dari RouteReuseStrategy : https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts
Observable
. Komponen harus berlanggananObservable
selamangOnInit
kait siklus hidup. Kemudian Anda akan dapat memberi tahu komponen, dariReuseRouteStrategy
, bahwa ia baru saja dipasang dan komponen dapat mengubah statusnya sesuai kebutuhan.Jangan terintimidasi oleh jawaban yang diterima, ini sangat mudah. Berikut jawaban cepat yang Anda butuhkan. Saya akan merekomendasikan setidaknya membaca jawaban yang diterima, karena penuh dengan detail yang luar biasa.
Solusi ini tidak melakukan perbandingan parameter apa pun seperti jawaban yang diterima tetapi akan berfungsi dengan baik untuk menyimpan sekumpulan rute.
impor app.module.ts:
shared / routing.ts:
sumber
Selain jawaban yang diterima (oleh Corbfon) dan penjelasan Chris Fremgen yang lebih pendek dan lugas, saya ingin menambahkan cara penanganan rute yang lebih fleksibel yang harus menggunakan strategi penggunaan kembali.
Kedua jawaban tersebut menyimpan rute yang ingin kita cache dalam array dan kemudian memeriksa apakah jalur rute saat ini ada dalam array atau tidak. Pemeriksaan ini dilakukan dengan
shouldDetach
metode.Saya menemukan pendekatan ini tidak fleksibel karena jika kita ingin mengubah nama rute, kita perlu mengingat untuk juga mengubah nama rute di
CustomReuseStrategy
kelas kita . Kami mungkin lupa untuk mengubahnya atau beberapa pengembang lain di tim kami mungkin memutuskan untuk mengubah nama rute bahkan tanpa mengetahui tentang keberadaanRouteReuseStrategy
.Alih-alih menyimpan rute yang ingin kita cache dalam sebuah array, kita bisa menandainya secara langsung dalam
RouterModule
menggunakandata
objek. Dengan cara ini meskipun kami mengubah nama rute, strategi penggunaan kembali akan tetap diterapkan.Dan kemudian dalam
shouldDetach
metode kami memanfaatkannya.sumber
Untuk menggunakan strategi Chris Fremgen dengan modul yang dimuat lambat, ubah kelas CustomReuseStrategy menjadi berikut:
terakhir, dalam file perutean modul fitur Anda, tentukan kunci Anda:
Info selengkapnya di sini .
sumber
route.data["key"]
untuk membangun tanpa kesalahan. Tetapi masalah yang saya alami adalah bahwa saya memiliki komponen rute + yang digunakan di dua tempat berbeda.1. sample/list/item
dan2. product/id/sample/list/item
ketika saya pertama kali memuat salah satu jalur itu memuat baik-baik saja tetapi yang lain melempar kesalahan yang dilampirkan kembali karena saya menyimpan berdasarkanlist/item
Jadi pekerjaan saya adalah saya menggandakan rute dan membuat beberapa perubahan ke jalur url tetapi menampilkan komponen yang sama. Tidak yakin apakah ada solusi lain untuk itu.Implementasi lain yang lebih valid, lengkap, dan dapat digunakan kembali. Yang satu ini mendukung modul yang dimuat lambat sebagai @ Uğur Dinç dan mengintegrasikan tanda data rute @Davor. Peningkatan terbaik adalah pembuatan otomatis pengenal (hampir) unik berdasarkan jalur absolut halaman. Dengan cara ini Anda tidak perlu mendefinisikannya sendiri di setiap halaman.
Tandai halaman mana saja yang ingin Anda cache pengaturannya
reuseRoute: true
. Ini akan digunakan dalamshouldDetach
metode.Ini adalah implementasi strategi paling sederhana, tanpa membandingkan parameter kueri.
Yang ini juga membandingkan parameter kueri.
compareObjects
memiliki sedikit peningkatan dibandingkan versi @Corbfon: perulangan melalui properti dari kedua basis dan membandingkan objek. Ingatlah bahwa Anda dapat menggunakan implementasi eksternal dan lebih andal sepertiisEqual
metode lodash .Jika Anda memiliki cara terbaik untuk menghasilkan kunci unik, komentar jawaban saya, saya akan memperbarui kode.
Terima kasih untuk semua orang yang telah membagikan solusinya.
sumber
Semua solusi yang disebutkan entah bagaimana tidak cukup dalam kasus kami. Kami memiliki aplikasi bisnis yang lebih kecil dengan:
Persyaratan kami:
Contoh sederhana dari rute kami:
Strategi penggunaan kembali:
sumber
berikut ini adalah pekerjaan! referensi: https://www.cnblogs.com/lovesangel/p/7853364.html
sumber
_routerState
._routerState
menyebabkan sesuatu yang berbahaya?deleteRouteSnapshot
?Saya menghadapi masalah ini dalam menerapkan strategi penggunaan ulang rute kustom:
Jadi saya menulis perpustakaan untuk memecahkan masalah ini. Library menyediakan layanan dan dekorator untuk kaitkan / lepas dan menggunakan komponen rute untuk menyimpan rute yang terlepas, bukan jalur rute.
Contoh:
Perpustakaan: https://www.npmjs.com/package/ng-cache-route-reuse
sumber