Helpex - Trao đổi & giúp đỡ Đăng nhập
68

Tôi có một mô-đun Angular 2 trong đó tôi đã thực hiện định tuyến và muốn các trạng thái được lưu trữ khi điều hướng. Người dùng sẽ có thể: 1. tìm kiếm tài liệu bằng cách sử dụng biểu mẫu tìm kiếm 2. điều hướng đến một trong các kết quả 3. điều hướng quay lại tìm kiếm - mà không cần giao tiếp với máy chủ

Điều này có thể bao gồm cả RouteReuseStrargety. Câu hỏi là: Làm thế nào để tôi thực hiện rằng tài liệu không nên được lưu trữ?

Vì vậy, trạng thái "tài liệu" của đường dẫn tuyến đường nên được lưu trữ và trạng thái "tài liệu /: id" đường dẫn tuyến đường KHÔNG nên được lưu trữ?

68 hữu ích 0 bình luận 24k xem chia sẻ
149

Này, câu hỏi tuyệt vời!

Tôi đã có gần như trường hợp sử dụng giống như bạn và muốn làm điều tương tự! Tìm kiếm người dùng> nhận kết quả> Điều hướng người dùng đến kết quả> Người dùng điều hướng trở lại> BOOM nhanh chóng quay lại kết quả , nhưng bạn không muốn lưu trữ kết quả cụ thể mà người dùng đã điều hướng.

tl; dr

Bạn cần phải có một lớp thực hiện RouteReuseStrategyvà cung cấp chiến lược của bạn trong ngModule. Nếu bạn muốn sửa đổi khi tuyến đường được lưu trữ, hãy sửa đổi shouldDetachchức năng. Khi nó trở lại true, Angular lưu trữ tuyến đường. Nếu bạn muốn sửa đổi khi tuyến đường được đính kèm, hãy sửa đổi shouldAttachchức năng. Khi shouldAttachtrả về true, Angular sẽ sử dụng tuyến được lưu trữ thay cho tuyến được yêu cầu. Đây là một Plunker để bạn chơi xung quanh.

Giới thiệu về RouteReuseStrargety

Bằng cách hỏi câu hỏi này, bạn đã hiểu rằng RouteReuseStrargety cho phép bạn nói với Angular không phá hủy một thành phần, nhưng thực tế là để lưu nó để hiển thị lại vào một ngày sau đó. Điều đó thật tuyệt vì nó cho phép:

  • Các cuộc gọi máy chủ giảm
  • Tăng tốc độ
  • thành phần hiển thị, theo mặc định, trong cùng một trạng thái, nó được để lại

Điều cuối cùng rất quan trọng nếu bạn muốn tạm thời rời khỏi một trang mặc dù người dùng đã nhập rất nhiều văn bản vào đó. Các ứng dụng doanh nghiệp sẽ yêu thích tính năng này vì số lượng biểu mẫu quá nhiều !

Đây là những gì tôi đã đưa ra để giải quyết vấn đề. Như bạn đã nói, bạn cần sử dụng RouteReuseStrategy@ angular / bộ định tuyến được cung cấp trong các phiên bản 3.4.1 trở lên.

LÀM

Trước tiên, hãy đảm bảo dự án của bạn có @ angular / bộ định tuyến phiên bản 3.4.1 trở lên.

Tiếp theo , tạo một tệp sẽ chứa lớp của bạn thực hiện RouteReuseStrategy. Tôi gọi cho tôi reuse-strategy.tsvà đặt nó trong /appthư mục để giữ an toàn. Bây giờ, lớp này sẽ trông như:

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

export class CustomReuseStrategy implements RouteReuseStrategy {
}

(đừng lo lắng về lỗi TypeScript của bạn, chúng tôi sẽ giải quyết mọi thứ)

Kết thúc nền tảng bằng cách cung cấp các lớp học của bạn app.module. Lưu ý rằng bạn chưa viết CustomReuseStrategy, nhưng nên tiếp tục và importnó từ reuse-strategy.tstất cả giống nhau. Cũng thếimport { RouteReuseStrategy } from '@angular/router';

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

Phần cuối cùng là viết lớp sẽ kiểm soát xem các tuyến có bị tách ra, lưu trữ, truy xuất và được gắn lại hay không. Trước khi chúng tôi nhận được bản sao / dán, tôi sẽ giải thích ngắn về cơ học ở đây, vì tôi hiểu chúng. Tham khảo mã dưới đây cho các phương pháp tôi mô tả, và tất nhiên, có rất nhiều tài liệu trong mã .

  1. Khi bạn điều hướng, shouldReuseRoutebắn. Đây là một điều hơi kỳ lạ đối với tôi, nhưng nếu nó trở lại true, thì nó thực sự sử dụng lại tuyến đường bạn đang đi và không có phương pháp nào khác được thực hiện. Tôi chỉ trả về false nếu người dùng đang điều hướng đi.
  2. Nếu shouldReuseRoutetrả lại false, shouldDetachcháy. shouldDetachxác định xem bạn có muốn lưu trữ tuyến đường hay không và trả về một booleanchỉ báo càng nhiều. Đây là nơi bạn nên quyết định lưu trữ / không lưu trữ các đường dẫn , điều tôi sẽ làm bằng cách kiểm tra một mảng các đường dẫn bạn muốn lưu trữ route.routeConfig.pathvà trả về false nếu pathkhông tồn tại trong mảng.
  3. Nếu shouldDetachtrả lại true, storebị sa thải, đó là cơ hội để bạn lưu trữ bất kỳ thông tin nào bạn muốn về tuyến đường. Dù bạn làm gì, bạn sẽ cần lưu trữ DetachedRouteHandlevì đó là những gì Angular sử dụng để xác định thành phần được lưu trữ của bạn sau này. Dưới đây, tôi lưu trữ cả DetachedRouteHandlevà và ActivatedRouteSnapshotvào một biến cục bộ cho lớp của tôi.

Vì vậy, chúng ta đã thấy logic để lưu trữ, nhưng điều hướng về một thành phần thì sao? Làm thế nào để Angular quyết định chặn điều hướng của bạn và đặt cái được lưu trữ vào vị trí của nó?

  1. Một lần nữa, sau khi shouldReuseRouteđã trở lại false, shouldAttachchạy, đó là cơ hội của bạn để tìm hiểu xem bạn muốn tái tạo hoặc sử dụng thành phần trong bộ nhớ. Nếu bạn muốn sử dụng lại một thành phần được lưu trữ, hãy quay lại truevà bạn đang đi đúng hướng!
  2. Bây giờ Angular sẽ hỏi bạn, "bạn muốn chúng tôi sử dụng thành phần nào?", Mà bạn sẽ chỉ ra bằng cách trả lại thành phần đó DetachedRouteHandletừ retrieve.

Đó là khá nhiều tất cả logic bạn cần! Trong mã cho reuse-strategy.ts, bên dưới, tôi cũng để lại cho bạn một hàm tiện lợi sẽ so sánh hai đối tượng. Tôi sử dụng nó để so sánh tuyến đường trong tương lai route.paramsroute.queryParamsvới tuyến đường được lưu trữ. Nếu tất cả đều khớp, tôi muốn sử dụng thành phần được lưu trữ thay vì tạo một thành phần mới. Nhưng làm thế nào bạn làm điều đó là tùy thuộc vào bạn!

tái sử dụng chiến lược.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;
    }
}

Hành vi

Việc triển khai này lưu trữ mọi tuyến đường duy nhất mà người dùng truy cập trên bộ định tuyến chính xác một lần. Điều này sẽ tiếp tục thêm vào các thành phần được lưu trữ trong bộ nhớ trong suốt phiên của người dùng trên trang web. Nếu bạn muốn giới hạn các tuyến đường mà bạn lưu trữ, thì nơi cần thực hiện là shouldDetachphương pháp. Nó kiểm soát các tuyến đường bạn lưu.

Thí dụ

Giả sử người dùng của bạn tìm kiếm thứ gì đó từ trang chủ, điều hướng họ đến đường dẫn search/:term, có thể xuất hiện như thế nào www.yourwebsite.com/search/thingsearchedfor. Trang tìm kiếm chứa một loạt các kết quả tìm kiếm. Bạn muốn lưu trữ tuyến đường này, trong trường hợp họ muốn quay lại tuyến đường đó! Bây giờ họ nhấp vào kết quả tìm kiếm và được điều hướng đến view/:resultIdmà bạn không muốn lưu trữ, vì họ có thể sẽ chỉ ở đó một lần. Với việc thực hiện ở trên, tôi chỉ cần thay đổi shouldDetachphương pháp! Đây là những gì nó có thể trông giống như:

Trước hết, hãy tạo một loạt các đường dẫn mà chúng ta muốn lưu trữ.

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

Bây giờ, trong shouldDetachchúng ta có thể kiểm tra route.routeConfig.pathchống lại mảng của chúng tôi.

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

Vì Angular sẽ chỉ lưu trữ một phiên bản của tuyến đường, nên bộ lưu trữ này sẽ rất nhẹ và chúng tôi sẽ chỉ lưu trữ thành phần được đặt tại search/:termchứ không phải tất cả các tuyến khác!

Liên kết bổ sung

Mặc dù hiện tại chưa có nhiều tài liệu, nhưng đây là một vài liên kết đến những gì tồn tại:

Tài liệu góc: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrargety- class.html

Bài viết giới thiệu: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrargety.aspx

149 hữu ích 5 bình luận chia sẻ
25

Đừng bị đe dọa bởi câu trả lời được chấp nhận, điều này khá đơn giản. Đây là một câu trả lời nhanh chóng những gì bạn cần. Tôi muốn giới thiệu ít nhất là đọc câu trả lời được chấp nhận, vì nó đầy chi tiết tuyệt vời.

Giải pháp này không thực hiện bất kỳ so sánh tham số nào như câu trả lời được chấp nhận nhưng nó sẽ hoạt động tốt để lưu trữ một tập hợp các tuyến đường.

nhập khẩu app.module.ts:

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

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

chia sẻ / định tuyến.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;
 }
}
25 hữu ích 3 bình luận chia sẻ
8

Để sử dụng chiến lược của Chris Fremgen với các mô-đun được tải một cách lười biếng, hãy sửa đổi lớp CustomReuseStrargety thành như sau:

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

cuối cùng, trong các tệp định tuyến của mô-đun tính năng của bạn, xác định các khóa của bạn:

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

Thêm thông tin ở đây .

8 hữu ích 4 bình luận chia sẻ
3

Sau đây là công việc! tham khảo: https://www.cnbloss.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, '_')
    }
}

3 hữu ích 5 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ javascript angular typescript angular-ui-router , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading