以角度2

时间:2016-11-25 16:05:43

标签: javascript angular rxjs

这是我的第一篇文章,所以我希望我能正确地做事。

我正在尝试锻炼使用解析器和在子路径组件与其父路径组件之间共享状态的最佳方法。

我有一个Angular 2应用程序,它具有一个组件/路由结构,其中包含一个父路由,其中​​包含两个子路由,如下面的代码示例所示:

路线:

export const personRoutes: Routes =  [
{ path: 'home/:id', component: MyDetailsComponent,
  resolve: {
    person: PersonResolver
  },
  children: [
    { path: '', component: MyDetailsOverviewComponent, pathMatch: 'full' },
    { path: 'update', component: UpdateMyDetailsComponent }
  ]
}];

我在父路由上使用一个解析器来获取一些数据,然后才能查看它们的后续子路径视图。从发出api呼叫的服务获取数据。

解析器:

export class PersonResolver implements Resolve<any> {
    constructor(private personService: PersonService) { }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Person> {
       let id: string = route.params['id'];
       return this.personService.getPerson(id);
    }
}

服务:

getPerson(id: string): Observable<Person> {
    let params = new URLSearchParams();
    params.set('id', id);
    return this.http.get(this.config.apiEndpoint + '/People/', { search: params })
        .map((r: Response) => {
            let person = r.json() as Person;
            this.extractPersonData(person);
            return this.currentPerson.asObservable();
        })
        .catch(error => {
            console.log(error);
            return Observable.throw(error);
        }).share();
}

该服务映射从http调用返回的数据,并使用它为ReplaySubject提供第一个值,然后由父组件和子路由组件订阅。

这是使用从http / api调用返回的数据调用的extractPersonData函数/ this.currentPerson是ReplaySubject,缓冲区为1,组件将订阅。

extractPersonData(person: Person): void {
    PersonService.cleanPersonData(person);
    this.currentPerson.next(person);
}

我想在所有三个组件之间共享person对象的状态,并且不能使用@ Input / @ Output变量,因为子路由是在父路由器插座中动态加载的。

以下是显示解析后订阅ReplaySubject的父组件和子组件之一的代码:

父组件:

export class MyDetailsComponent implements OnInit {

  private person: Person;
  private subscription: Subscription;

  constructor(private route: ActivatedRoute, private personService: PersonService, private modalService: NgbModal) {
    this.route.data.subscribe((obs: Observable<Person>) => {
      console.log(obs);
      this.subscription = obs['person'].subscribe(person => {
      this.person = person;
    })
  },
  error => {
    console.log('Home Component - Init person error: ' + error);
  });
}

子组件:

export class MyDetailsOverviewComponent implements OnInit {

  private person: Person;
  private subscription: Subscription;
  isReady: boolean = false;

  constructor(private route: ActivatedRoute) {
    this.route.parent.data.subscribe((obs: Observable<Person>) => {
      this.person = obs['person'];
      this.subscription = obs['person'].subscribe(person => {
        this.person = person;
      });
    });
  }

我的想法是在服务中有一个更新方法,每个组件都可以调用。 update方法应该在内部调用this.currentPerson.next(newPerson),然后应该通知兄弟子组件和父组件彼此引起的更改。

我的问题是,这是一种在父母和子女路线上分享国家的糟糕方法吗?有没有其他方法可以做到这一点。目前我认为我正在泄漏父和子组件中的订阅者,并且在另一个内部的订阅调用感觉不对,如下所示:

this.route.data.subscribe((obs: Observable<Person>) => {
  console.log(obs);
  this.subscription = obs['person'].subscribe(person => {
  this.person = person;
})

感谢。

3 个答案:

答案 0 :(得分:0)

是订阅内部订阅是noobish。改为使用flatmap。

要消化的一些代码

  public tagsTextStream = this.tagsTextSubject.asObservable().flatMap((q: string) => {
    // noinspection UnnecessaryLocalVariableJS
    let restStream = this.restQueryService.getTagsList(q)
      .map((tags: any) => {
        // filter out any tags that already exist on the document
        let allTags = _.map(tags, 'name');
        let documentTags = _.map(this.tags, 'name');
        return _.pull(allTags, ...documentTags);
      })
      .catch((err: any) => {
        return Observable.throw(err);
      });
    return restStream;
  }).publish().refCount();

答案 1 :(得分:0)

看起来你正在为&#34; currentPerson&#34;设置一个观察点。已使用extractPersonData() -

this.currentPerson.next(person);已有的服务上

通常需要该用户的组件会订阅PersonService.currentPerson:)

我不是100%,如果这回答了问题,如果没有,请告诉我,我会尽力帮助!

答案 2 :(得分:0)

谢谢你们。

我想我已经解决了一些事情,所以他们有点整洁。

我仍然订阅了route.data,但现在我将组件内部人物对象设置为Observable类型,并使用异步管道来组成“哑”子组件。

所以这是父母:

export class PersonResolver implements Resolve<any> {
    constructor(private personService: PersonService) { }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): any {
      let id: string = route.params['id'];
      return this.personService.getPerson(id);
    }
}

这是子路由组件:

export class UpdateMyDetailsComponent implements OnInit {
  private person: Observable<Person>;
  private subscription: Subscription;

  constructor(private route: ActivatedRoute) {
    this.route.parent.data.subscribe((result: Observable<Person>) => {
      this.person = result['person'];
    });
  }

这是子路径视图/模板:

<div class="hidden-xs-down">
  <div class="row">
    <div class="card offset-sm-2 col-sm-3">
      <div class="card-title">
        <h4>Personal Details</h4>
      </div>
      <update-personal-details class="card-block" [person]="person | async"></update-personal-details>
    </div>
    <div class="card offset-sm-1 col-sm-3">
      <div class="card-title">
        <h4>Contact Details</h4>
      </div>
      <update-contact-details class="card-block" [person]="person | async"></update-contact-details>
    </div>
  </div>
  <br />
 <div class="row">
    <div class="card offset-sm-2 col-sm-7">
      <div class="card-title">
        <h4>Address Details</h4>
      </div>
      <update-address-details class="card-block" [person]="person | async"></update-address-details>
    </div>
  </div>
</div>

基本解析逻辑可以保持不变:

@Injectable()
export class PersonResolver implements Resolve<Observable<Person>> {
    constructor(private personService: PersonService) { }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Person> {
      let id: string = route.params['id'];
      return this.personService.getPerson(id);
    }
}

我认为这是正确的做法。让我分享所有组件之间的可观察状态,我应该能够通过共享observable等下面的服务调用来触发更新。

希望这可以帮助任何人搞乱类似的结构。

感谢所有答案。

如果有人认为这是错误的做法,那就开始批评!