【问题标题】:Angular 4 - Pass an object from one component to another (no parent - child hierarchy)Angular 4 - 将对象从一个组件传递到另一个组件(无父 - 子层次结构)
【发布时间】:2018-05-08 09:03:30
【问题描述】:

我的情况:

我有一个显示图块的组件,每个图块代表一个数组中的一个对象,该数组使用 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();
  }
}

提前致谢!

【问题讨论】:

  • 如果你可以在 stackblitz 中复制它,我很难弄清楚它会更容易理解。截至目前,对shared services 的解释更简单
  • 您需要在包含您的两个组件的模块中提供共享服务。是这样吗?否则它不会是一个单例,并且服务会失去它的目的
  • 是的,该服务在功能模块的提供者中列出。
  • 您找到其他解决方案了吗?
  • @RobertWilliams 我在下面发布了我的解决方案。看看接受的答案。

标签: angular angular-components angular-router


【解决方案1】:

我通过传递被点击的磁贴对象的 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);
      }
  }

【讨论】:

    【解决方案2】:

    请尝试以下示例:

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

    第二步:在登录组件中设置值

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

    注意:页面刷新时值会丢失。

    【讨论】:

    • 我正在尝试完全按照您所写的操作,但无法正常工作。我的问题是,这两个组件是否必须同时实例化才能正常工作?因为在我的情况下,我希望第一个组件设置数据,而另一个组件尚未实例化,我想知道这是否是我只从服务中获取初始值的原因。
    • 要使数据服务正常工作,您要从中获取的组件必须先用 setter 实例化,然后再获取其他组件
    【解决方案3】:

    使用服务 首先,您在服务上创建一个功能。 调用该函数并使用其他组件。 写这段代码

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

    这里, handleReq 是服务。 handlefilterdata 是 rxjs 主题。

    【讨论】:

    • RXJS 是什么?,请告诉我们。谢谢!
    • 反应式编程是一种异步编程范式,关注数据流和变化的传播。 RxJS(Reactive Extensions for JavaScript)是一个使用 observables 进行反应式编程的库,可以更轻松地编写异步或基于回调的代码
    • 哇,节省了很多行!
    【解决方案4】:

    试试这样:

    尝试订阅this.sharedPropertyService.propertyToDisplay 而不是this.sharedPropertyService.getPropertyToDisplay()

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

    然后像下面这样发送对象:

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

    【讨论】:

    • 它仍然没有记录任何东西。
    • 似乎也不起作用,另外,触发导航到详细组件的最佳位置是什么?
    【解决方案5】:

    我正在研究类似的功能,但遇到了同样的问题(未定义)。你可以这样初始化。

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

    做出这样的改变之后。我能够从服务文件中的Observable 以及我尝试使用此服务的组件中获取值。

    【讨论】:

    • * public propertyToDisplay = new BehaviorSubject(undefined);
    【解决方案6】:

    你的控制台输出什么? this.property 是否曾经在子组件上设置过?

    我会尝试摆脱这个功能:

    getPropertyToDisplay(): Observable<Property>
    

    并尝试直接访问propertyToDisplay

    .navigate 还可以将数据作为第二个参数,因此您可以尝试在路由更改中传递数据。

    constructor(
        private route: ActivatedRoute,
        private router: Router) {}
    
      ngOnInit() {
        this.property = this.route
          .variableYouPassedIntoNavigator
    

    【讨论】:

    • 如何在导航功能中传递属性?试图查看有关 NavigationExtras 的文档,但无法弄清楚。 editPropertyDetails(property: Property) { this.router.navigate(['/properties/detail'], property); }
    • 文档应该有所有的例子,ngUnsubsribe 是否有可能被调用?
    • 不幸的是,通过 navigate 方法传递复杂的对象是不可能的。我在下面发布了我的解决方案。
    猜你喜欢
    • 2019-07-04
    • 1970-01-01
    • 1970-01-01
    • 2016-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-22
    • 1970-01-01
    相关资源
    最近更新 更多