programing

Angular 2에서 다시 로드하지 않고 경로 매개변수 변경

newstyles 2023. 5. 29. 09:57

Angular 2에서 다시 로드하지 않고 경로 매개변수 변경

Angular 2, Google Maps 등을 이용하여 부동산 웹사이트를 만들고 있으며 사용자가 지도의 중심을 변경하면 지도의 현재 위치와 반경을 나타내는 API로 검색을 수행합니다.중요한 것은, 저는 전체 페이지를 다시 로드하지 않고 URL에 그 값을 반영하고 싶습니다.그게 가능한가요?AngularJS 1.x를 사용한 솔루션을 찾았지만 Angular 2에 대한 솔루션은 없었습니다.

RC6 기준으로 다음을 수행하여 상태 변경 없이 URL을 변경하여 경로 기록을 유지할 수 있습니다.

import {OnInit} from '@angular/core';

import {Location} from '@angular/common'; 
// If you dont import this angular will import the wrong "Location"

@Component({
  selector: 'example-component',
  templateUrl: 'xxx.html'
})
export class ExampleComponent implements OnInit
{
  constructor( private location: Location )
  {}

  ngOnInit()
  {    
    this.location.replaceState("/some/newstate/");
  }
}

사용할 수 있습니다.location.go(url)신청 경로 변경 없이 기본적으로 URL을 변경할 수 있습니다.

이로 인해 현재 경로에서 하위 경로로 리디렉션하는 등의 다른 영향이 발생할 수 있습니다.

다음을 설명하는 관련 질문location.go에게 친밀하지 않은Router변화를 일으키는 것.

방법

import { Location } from '@angular/common';

constructor(private _location: Location){ }

updateUrl(url: string) {
  this._location.go(url)
}

용사를 합니다.location.go(url)방법이지만 URL을 하드 코딩하는 대신 사용하여 생성하는 것을 고려하십시오.router.createUrlTree().

라우터 호출을 수행하려는 : " 다음라호수는려행경우하출을우":this.router.navigate([{param: 1}], {relativeTo: this.activatedRoute})그러나 구성 요소를 다시 로드하지 않고 다음과 같이 다시 쓸 수 있습니다.

const url = this.router.createUrlTree([], {relativeTo: this.activatedRoute, queryParams: {param: 1}}).toString()

 this.location.go(url);

저처럼 이 질문을 찾는 사람에게는 다음과 같은 것이 유용할 수 있습니다.

저도 비슷한 문제가 있어서 처음에 location.go 및 location.replaceState를 여기 다른 답변에서 제안한 대로 사용해 보았습니다.그러나 탐색이 현재 경로와 관련되어 있고 현재 경로가 location.go 또는 location.replaceState에 의해 업데이트되지 않았기 때문에 앱의 다른 페이지로 이동해야 할 때 문제가 발생했습니다(라우터는 URL에 대한 작업에 대해 전혀 알지 못합니다).

본질적으로 저는 경로 매개변수가 변경되었을 때 페이지/구성요소를 다시 로드하지 않고 내부적으로 경로 상태를 업데이트하는 솔루션이 필요했습니다.

쿼리 매개 변수를 사용하게 되었습니다.자세한 내용은 여기에서 확인할 수 있습니다. https://angular-2-training-book.rangle.io/handout/routing/query_params.html

따라서 주문을 저장하고 주문 ID를 얻어야 할 경우 아래와 같이 페이지 URL을 업데이트할 수 있습니다.지도의 중심 위치와 관련 데이터를 업데이트하는 것도 유사합니다.

// let's say we're saving an order. Initally the URL is just blah/orders
save(orderId) {
    // [Here we would call back-end to save the order in the database]

    this.router.navigate(['orders'], { queryParams: { id: orderId } });
    // now the URL is blah/orders?id:1234. We don't reload the orders
    // page or component so get desired behaviour of not seeing any 
    // flickers or resetting the page.
}

ngOnInit 메서드 내에서 다음과 같이 추적할 수 있습니다.

ngOnInit() {
    this.orderId = this.route
        .queryParamMap
        .map(params => params.get('id') || null);
    // orderID is up-to-date with what is saved in database now, or if
    // nothing is saved and hence no id query paramter the orderId variable
    // is simply null.
    // [You can load the order here from its ID if this suits your design]
}

새(저장되지 않은) 주문과 함께 주문 페이지로 직접 이동해야 하는 경우 다음 작업을 수행할 수 있습니다.

this.router.navigate(['orders']);

또는 기존(저장된) 주문에 대한 주문 페이지로 직접 이동해야 하는 경우 다음 작업을 수행할 수 있습니다.

this.router.navigate(['orders'], { queryParams: { id: '1234' } });

저는 angular2의 RCx 릴리즈에서 이것이 작동하는 데 큰 어려움을 겪었습니다.위치 패키지가 이동되었으며 생성자() 내부에서 실행 중인 location.go()가 작동하지 않습니다.라이프사이클에서 nGOnInit() 이상이어야 합니다.다음은 몇 가지 코드 예제입니다.

import {OnInit} from '@angular/core';
import {Location} from '@angular/common';

@Component({
  selector: 'example-component',
  templateUrl: 'xxx.html'
})
export class ExampleComponent implements OnInit
{
  constructor( private location: Location )
  {}

  ngOnInit()
  {    
    this.location.go( '/example;example_param=917' );
  }
}

다음은 이 문제에 대한 각진 리소스입니다. https://angular.io/docs/ts/latest/api/common/index/Location-class.html https://angular.io/docs/ts/latest/api/common/index/LocationStrategy-class.html

질문에서 설명한 것과 유사한 요구사항이 있었고 기존 답변을 바탕으로 문제를 파악하는 데 시간이 오래 걸렸기 때문에 최종 해결책을 공유하고자 합니다.

요구 사항들

내 보기 상태(구성요소, 기술적으로)는 사용자가 변경할 수 있습니다(필터 설정, 정렬 옵션 등).상태가 변경되면(예: 사용자가 정렬 방향을 변경하면) 다음 작업을 수행합니다.

  • URL에 상태 변경 사항 반영
  • 상태 변경을 처리합니다. 즉, API 호출을 통해 새 결과 집합을 수행합니다.

추가로, 저는 다음을 원합니다.

  • 상황에 따라 URL 변경 사항을 브라우저 기록(뒤로/앞으로)에서 고려할 것인지 지정
  • 복잡한 개체를 상태 매개 변수로 사용하여 상태 변경을 보다 유연하게 처리할 수 있습니다(선택 사항이지만 일부 상태 변경이 백엔드/API 호출을 트리거하고 다른 상태 변경은 프런트엔드에서 내부적으로 처리하는 경우 등).

솔루션:구성 요소를 다시 로드하지 않고 상태 변경

경로 매개 변수 또는 쿼리 매개 변수를 사용할 때 상태 변경으로 인해 구성 요소가 다시 로드되지 않습니다.구성 요소 인스턴스가 활성 상태로 유지됩니다.를 사용하여 라우터 상태를 엉망으로 만들 좋은 이유가 없습니다.Location.go()또는location.replaceState().

var state = { q: 'foo', sort: 'bar' }; 
var url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: state }).toString();
this.router.navigateByUrl(url);

state's Angular's의 URL에 의해 됩니다.Router:

https://localhost/some/route?q=foo&sort=bar

솔루션:API 호출을 위한 상태 변경 처리

은 위서트된상변에수있처다습니리할가여입하에 할 수 .ActivatedRoute.queryParams:

export class MyComponent implements OnInit {

   constructor(private activatedRoute: ActivatedRoute) { }

   ngOnInit()
   {
      this.activatedRoute.queryParams.subscribe((params) => {
         // params is the state object passed to the router on navigation 
         // Make API calls here
      });
   }

}

state는 위의샘객다같음전이것입달로 입니다.paramsqueryParams을 할 수 .필요한 경우 핸들러에서 API 호출을 수행할 수 있습니다.

하지만: 구성 요소에서 상태 변화를 직접 처리하고 우회하는 것을 피하고 싶습니다.ActivatedRoute.queryParams, 을 수행하고 IMO를 할 수 있도록 것, "Angular", "Angular"와 같은 것입니다.queryParams무언가를 하기 위해 변경하면, 내 코드의 유지보수성과 가독성과 관련하여 내 구성요소에서 일어나는 일들을 완전히 혼란스럽게 합니다.대신 수행할 작업:

를 에 비교합니다.queryParams내 요소의 하며, 도 하지 변경을 처리하십시오.: " 구요현상재관며가로내하능찰태다성않수변처니않직지아대경경합리변상을접경태신고은지무행도하것우되소의▁changes▁observ내▁state,▁directly처▁instead▁do다▁in직합니리▁handle:,▁nothingable▁with구▁my▁component▁and접▁the▁it▁state변을경상▁current▁if성▁hasn▁there태대신'지.

export class MyComponent implements OnInit {

   private _currentState;

   constructor(private activatedRoute: ActivatedRoute) { }

   ngOnInit()
   {
      this.activatedRoute.queryParams.subscribe((params) => {
         // Following comparison assumes, that property order doesn't change
         if (JSON.stringify(this._currentState) == JSON.stringify(params)) return;
         // The followig code will be executed only when the state changes externally, i.e. through navigating to a URL with params by the user
         this._currentState = params;
         this.makeApiCalls();
      });
   }

   updateView()
   {          
      this.makeApiCalls();
      this.updateUri();
   }    

   updateUri()
   {
      var url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: this._currentState }).toString();
this.router.navigateByUrl(url);
   }
}

솔루션:브라우저 기록 동작 지정

var createHistoryEntry = true // or false
var url = ... // see above
this.router.navigateByUrl(url, { replaceUrl : !createHistoryEntry});

솔루션:복잡한 객체 상태

이는 원래 질문을 벗어나지만 일반적인 시나리오를 다루므로 유용할 수 있습니다.state위의 개체는 플랫 개체(단순 문자열/bool/int/... 개체는 .속성은 있지만 중첩된 개체는 없습니다.백엔드 호출로 처리해야 하는 속성과 구성 요소가 내부적으로만 사용하는 속성을 구분해야 하기 때문에 이러한 제한이 있다는 것을 알게 되었습니다.저는 다음과 같은 상태 객체를 원했습니다.

var state = { filter: { something: '', foo: 'bar' }, viewSettings: { ... } };

이 상태를 라우터에 대한 queryParams 개체로 사용하려면 이 상태를 평평하게 만들어야 합니다.나는 간단하게JSON.stringify개체의 모든 첫 번째 수준 속성:

private convertToParamsData(data) {
    var params = {};

    for (var prop in data) {
      if (Object.prototype.hasOwnProperty.call(data, prop)) {
        var value = data[prop];
        if (value == null || value == undefined) continue;
        params[prop] = JSON.stringify(value, (k, v) => {
          if (v !== null) return v
        });
      }
    }
    return params;
 }

라우터에 의해 전달된 쿼리를 처리할 때 Params가 반환됩니다.

private convertFromParamsData(params) {
    var data = {};

    for (var prop in params) {
      if (Object.prototype.hasOwnProperty.call(params, prop)) {
        data[prop] = JSON.parse(params[prop]);
      }
    }
    return data;
}

마지막: 바로 사용할 수 있는 Angular 서비스

마지막으로, 이 모든 것이 하나의 단순한 서비스로 분리되었습니다.

import { Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { Location } from '@angular/common';
import { map, filter, tap } from 'rxjs/operators';

@Injectable()
export class QueryParamsService {

  private currentParams: any;

  externalStateChange: Observable<any>;

  constructor(private activatedRoute: ActivatedRoute, private router: Router, private location: Location) {

    this.externalStateChange = this.activatedRoute.queryParams
      .pipe(map((flatParams) => {
        var params = this.convertFromParamsData(flatParams);
        return params
      }))
      .pipe(filter((params) => {
        return !this.equalsCurrentParams(params);
      }))
      .pipe(tap((params) => {
        this.currentParams = params;
      }));
  }

  setState(data: any, createHistoryEntry = false) {
    var flat = this.convertToParamsData(data);
    const url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams: flat }).toString();
    this.currentParams = data;
    this.router.navigateByUrl(url, { replaceUrl: !createHistoryEntry });
  }

  private equalsCurrentParams(data) {
    var isEqual = JSON.stringify(data) == JSON.stringify(this.currentParams);
    return isEqual;
  }

  private convertToParamsData(data) {
    var params = {};

    for (var prop in data) {
      if (Object.prototype.hasOwnProperty.call(data, prop)) {
        var value = data[prop];
        if (value == null || value == undefined) continue;
        params[prop] = JSON.stringify(value, (k, v) => {
          if (v !== null) return v
        });
      }
    }
    return params;
  }

  private convertFromParamsData(params) {
    var data = {};

    for (var prop in params) {
      if (Object.prototype.hasOwnProperty.call(params, prop)) {
        data[prop] = JSON.parse(params[prop]);
      }
    }
    return data;
  }
}

다음과 같이 사용할 수 있습니다.

@Component({
  selector: "app-search",
  templateUrl: "./search.component.html",
  styleUrls: ["./search.component.scss"],
  providers: [QueryParamsService]
})
export class ProjectSearchComponent implements OnInit {

    filter : any;

    viewSettings : any;

    constructor(private queryParamsService: QueryParamsService) { }

    ngOnInit(): void {

        this.queryParamsService.externalStateChange
          .pipe(debounce(() => interval(500))) // Debounce optional
          .subscribe(params => {
           // Set state from params, i.e.
           if (params.filter) this.filter = params.filter;
           if (params.viewSettings) this.viewSettings = params.viewSettings;

           // You might want to init this.filter, ... with default values here
           // If you want to write default values to URL, you can call setState here
            this.queryParamsService.setState(params, false); // false = no history entry

            this.initializeView(); //i.e. make API calls        
         });
     }

     updateView() {

       var data = {
         filter: this.filter,
         viewSettings: this.viewSettings
       };

       this.queryParamsService.setState(data, true);

       // Do whatever to update your view
     }

  // ...

}

잊지 마세요.providers: [QueryParamsService]구성 요소에 대한 새 서비스 인스턴스를 만들기 위한 구성 요소 수준의 문입니다.앱 모듈에 글로벌하게 서비스를 등록하지 마십시오.

이 방법을 사용하여 얻을 수 있습니다.

const queryParamsObj = {foo: 1, bar: 2, andThis: 'text'};

this.location.replaceState(
  this.router.createUrlTree(
    [this.locationStrategy.path().split('?')[0]], // Get uri
    {queryParams: queryParamsObj} // Pass all parameters inside queryParamsObj
  ).toString()
);

편집 --

저는 이것을 위해 정보를 좀 더 추가해야 한다고 생각합니다.

사용하는 경우this.location.replaceState()응용프로그램의 라우터가 업데이트되지 않았으므로 나중에 라우터 정보를 사용할 경우 브라우저의 라우터 정보와 동일하지 않습니다.예를 들어 다음을 사용하는 경우localizeService언어를하기 위해 한 후 에 변경했던 합니다.this.location.replaceState().

이 동작을 원하지 않는 경우 다음과 같은 다른 업데이트 URL 방법을 선택할 수 있습니다.

this.router.navigate(
  [this.locationStrategy.path().split('?')[0]],
  {queryParams: queryParamsObj}
);

고쳐지지, 사용자의 이옵에서브새로가고않쳐지만지지저는라우션,URL 변화또주다니됩에 됩니다.Router당신의 애플리케이션의, 그래서 당신이 언어를 바꿀 때 당신은 문제가 없습니다.this.location.replaceState().

물론 필요에 따라 방법을 선택할 수 있습니다.첫 는 변경 에 더 사용할 수 .URL브라우저에서

URL을 변경하는 동안 속성 쿼리ParamsHandling: 'merge'를 사용합니다.

this.router.navigate([], {
        queryParams: this.queryParams,
        queryParamsHandling: 'merge',
        replaceUrl: true,
});

저는 Angular 4.4.5를 사용하여 두 가지를 혼합했습니다.

router.navigate를 사용하여 realiveTo: activatedRoute 부분을 존중하지 않음으로써 내 URL을 계속 파괴했습니다.

저는 다음과 같은 일을 하게 되었습니다.

this._location.go(this._router.createUrlTree([this._router.url], { queryParams: { profile: value.id } }).toString())

2021년에는 제가 사용하는 솔루션이 있습니다.「 」 「 」 「 」 「 」 「 」를 이용한 「」createUrlTree 다을사여경이동다니합로로하음용을 사용하여 합니다.location

//Build URL Tree
    const urlTree = this.router.createUrlTree(["/employee/"+this.employeeId],{
      relativeTo: this.route,
      queryParams: params,
      queryParamsHandling: 'merge'
    });

//Update the URL 
this.location.go(urlTree.toString());

나의 경우 사용자가 URL을 볼 수 없도록 URL의 쿼리 매개 변수를 제거해야 했습니다.

찾았습니다replaceState이전 쿼리 매개 변수가 있는 경로가 스택에서 사라졌기 때문에 location.go보다 안전합니다. 사용자는 이 쿼리와 관련된 쿼리를 다시 실행할 수 있습니다.그래서, 저는 그것을 하는 것을 선호합니다.

this.location.replaceState(this.router.url.split('?')[0]);

아주 조금location.go브라우저를 사용하여 뒤로 이동하면 쿼리 매개 변수를 사용하여 이전 경로로 돌아가 탐색 스택에 유지됩니다.

this.location.go(this.router.url.split('?')[0]);

URL 매개 변수가 변경될 때 API를 호출하지 않으려면 activatedRoute.navigate()를 사용하여 URL 매개 변수를 변경하고 스냅샷(구독 아님)을 사용하여 API를 호출하는 것이 좋습니다.

export class MyComponent implements OnInit {

   constructor(private activatedRoute: ActivatedRoute) { }

   ngOnInit()
   {
      const params = this.activatedRoute.snapshot.queryParams;
         // params is the state object passed to the router on navigation 
         // Make API calls here
   }

}
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';

@Component({
    selector: 'child-component',
    templateUrl: 'child.component.html',
    styleUrls: ['child.component.scss']
})
export class ChildComponent implements OnInit {
    
    constructor(
       private location: Location
    ) {}

    ngOnInit() {
        // you can put 'this.location.go()' method call in any another method
        this.location.go('parentRoute/anotherChildRoute');
    }
}

현재 구성 요소를 다시 로드하지 않고 브라우저에서 하위 경로를 변경합니다.

쿼리 매개 변수를 업데이트하고 다시 로드하지 않고 탐색하려고 했습니다.activatedRoute.snapshot.queryparams읽기 전용입니다.그리고 이러한 전환 접근법은 제 문제를 해결했습니다.

// Get queryparams
let state = Object.assign({}, this.route.snapshot.queryParams)

// Change parameters of url
state["z"] = "hi";
state["y"] = "bye";

// Create url and navigate to it without reloading
const url = this.router.createUrlTree([], { relativeTo: this.route, queryParams: state }).toString();
this.router.navigateByUrl(url);

언급URL : https://stackoverflow.com/questions/35618463/change-route-params-without-reloading-in-angular-2