कोणीय 2 में विशिष्ट मार्गों के लिए रूटर्यूजस्ट्रीट को कैसे लागू करना चाहिए


114

मेरे पास एक कोणीय 2 मॉड्यूल है जिसमें मैंने राउटिंग लागू किया है और यह नेविगेट करने पर राज्यों को संग्रहीत करना चाहते हैं। उपयोगकर्ता के लिए सक्षम होना चाहिए: 1. एक खोज का उपयोग कर दस्तावेजों के लिए खोज 2. परिणामों में से एक पर नेविगेट करें। 3. खोज के लिए वापस नेविगेट करें - सर्वर के साथ संचार किए बिना

यह RouteReuseStrategy सहित संभव है। सवाल यह है: मैं कैसे लागू करूं कि दस्तावेज़ संग्रहीत नहीं किया जाना चाहिए?

इसलिए मार्ग पथ "दस्तावेज़" का राज्य संग्रहीत किया जाना चाहिए और मार्ग पथ "दस्तावेज़ /: आईडी" राज्य को संग्रहीत नहीं किया जाना चाहिए?

जवाबों:


209

हे एंडर्स, महान सवाल!

मुझे आपके समान लगभग उपयोग का मामला मिल गया है, और वही काम करना चाहता था! उपयोगकर्ता खोज> परिणाम प्राप्त करें> उपयोगकर्ता परिणाम के लिए नेविगेट करता है> उपयोगकर्ता वापस नेविगेट करता है> BOOM परिणामों में तेजी से धधकते हैं , लेकिन आप उस विशिष्ट परिणाम को संग्रहीत नहीं करना चाहते हैं जिसे उपयोगकर्ता ने नेविगेट किया हो।

tl; डॉ

आप एक वर्ग है कि लागू करता है RouteReuseStrategyऔर में अपनी रणनीति प्रदान करने की आवश्यकता है ngModule। यदि आप रूट संग्रहीत होने पर संशोधित करना चाहते हैं, तो shouldDetachफ़ंक्शन को संशोधित करें । जब यह वापस आता है true, तो कोणीय मार्ग को संग्रहीत करता है। यदि आप रूट संलग्न होने पर संशोधित करना चाहते हैं, तो shouldAttachफ़ंक्शन को संशोधित करें । जब shouldAttachसही हो जाता है, तो कोणीय अनुरोधित मार्ग के स्थान पर संग्रहीत मार्ग का उपयोग करेगा। यहाँ आप के साथ खेलने के लिए एक प्लंकर है

RouteReuseStrategy के बारे में

इस प्रश्न को पूछने से, आप पहले से ही समझ जाते हैं कि RouteReuseStrategy आपको एक घटक को नष्ट करने के लिए कोणीय को बताने की अनुमति नहीं देता है , लेकिन वास्तव में इसे बाद की तारीख में फिर से प्रस्तुत करने के लिए सहेजना है। यह अच्छा है क्योंकि यह अनुमति देता है:

  • सर्वर कॉल में कमी
  • वृद्धि की गति
  • और घटक रेंडर करता है, डिफ़ॉल्ट रूप से, उसी स्थिति में इसे छोड़ दिया गया था

यह अंतिम महत्वपूर्ण है यदि आप चाहें, तो एक पृष्ठ को अस्थायी रूप से छोड़ दें, भले ही उपयोगकर्ता ने इसमें बहुत सारे पाठ दर्ज किए हों । रूपों की अत्यधिक मात्रा के कारण एंटरप्राइज एप्लिकेशन को यह सुविधा पसंद आएगी !

यही मैं समस्या को हल करने के लिए आया हूं। जैसा कि आपने कहा, आपको RouteReuseStrategy3.4.1 और उच्चतर संस्करणों में @ कोणीय / राउटर द्वारा ऑफ़र किए गए का उपयोग करने की आवश्यकता है ।

करने के लिए

पहले सुनिश्चित करें कि आपकी परियोजना में @ कोणीय / राउटर संस्करण 3.4.1 या उच्चतर है।

इसके बाद , एक फ़ाइल बनाएं जो आपकी कक्षा को लागू करेगीRouteReuseStrategy । मैंने उसे बुलाया reuse-strategy.tsऔर उसे /appसुरक्षित रखने के लिए फ़ोल्डर में रख दिया। अभी के लिए, इस वर्ग को इस तरह दिखना चाहिए:

import { RouteReuseStrategy } from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {
}

(अपनी टाइपस्क्रिप्ट त्रुटियों के बारे में चिंता न करें, हम सब कुछ हल करने वाले हैं)

अपने वर्ग को कक्षा प्रदान करके जमीनी कार्य को पूरा करें app.module। ध्यान दें कि आपने अभी तक नहीं लिखा है CustomReuseStrategy, लेकिन importइसे आगे बढ़ना चाहिए और यह reuse-strategy.tsसभी समान है। भीimport { RouteReuseStrategy } from '@angular/router';

@NgModule({
    [...],
    providers: [
        {provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
    ]
)}
export class AppModule {
}

अंतिम टुकड़ा वह वर्ग लिख रहा है जो नियंत्रित करेगा कि मार्गों को अलग किया जाए, संग्रहीत किया जाए, पुनर्प्राप्त किया जाए और फिर से जोड़ा जाए। इससे पहले कि हम पुरानी कॉपी / पेस्ट करें , मैं यहाँ यांत्रिकी की एक छोटी व्याख्या करूँगा, जैसा कि मैं उन्हें समझता हूँ। नीचे बताए गए तरीकों के लिए कोड का संदर्भ दें, और निश्चित रूप से, बहुत सारे दस्तावेज हैं कोड में

  1. जब आप नेविगेट करते हैं, shouldReuseRoute आग। यह मेरे लिए थोड़ा अजीब है, लेकिन अगर यह वापस लौटता है true, तो यह वास्तव में उस मार्ग का पुन: उपयोग करता है जो आप वर्तमान में कर रहे हैं और अन्य विधियों में से कोई भी निकाल नहीं है। यदि उपयोगकर्ता नेविगेट कर रहा है तो मैं बस गलत वापस करता हूं।
  2. अगर shouldReuseRouteलौटता है false, shouldDetachआग। shouldDetachनिर्धारित करता है कि आप मार्ग को संग्रहीत करना चाहते हैं या नहीं, और एक booleanसंकेत जितना लौटाता है । यह वह जगह है जहां आपको पथों को संग्रहीत करने / न करने का निर्णय लेना चाहिए , जो मैं उन रास्तों की एक सरणी की जांच करके करूंगा, जिन्हें आप संग्रहीत करना चाहते हैंroute.routeConfig.path , और यदि pathसरणी में मौजूद नहीं है, तो वापस लौटना होगा ।
  3. यदि shouldDetachरिटर्न true, storeनिकाल दिया जाता है, जो आपके लिए मार्ग के बारे में जो भी जानकारी आप चाहते हैं उसे संग्रहीत करने का एक अवसर है। आप जो कुछ भी करते हैं, आपको उसे संग्रहीत करने की आवश्यकता होगी, DetachedRouteHandleक्योंकि बाद में आपके संग्रहीत घटक की पहचान करने के लिए कोणीय का उपयोग करता है। नीचे, मैं दोनों को संग्रहीत करता हूंDetachedRouteHandle और ActivatedRouteSnapshotमेरी कक्षा के लिए एक चर स्थानीय में।

इसलिए, हमने स्टोरेज के लिए लॉजिक देखा है, लेकिन इसमें नेविगेट करने के बारे में क्या एक कंपोनेंट पर ? एंगुलर आपके नेविगेशन को इंटरसेप्ट करने और स्टोर किए गए को अपनी जगह पर रखने का फैसला कैसे करता है?

  1. फिर से, shouldReuseRouteवापस आ गया है false,shouldAttach चलाता है, जो यह पता लगाने का मौका है कि आप स्मृति में घटक को फिर से बनाना या उपयोग करना चाहते हैं या नहीं। यदि आप एक संग्रहीत घटक का पुन: उपयोग करना चाहते हैं, तो वापस लौटें trueऔर आप अपने रास्ते पर अच्छी तरह से आ जाएँ!
  2. अब कोणीय के लिए कहेगा, "जो घटक आप हमें उपयोग करना चाहते हैं?", तो आप उस घटक के वापस लौट कर यह संकेत मिलेगा जो DetachedRouteHandleसे retrieve

आप की जरूरत है कि सभी तर्क बहुत सुंदर है! reuse-strategy.tsनीचे दिए गए कोड में , मैंने आपको एक निफ्टी फ़ंक्शन भी छोड़ा है जो दो वस्तुओं की तुलना करेगा। मैं इसका उपयोग भविष्य के मार्ग की तुलना करने route.paramsऔर route.queryParamsसंग्रहीत के साथ करने के लिए करता हूं । यदि वे सभी मेल खाते हैं, तो मैं एक नया उत्पादन करने के बजाय संग्रहीत घटक का उपयोग करना चाहता हूं। लेकिन आप इसे कैसे करते हैं आपके ऊपर है!

उपयोग किए जाने वाले 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;
    }
}

व्यवहार

यह कार्यान्वयन प्रत्येक अद्वितीय मार्ग को संग्रहीत करता है जो उपयोगकर्ता राउटर पर ठीक एक बार आता है। यह साइट पर उपयोगकर्ता के पूरे सत्र में मेमोरी में संग्रहीत घटकों को जोड़ना जारी रखेगा। यदि आप अपने द्वारा संग्रहीत मार्गों को सीमित करना चाहते हैं, तो ऐसा करने का स्थान shouldDetachविधि है। यह नियंत्रित करता है कि आप किन मार्गों को बचाते हैं।

उदाहरण

मान लें कि आपका उपयोगकर्ता मुखपृष्ठ से कुछ खोजता है, जो उन्हें उस मार्ग पर ले जाता है search/:term, जो दिखाई दे सकता है www.yourwebsite.com/search/thingsearchedfor। खोज पृष्ठ में खोज परिणामों का एक समूह होता है। यदि आप उस पर वापस आना चाहते हैं, तो आप इस मार्ग को संग्रहीत करना चाहेंगे! अब वे एक खोज परिणाम पर क्लिक करते हैं और नेविगेट करने के लिए मिलते हैं view/:resultId, जिसे आप स्टोर नहीं करना चाहते हैं, यह देखते हुए कि वे शायद केवल एक बार ही होंगे। जगह में उपरोक्त कार्यान्वयन के साथ, मैं बस shouldDetachविधि बदलूंगा! यहाँ यह है कि यह कैसा दिख सकता है:

सबसे पहले हम उन रास्तों की एक सरणी बनाते हैं जिन्हें हम स्टोर करना चाहते हैं।

private acceptedRoutes: string[] = ["search/:term"];

अब, shouldDetachहम route.routeConfig.pathअपने ऐरे के खिलाफ जाँच कर सकते हैं ।

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

क्योंकि कोणीय केवल एक मार्ग का एक उदाहरण संग्रहीत करेगा , यह भंडारण हल्का होगा, और हम केवल search/:termअन्य सभी पर स्थित घटक का भंडारण करेंगे !

अतिरिक्त लिंक

हालाँकि अभी तक वहाँ बहुत सारे दस्तावेज नहीं हैं, यहाँ कुछ लिंक मौजूद हैं जो मौजूद हैं:

कोणीय डॉक्स: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrategy-class.html

इंट्रो आलेख: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx

की nativescript-कोणीय के डिफ़ॉल्ट कार्यान्वयन RouteReuseStrategy : https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts


2
@ शहाहिन ने एक उदाहरण जोड़ा है, जो मेरे वर्तमान कार्यान्वयन में शामिल सटीक कोड है!
कॉर्बफॉन

1
@Corbfon मैंने आधिकारिक github पेज पर एक मुद्दा भी खोला है: github.com/angular/angular/issues/13869
Tjaart van der Walt

2
क्या एक संग्रहीत मार्ग को फिर से सक्रिय करने के लिए इसे फिर से दर्ज करने के लिए एनिमेशन प्राप्त करना है?
जिंदर सिद्धू

2
ReuseRouteStrategy आपके कंपोनेंट को राउटर में वापस सौंप देगी, इसलिए यह उस स्थिति में होगा जब आप इसे छोड़ चुके हैं। यदि आप अटैचमेंट पर प्रतिक्रिया करने के लिए कंपोनेंट (ओं) को पसंद करते हैं, तो आप एक ऐसी सर्विस का उपयोग कर सकते हैं, जो अ Observable। घटक को जीवनचक्र हुक के Observableदौरान सदस्यता लेनी चाहिए ngOnInit। तब आप घटक से ReuseRouteStrategy, यह बताने में सक्षम होंगे कि यह सिर्फ संलग्न किया गया है और घटक अपने राज्य को फिट के रूप में संशोधित कर सकता है।
Corbfon

1
@AndersGramMygind यदि मेरा उत्तर आपके द्वारा प्रस्तावित प्रश्न का उत्तर प्रदान करता है, तो क्या आप इसे उत्तर के रूप में चिह्नित करेंगे?
कॉर्बफॉन

45

स्वीकार किए गए उत्तर से भयभीत न हों, यह बहुत सीधा है। यहाँ एक त्वरित उत्तर है कि आपको क्या चाहिए। मैं कम से कम स्वीकृत उत्तर को पढ़ने की सलाह दूंगा, क्योंकि यह बहुत विस्तार से भरा हुआ है।

यह समाधान स्वीकृत उत्तर की तरह कोई भी पैरामीटर तुलना नहीं करता है, लेकिन यह मार्गों के एक सेट को संग्रहीत करने के लिए ठीक काम करेगा।

app.module.ts आयात:

import { RouteReuseStrategy } from '@angular/router';
import { CustomReuseStrategy, Routing } from './shared/routing';

@NgModule({
//...
providers: [
    { provide: RouteReuseStrategy, useClass: CustomReuseStrategy },
  ]})

साझा / routing.ts:

export class CustomReuseStrategy implements RouteReuseStrategy {
 routesToCache: string[] = ["dashboard"];
 storedRouteHandles = new Map<string, DetachedRouteHandle>();

 // Decides if the route should be stored
 shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return this.routesToCache.indexOf(route.routeConfig.path) > -1;
 }

 //Store the information for the route we're destructing
 store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.storedRouteHandles.set(route.routeConfig.path, handle);
 }

//Return true if we have a stored route object for the next route
 shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return this.storedRouteHandles.has(route.routeConfig.path);
 }

 //If we returned true in shouldAttach(), now return the actual route data for restoration
 retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    return this.storedRouteHandles.get(route.routeConfig.path);
 }

 //Reuse the route if we're going to and from the same route
 shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
 }
}

1
क्या यह उन मार्गों के लिए भी काम करेगा जो आलसी लोड किए गए हैं?
ब्लूपार्क

@bluePearl नीचे दिए गए उत्तर की जाँच करें
क्रिस Fremgen

2
pathConfig अलग-अलग मार्गों के लिए अशक्त है, इसलिए rReuseRoute को हमेशा वही वापस लौटना चाहिए जो वांछित व्यवहार नहीं है
Gil Epshtain

19

स्वीकृत उत्तर (कॉर्बफॉन द्वारा) और क्रिस फ्रेमजेन की छोटी और अधिक स्पष्ट व्याख्या के अलावा, मैं उन मार्गों को संभालने के लिए अधिक लचीला तरीका जोड़ना चाहता हूं जिन्हें पुन: उपयोग की रणनीति का उपयोग करना चाहिए।

दोनों उत्तर उन मार्गों को संग्रहीत करते हैं जिन्हें हम किसी सरणी में कैश करना चाहते हैं और फिर जांचें कि वर्तमान मार्ग पथ सरणी में है या नहीं। इसमें जांच की जाती हैshouldDetach विधि से की जाती है।

मुझे यह दृष्टिकोण अनम्य लगता है क्योंकि अगर हम उस मार्ग का नाम बदलना चाहते हैं जिसे हमें अपनी CustomReuseStrategyकक्षा में मार्ग का नाम बदलने के लिए भी याद रखना होगा । हम या तो इसे बदलना भूल सकते हैं या हमारी टीम के कुछ अन्य डेवलपर मार्ग के नाम को बदलने का फैसला कर सकते हैं, जिनके अस्तित्व के बारे में भी नहीं जानते हैंRouteReuseStrategy

उन मार्गों को संग्रहीत करने के बजाय जिन्हें हम किसी सरणी में कैश करना चाहते हैं, हम उन्हें ऑब्जेक्ट RouterModuleका उपयोग करके सीधे चिह्नित कर सकते हैं data। इस तरह से अगर हम रूट का नाम बदलते हैं, तो भी पुन: उपयोग की रणनीति लागू की जाएगी।

{
  path: 'route-name-i-can-change',
  component: TestComponent,
  data: {
    reuseRoute: true
  }
}

और फिर shouldDetachविधि में हम उसका उपयोग करते हैं।

shouldDetach(route: ActivatedRouteSnapshot): boolean {
  return route.data.reuseRoute === true;
}

1
अच्छा समाधान। यह वास्तव में सिर्फ एक साधारण ध्वज के साथ मानक कोणीय मार्ग के पुन: उपयोग की रणनीति में बेक किया जाना चाहिए जैसे आपने आवेदन किया है।
MIP1983

बहुत बढ़िया जवाब। आपका बहुत बहुत धन्यवाद!
क्लुडीयोमातिसारग

14

Lazily लोड मॉड्यूल के साथ क्रिस Fremgen की रणनीति का उपयोग करने के लिए, CustomReuseStrategy वर्ग को निम्न में संशोधित करें:

import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {
  routesToCache: string[] = ["company"];
  storedRouteHandles = new Map<string, DetachedRouteHandle>();

  // Decides if the route should be stored
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
     return this.routesToCache.indexOf(route.data["key"]) > -1;
  }

  //Store the information for the route we're destructing
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
     this.storedRouteHandles.set(route.data["key"], handle);
  }

  //Return true if we have a stored route object for the next route
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
     return this.storedRouteHandles.has(route.data["key"]);
  }

  //If we returned true in shouldAttach(), now return the actual route data for restoration
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
     return this.storedRouteHandles.get(route.data["key"]);
  }

  //Reuse the route if we're going to and from the same route
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
     return future.routeConfig === curr.routeConfig;
  }
}

अंत में, आपके फ़ीचर मॉड्यूल की राउटिंग फ़ाइलों में, अपनी कुंजियाँ निर्धारित करें:

{ path: '', component: CompanyComponent, children: [
    {path: '', component: CompanyListComponent, data: {key: "company"}},
    {path: ':companyID', component: CompanyDetailComponent},
]}

अधिक जानकारी यहाँ


1
इसे जोड़ने के लिए धन्यवाद! मैं इसे एक कोशिश देने के लिए मिला है। यह उन मुद्दों से निपटने वाले कुछ बाल मार्ग को भी ठीक कर सकता है जो मेरे समाधान में चलते हैं।
कॉर्बफॉन

मुझे route.data["key"]बिना किसी त्रुटि के निर्माण के लिए उपयोग करना था । लेकिन मेरे पास जो समस्या है वह यह है कि मेरे पास एक मार्ग + घटक है जो दो अलग-अलग स्थानों में उपयोग किया जाता है। 1. sample/list/itemऔर 2. product/id/sample/list/itemजब मैं पहली बार या तो रास्तों को लोड करता हूं तो यह ठीक से लोड हो जाता है, लेकिन दूसरा रीटैच्ड एरर को फेंक देता है क्योंकि मैं list/itemअपने काम के आधार पर स्टोर कर रहा हूं इसलिए मैंने रूट को डुप्लिकेट किया है और यूआरएल पथ में कुछ बदलाव किया है लेकिन उसी घटक को प्रदर्शित करता है। निश्चित नहीं है कि उसके लिए कोई दूसरा काम हो।
ब्लूपार्क

मुझे इस तरह का भ्रम है, उपरोक्त बस काम नहीं करेगा, जैसे ही मैं अपने कैश मार्गों में से एक को मारूंगा, (यह अब नेविगेट नहीं करेगा और जहां कंसोल में त्रुटियां हैं)। क्रिस Fremgen समाधान मेरे आलसी मॉड्यूल के साथ ठीक काम करने के लिए लगता है जहाँ तक मैं बता सकता हूँ ...
MIP1983

12

एक और कार्यान्वयन अधिक वैध, पूर्ण और पुन: प्रयोज्य। यह आलसी लोड किए गए मॉड्यूल को @ Uğur Dinç के रूप में सपोर्ट करता है और @Davor रूट डेटा फ्लैग को एकीकृत करता है। सबसे अच्छा सुधार पृष्ठ निरपेक्ष पथ पर आधारित (लगभग) अद्वितीय पहचानकर्ता की स्वचालित पीढ़ी है। इस तरह आपको हर पृष्ठ पर इसे स्वयं परिभाषित नहीं करना है।

उस किसी भी पेज को चिह्नित करें जिसे आप सेटिंग को कैश करना चाहते हैं reuseRoute: true। इसका उपयोग shouldDetachविधि में किया जाएगा ।

{
  path: '',
  component: MyPageComponent,
  data: { reuseRoute: true },
}

क्वेरी परम की तुलना किए बिना यह सबसे सरल रणनीति कार्यान्वयन है।

import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle, UrlSegment } from '@angular/router'

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedHandles: { [key: string]: DetachedRouteHandle } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute) {
      this.storedHandles[id] = handle;
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const handle = this.storedHandles[id];
    const canAttach = !!route.routeConfig && !!handle;
    return canAttach;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedHandles[id]) return null;
    return this.storedHandles[id];
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }
}

यह एक क्वेरी परम की तुलना भी करता है। compareObjects@Corbfon संस्करण पर थोड़ा सुधार हुआ है: दोनों आधार के गुणों के माध्यम से लूप और वस्तुओं की तुलना करें। याद रखें कि आप बाहरी और अधिक विश्वसनीय कार्यान्वयन का उपयोग कर सकते हैं जैसे कि लॉश isEqualविधि।

import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle, UrlSegment } from '@angular/router'

interface RouteStorageObject {
  snapshot: ActivatedRouteSnapshot;
  handle: DetachedRouteHandle;
}

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedRoutes: { [key: string]: RouteStorageObject } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute && id.length > 0) {
      this.storedRoutes[id] = { handle, snapshot: route };
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const storedObject = this.storedRoutes[id];
    const canAttach = !!route.routeConfig && !!storedObject;
    if (!canAttach) return false;

    const paramsMatch = this.compareObjects(route.params, storedObject.snapshot.params);
    const queryParamsMatch = this.compareObjects(route.queryParams, storedObject.snapshot.queryParams);

    console.log('deciding to attach...', route, 'does it match?');
    console.log('param comparison:', paramsMatch);
    console.log('query param comparison', queryParamsMatch);
    console.log(storedObject.snapshot, 'return: ', paramsMatch && queryParamsMatch);

    return paramsMatch && queryParamsMatch;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedRoutes[id]) return null;
    return this.storedRoutes[id].handle;
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }

  private compareObjects(base: any, compare: any): boolean {

    // loop through all properties
    for (const baseProperty in { ...base, ...compare }) {

      // 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:
            // tslint:disable-next-line triple-equals
            if (base[baseProperty] != compare[baseProperty]) {
              return false;
            }
        }
      } else {
        return false;
      }
    }

    // returns true only after false HAS NOT BEEN returned through all loops
    return true;
  }
}

यदि आपके पास अद्वितीय कुंजी उत्पन्न करने का सबसे अच्छा तरीका है, तो मेरे उत्तर पर टिप्पणी करें, मैं कोड को अपडेट कर दूंगा।

उन सभी लोगों को धन्यवाद जिन्होंने अपना समाधान साझा किया।


3
यह स्वीकृत उत्तर होना चाहिए। ऊपर दिए गए कई समाधान एक ही बाल URL के साथ कई पृष्ठों का समर्थन नहीं कर सकते। क्योंकि वे ActiveRoute URL की तुलना कर रहे हैं, जो कि पूर्ण पथ नहीं है।
झुहंग.जास्पर

4

सभी उल्लिखित समाधान हमारे मामले में किसी तरह अपर्याप्त थे। हमारे पास छोटे व्यवसाय ऐप हैं:

  1. परिचय पृष्ठ
  2. लॉगिन वाला पन्ना
  3. एप्लिकेशन (लॉगिन के बाद)

हमारी आवश्यकताओं:

  1. आलसी-भारित मॉड्यूल
  2. बहु-स्तरीय मार्ग
  3. ऐप सेक्शन में मेमोरी में सभी राउटर / कंपोनेंट स्टेट्स स्टोर करें
  4. विशिष्ट मार्गों पर डिफ़ॉल्ट कोणीय पुन: उपयोग की रणनीति का विकल्प
  5. लॉगआउट पर मेमोरी में संग्रहीत सभी घटकों को नष्ट करना

हमारे मार्गों का सरल उदाहरण:

const routes: Routes = [{
    path: '',
    children: [
        {
            path: '',
            canActivate: [CanActivate],
            loadChildren: () => import('./modules/dashboard/dashboard.module').then(module => module.DashboardModule)
        },
        {
            path: 'companies',
            canActivate: [CanActivate],
            loadChildren: () => import('./modules/company/company.module').then(module => module.CompanyModule)
        }
    ]
},
{
    path: 'login',
    loadChildren: () => import('./modules/login/login.module').then(module => module.LoginModule),
    data: {
        defaultReuseStrategy: true, // Ignore our custom route strategy
        resetReuseStrategy: true // Logout redirect user to login and all data are destroyed
    }
}];

पुन: उपयोग की रणनीति:

export class AppReuseStrategy implements RouteReuseStrategy {

private handles: Map<string, DetachedRouteHandle> = new Map();

// Asks if a snapshot from the current routing can be used for the future routing.
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
}

// Asks if a snapshot for the current route already has been stored.
// Return true, if handles map contains the right snapshot and the router should re-attach this snapshot to the routing.
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
    if (this.shouldResetReuseStrategy(route)) {
        this.deactivateAllHandles();
        return false;
    }

    if (this.shouldIgnoreReuseStrategy(route)) {
        return false;
    }

    return this.handles.has(this.getKey(route));
}

// Load the snapshot from storage. It's only called, if the shouldAttach-method returned true.
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
    return this.handles.get(this.getKey(route)) || null;
}

// Asks if the snapshot should be detached from the router.
// That means that the router will no longer handle this snapshot after it has been stored by calling the store-method.
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return !this.shouldIgnoreReuseStrategy(route);
}

// After the router has asked by using the shouldDetach-method and it returned true, the store-method is called (not immediately but some time later).
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
    if (!handle) {
        return;
    }

    this.handles.set(this.getKey(route), handle);
}

private shouldResetReuseStrategy(route: ActivatedRouteSnapshot): boolean {
    let snapshot: ActivatedRouteSnapshot = route;

    while (snapshot.children && snapshot.children.length) {
        snapshot = snapshot.children[0];
    }

    return snapshot.data && snapshot.data.resetReuseStrategy;
}

private shouldIgnoreReuseStrategy(route: ActivatedRouteSnapshot): boolean {
    return route.data && route.data.defaultReuseStrategy;
}

private deactivateAllHandles(): void {
    this.handles.forEach((handle: DetachedRouteHandle) => this.destroyComponent(handle));
    this.handles.clear();
}

private destroyComponent(handle: DetachedRouteHandle): void {
    const componentRef: ComponentRef<any> = handle['componentRef'];

    if (componentRef) {
        componentRef.destroy();
    }
}

private getKey(route: ActivatedRouteSnapshot): string {
    return route.pathFromRoot
        .map((snapshot: ActivatedRouteSnapshot) => snapshot.routeConfig ? snapshot.routeConfig.path : '')
        .filter((path: string) => path.length > 0)
        .join('');
    }
}

3

निम्नलिखित काम है! संदर्भ: https://www.cnblogs.com/lovesangel/p/7853364.html

import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {

    public static handlers: { [key: string]: DetachedRouteHandle } = {}

    private static waitDelete: string

    public static deleteRouteSnapshot(name: string): void {
        if (CustomReuseStrategy.handlers[name]) {
            delete CustomReuseStrategy.handlers[name];
        } else {
            CustomReuseStrategy.waitDelete = name;
        }
    }
   
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return true;
    }

   
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        if (CustomReuseStrategy.waitDelete && CustomReuseStrategy.waitDelete == this.getRouteUrl(route)) {
            // 如果待删除是当前路由则不存储快照
            CustomReuseStrategy.waitDelete = null
            return;
        }
        CustomReuseStrategy.handlers[this.getRouteUrl(route)] = handle
    }

    
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

    /** 从缓存中获取快照,若无则返回nul */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        if (!route.routeConfig) {
            return null
        }

        return CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

   
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig &&
            JSON.stringify(future.params) === JSON.stringify(curr.params);
    }

    private getRouteUrl(route: ActivatedRouteSnapshot) {
        return route['_routerState'].url.replace(/\//g, '_')
    }
}


1
सावधान, यह एक आंतरिक चर का उपयोग करता है _routerState
डार्कनॉरटन

@DarkNeuron _routerStateकिसी भी हानिकारक का कारण बनता है ?
k11k2

2
नहीं, लेकिन Google उस चर को आसपास रखने के लिए कोई दायित्व नहीं है, क्योंकि यह आंतरिक रूप से उपयोग किया जाता है और एपीआई में उजागर नहीं होता है।
डार्कनॉरनॉन

जब हम बुला रहे हैं deleteRouteSnapshot?
k11k2

0

मैंने एक कस्टम मार्ग पुन: उपयोग की रणनीति को लागू करने वाले इन मुद्दों का सामना किया:

  1. रूट अटैच / डिटैच पर ऑपरेशन करें: सब्सक्रिप्शन, क्लीनअप आदि को प्रबंधित करें;
  2. केवल अंतिम पैरामीटर किए गए मार्ग की स्थिति को संरक्षित करें: मेमोरी ऑप्टिमाइज़ेशन;
  3. एक घटक का पुन: उपयोग करें, एक राज्य नहीं: एक राज्य-प्रबंधन उपकरण के साथ राज्य का प्रबंधन करें।
  4. "एक अलग मार्ग से बनाया गया सक्रियण RouteSnapshot पुनः जारी नहीं कर सकता" त्रुटि;

इसलिए मैंने इन मुद्दों को हल करने वाला एक पुस्तकालय लिखा। लाइब्रेरी एक सेवा प्रदान करती है और अटैच / डिटैच हुक के लिए डेकोरेटर्स का उपयोग करती है और एक रूट के पुर्जों को अलग करने के लिए मार्ग का उपयोग करती है, न कि किसी रूट के रास्तों को।

उदाहरण:

/* Usage with decorators */
@onAttach()
public onAttach(): void {
  // your code...
}

@onDetach()
public onDetach(): void {
  // your code...
}

/* Usage with a service */
public ngOnInit(): void {
  this.cacheRouteReuse
    .onAttach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });

  this.cacheRouteReuse
    .onDetach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });
}

पुस्तकालय: https://www.npmjs.com/package/ng-cache-route-reuse


बस अपने खुद के पुस्तकालय या ट्यूटोरियल से जोड़ना एक अच्छा जवाब नहीं है। इसे लिंक करना, यह बताना कि यह समस्या क्यों हल करता है, ऐसा करने के लिए कोड प्रदान करना और यह बताना कि आपने इसे बेहतर उत्तर के लिए बनाया है। देखें: क्या "अच्छा" आत्म प्रचार दर्शाता है?
पॉल राउ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.