web-dev-qa-db-ja.com

Angular 4-あるコンポーネントから別のコンポーネントにオブジェクトを渡す(親-子階層なし)

私の状況:

Ngforでループされた配列のオブジェクトを各タイルが表すタイルを表示するコンポーネントがあります。タイルをクリックすると、オブジェクトを別のコンポーネントに渡します。このコンポーネントは、変更可能なフィールドにそのオブジェクトのすべてのプロパティを表示する役割を果たします。

私が試したもの:

いくつかの調査を行い、親-子階層でこれを達成する方法を示す複数の投稿に出会った後、必要な機能を達成するために共有サービスを使用する必要があることを説明するいくつかの投稿を見つけ、サービス。

しかし、私が取得していないように見えるのは、いつ別のルートに移動する必要があるかです。サービスに渡されたオブジェクトは、詳細コンポーネントで取得するときに定義されていないため、ナビゲーションは早期に場所を見つけたようです。

私のコード:

タイルを表示するコンポーネントには、クリックされたオブジェクトを共有サービスに渡す次の機能があります。

editPropertyDetails(property: Property) {
    console.log('Edit property called');

    return new Promise(resolve => {
      this.sharedPropertyService.setPropertyToDisplay(property);
      resolve();
    }).then(
      () => this.router.navigate(['/properties/detail'])
    )
  }

共有サービスには、プロパティオブジェクトを設定する機能とそれを取得する機能があり、次のようになります。

@Injectable()
export class SharedPropertyService {
  // Observable
  public propertyToDisplay = new Subject<Property>();

  constructor( private router: Router) {}

  setPropertyToDisplay(property: Property) {
    console.log('setPropertyToDisplay called');
    this.propertyToDisplay.next(property);
  }

  getPropertyToDisplay(): Observable<Property> {
    console.log('getPropertyToDisplay called');
    return this.propertyToDisplay.asObservable();
  }
}

最後に、クリックされたが未定義のオブジェクトを取得するオブジェクトを受け取る必要がある詳細コンポーネント:

export class PropertyDetailComponent implements OnDestroy {

  property: Property;
  subscription: Subscription;

  constructor(private sharedPropertyService: SharedPropertyService) {
        this.subscription = this.sharedPropertyService.getPropertyToDisplay()
          .subscribe(
            property => { this.property = property; console.log('Detail Component: ' + property.description);}
          );
  }

  ngOnDestroy() {
    // When view destroyed, clear the subscription to prevent memory leaks
    this.subscription.unsubscribe();
  }
}

前もって感謝します!

7
DennisN

ルートのナビゲーションエクストラのようにクリックされたタイルのオブジェクトのIDを渡すことで問題を解決し、詳細コンポーネントのサービスを使用して、ルートを通過したIDに基づいてオブジェクトを取得しました。

私は以下のコードを提供するので、うまくいけば誰もこのすべてをもう一度経験する必要はありません。

含まれるオブジェクトの詳細を表示するためにクリックできるタイルを表示するコンポーネント:

  editPropertyDetails(property: Property) {
    console.log('Edit property called');

    let navigationExtras: NavigationExtras = {
            queryParams: {
                "property_id": property.id
            }
        };

    this.router.navigate(['/properties/detail'], navigationExtras);
  }

クリックされたオブジェクトを受け取る詳細コンポーネント

  private sub: any;
  propertyToDisplay: Property;

  constructor
  (
    private sharedPropertyService: SharedPropertyService,
    private router: Router,
    private route: ActivatedRoute
  ) {}

  ngOnInit() {
  this.sub = this.route.queryParams.subscribe(params => {
        let id = params["property_id"];

        if(id) {
          this.getPropertyToDisplay(id);
        }

    });
  }

  getPropertyToDisplay(id: number) {
    this.sharedPropertyService.getPropertyToDisplay(id).subscribe(
            property => {
              this.propertyToDisplay = property;
            },
            error => console.log('Something went wrong'));
  }

  // Prevent memory leaks
  ngOnDestroy() {
    this.sub.unsubscribe();
  }

サービス

  properties: Property[];

  constructor( private propertyService: PropertyService) {}

  public getPropertyToDisplay(id: number): Observable<Property> {
    if (this.properties) {
      return this.findPropertyObservable(id);
    } else {
            return Observable.create((observer: Observer<Property>) => {
                this.getProperties().subscribe((properties: Property[]) => {
                    this.properties = properties;
                    const prop = this.filterProperties(id);
                    observer.next(prop);
                    observer.complete();
                })
            }).catch(this.handleError);
    }
  }

  private findPropertyObservable(id: number): Observable<Property> {
    return this.createObservable(this.filterProperties(id));
  }

  private filterProperties(id: number): Property {
        const props = this.properties.filter((prop) => prop.id == id);
        return (props.length) ? props[0] : null;
    }

  private createObservable(data: any): Observable<any> {
        return Observable.create((observer: Observer<any>) => {
            observer.next(data);
            observer.complete();
        });
    }

  private handleError(error: any) {
      console.error(error);
      return Observable.throw(error.json().error || 'Server error');
  }

  private getProperties(): Observable<Property[]> {
    if (!this.properties) {
    return this.propertyService.getProperties().map((res: Property[]) => {
      this.properties = res;
      console.log('The properties: ' + JSON.stringify(this.properties));
      return this.properties;
    })
      .catch(this.handleError);
    } else {
      return this.createObservable(this.properties);
      }
  }
5
DennisN

以下のサンプルで試してください:

ステップ1:サービスを作成する[DataService]

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class DataService {
  private userIdSource = new BehaviorSubject<number>(0);
  currentUser = this.userIdSource.asObservable();

  private orderNumSource = new BehaviorSubject<number>(0);
  currentOrder = this.orderNumSource.asObservable();

  constructor() { }

  setUser(userid: number) {
    this.userIdSource.next(userid)
  }

   setOrderNumber(userid: number) {
    this.orderNumSource.next(userid)
  }
}

ステップ2:ログインコンポーネントに値を設定する

import { Component } from '@angular/core';
import { DataService } from "../services/data.service";

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'] 
})
export class LoginComponent {
  constructor( private dataService:DataService) {     }
   onSubmit() {
        this.dataService.setUser(1); 
  } 
}

ステップ3:別のコンポーネントで値を取得する

import { Component, OnInit } from '@angular/core';
import { DataService } from "../services/data.service";

@Component({
  selector: 'app-shopping-cart',
  templateUrl: './shopping-cart.component.html',
  styleUrls: ['./shopping-cart.component.css']
})
export class ShoppingCartComponent implements OnInit {
  userId: number = 0;
  constructor(private dataService: DataService) { }
  ngOnInit() {
    this.getUser();
 }
  getUser() {
    this.dataService.currentUser.subscribe(user => {
      this.userId = user
    }, err => {
      console.log(err);
    });
  }
 }

:ページを更新すると、値は失われます。

4
Sanjay Katiyar

最初にサービスを使用して、サービスに1つの機能を作成します。その関数を呼び出し、他のコンポーネントのサブジェクトを使用します。このコードを書く

 this.handleReq.handlefilterdata.subscribe(() => {
                this.ngDoCheck(); 
            });

ここでは、handleReqはサービスです。 handlefilterdataはrxjsの主題です。

3
vishal dobariya

私は同様の機能に取り組んでいて、同じ問題に遭遇しました(未定義として)。このように初期化できます。

public propertyToDisplay = new BehaviorSubject<Property>(undefined);

このような変更を加えた後。サービスファイル内およびこのサービスを使用しようとしているコンポーネント内のObservableから値を取得できます。

0
Prashanti.D

コンソールは何を出力しますか? this.property子コンポーネントに設定したことがありますか?

私はこの機能を取り除こうとします:

getPropertyToDisplay(): Observable<Property>

そして、propertyToDisplayに直接アクセスしてみてください。

また、.navigateは2番目のパラメーターとしてデータを受け取ることができるため、ルート変更でデータを渡そうとする場合があります。

constructor(
    private route: ActivatedRoute,
    private router: Router) {}

  ngOnInit() {
    this.property = this.route
      .variableYouPassedIntoNavigator
0
JBoothUA

このようにしてみてください:

this.sharedPropertyService.getPropertyToDisplay()の代わりにthis.sharedPropertyService.propertyToDisplayをサブスクライブしてください

this.sharedPropertyService.propertyToDisplay.subscribe((property) => {
    this.property = property;
    console.log('Detail Component: ' + property.description);
});

以下のようにオブジェクトを送信します。

editPropertyDetails(property: Property) {
    this.sharedPropertyService.setPropertyToDisplay(property);
}
0
Chandru