两个可观察变量相互依赖

时间:2021-01-18 11:58:11

I need advice on how to make observable variable currentShop dependent on variable shopList.

我需要有关如何根据变量shopList创建可观察变量currentShop的建议。

AppComponent reads shopList through shopProvider. It works without a problem, but I need to modify the code to set currentShop from loaded shops.

AppComponent通过shopProvider读取shopList。它没有问题,但我需要修改代码以从加载的商店设置currentShop。

Thanks.

谢谢。

AppComponent:

AppComponent:

export class AppComponent {

    currentShop;
    shopList;

    constructor(router: Router,shopProvider:ShopProvider){
        this.shopList = shopProvider.shops;
        this.currentShop = shopProvider.currentShop;

        shopProvider.loadShops();
        shopProvider.setCurrentShop(-1);

        router.navigate(['/System']);
    }
}

ShopProvider:

ShopProvider:

export class ShopProvider{

    private _http:Http;

    shops:Observable<Array<Shop>>;

    currentShop:Observable<Shop>;

    private obServer:any;

    private _shopDataStore : {
        shops: Array<Shop>
    }

    constructor(http:Http){
        this._http = http;
        this.shops = new Observable(observer => this.obServer = observer).share();
        //this.currentShop = ??
        this._shopDataStore = {shops: []};
    }

    setCurrentShop(shopId:number){
       //Code ??
    }

    loadShops(){
        this._http
            .get('/services/shop/list')
            .map(res => res.json())
            .subscribe(data => {
                this._shopDataStore.shops = data;
                this.obServer.next(this._shopDataStore.shops);
            })
    }
}

HTML:

HTML:

<div>{{currentShop.name | async}}</div>

<ul>
    <li *ngFor="#shop of shopList | async">
        <a [routerLink]="['/Shop', {shopId: shop.id}]">
            <span>{{shop.name}}</span>
        </a>
    </li>
</ul>

2 个解决方案

#1


3  

I would go for an architecture more similar to:

我会选择一个更类似于以下的架构:

ShopService

ShopService

class Shop {
  constructor(public shopId: Number, public shopName: string) {}
}

@Injectable()
class ShopService {
  shops: Observable<Array<Shop>> = new Observable<Array<Shop>>();
  shopId: Subject<Number> = new Subject<Number>();
  currentShop: Observable<Shop> = new Observable<Shop>();

  public static getShopById(shops: Array<Shop>, shopId: Number): Shop {
    return shops.filter(shop => shop.shopId === shopId)[0];
  }
  public static compareShops(oldShop: Shop, newShop: Shop): Boolean {
    return oldShop.shopId === newShop.shopId;
  }

  constructor(@Inject(Http) private http: Http) {
    this.shops = this.http
      .get('**url here**')
      .map(res => res.json().shops);

    this.currentShop = this.shops
      .combineLatest(this.shopId, ShopService.getShopById)
      .distinctUntilChanged(ShopService.compareShops);
  }

  setCurrentShop(shopId: Number): void {
    this.shopId.next(shopId);
  }
}

Usage in your Component:

组件中的用法:

  • save ShopService.currentShop to a variable in the Component (let's say this.currentShop
  • 将ShopService.currentShop保存到Component中的变量(比如说this.currentShop
  • use {{ currentShop | async }} or .subscribe() to the ShopService.currentShop observable to be given the shop when it changes
  • 使用{{currentShop | async}}或.subscribe()到ShopService.currentShop observable,当它发生变化时给予商店

See a working example on CodePen. I like this approach more because I'm not saving any state, which seems more in line with reactive programming.

查看CodePen上的工作示例。我更喜欢这种方法,因为我没有保存任何状态,这似乎更符合反应式编程。

#2


1  

I suggest the following:

我建议如下:

ShopProvider

ShopProvider

private shopsObserver:Observer<Shop[]>;
private currentShopObserver: Observer<Shop>;
private _shopDataStore : {
    shops: [],
    currentShop: undefined
}

constructor(http:Http){
    this._http = http;
    this.shops = new Observable(observer => this.obServer = observer).share();
    this.currentShop = new Observable(observer => this.shopObserver = observer).share();

}

setCurrentShop(shopId:number){
   var item = this._shopDataStore.shops.find(item=>item.shopId == shopId);
   this._shopDataStore.currentShop = item;
   this.currentShopObserver.next(this._shopDataStore.currentShop);
}

loadShops(){
    this._http
        .get('/services/shop/list')
        .map(res => res.json())
        .subscribe(data => {
            this._shopDataStore.shops = data;
            this.shopsObserver.next(this._shopDataStore.shops);                
        })
}

AppComponent

AppComponent

export class AppComponent {

    currentShop;
    shopList;

    constructor(router: Router,shopProvider:ShopProvider){
        shopProvider.shops.subscribe(shops=>this.shopList = shops);
        shopProvider.currentShop.subscribe(currentShop => this.currentShop = currentShop);

        shopProvider.loadShops();
        shopProvider.setCurrentShop(-1);

        router.navigate(['/System']);
    }
}

HTML

HTML

<div>{{currentShop?.name}}</div>

<ul>
    <li *ngFor="#shop of shopList">
        <a [routerLink]="['/Shop', {shopId: shop.id}]">
            <span>{{shop.name}}</span>
        </a>
    </li>
</ul>

#1


3  

I would go for an architecture more similar to:

我会选择一个更类似于以下的架构:

ShopService

ShopService

class Shop {
  constructor(public shopId: Number, public shopName: string) {}
}

@Injectable()
class ShopService {
  shops: Observable<Array<Shop>> = new Observable<Array<Shop>>();
  shopId: Subject<Number> = new Subject<Number>();
  currentShop: Observable<Shop> = new Observable<Shop>();

  public static getShopById(shops: Array<Shop>, shopId: Number): Shop {
    return shops.filter(shop => shop.shopId === shopId)[0];
  }
  public static compareShops(oldShop: Shop, newShop: Shop): Boolean {
    return oldShop.shopId === newShop.shopId;
  }

  constructor(@Inject(Http) private http: Http) {
    this.shops = this.http
      .get('**url here**')
      .map(res => res.json().shops);

    this.currentShop = this.shops
      .combineLatest(this.shopId, ShopService.getShopById)
      .distinctUntilChanged(ShopService.compareShops);
  }

  setCurrentShop(shopId: Number): void {
    this.shopId.next(shopId);
  }
}

Usage in your Component:

组件中的用法:

  • save ShopService.currentShop to a variable in the Component (let's say this.currentShop
  • 将ShopService.currentShop保存到Component中的变量(比如说this.currentShop
  • use {{ currentShop | async }} or .subscribe() to the ShopService.currentShop observable to be given the shop when it changes
  • 使用{{currentShop | async}}或.subscribe()到ShopService.currentShop observable,当它发生变化时给予商店

See a working example on CodePen. I like this approach more because I'm not saving any state, which seems more in line with reactive programming.

查看CodePen上的工作示例。我更喜欢这种方法,因为我没有保存任何状态,这似乎更符合反应式编程。

#2


1  

I suggest the following:

我建议如下:

ShopProvider

ShopProvider

private shopsObserver:Observer<Shop[]>;
private currentShopObserver: Observer<Shop>;
private _shopDataStore : {
    shops: [],
    currentShop: undefined
}

constructor(http:Http){
    this._http = http;
    this.shops = new Observable(observer => this.obServer = observer).share();
    this.currentShop = new Observable(observer => this.shopObserver = observer).share();

}

setCurrentShop(shopId:number){
   var item = this._shopDataStore.shops.find(item=>item.shopId == shopId);
   this._shopDataStore.currentShop = item;
   this.currentShopObserver.next(this._shopDataStore.currentShop);
}

loadShops(){
    this._http
        .get('/services/shop/list')
        .map(res => res.json())
        .subscribe(data => {
            this._shopDataStore.shops = data;
            this.shopsObserver.next(this._shopDataStore.shops);                
        })
}

AppComponent

AppComponent

export class AppComponent {

    currentShop;
    shopList;

    constructor(router: Router,shopProvider:ShopProvider){
        shopProvider.shops.subscribe(shops=>this.shopList = shops);
        shopProvider.currentShop.subscribe(currentShop => this.currentShop = currentShop);

        shopProvider.loadShops();
        shopProvider.setCurrentShop(-1);

        router.navigate(['/System']);
    }
}

HTML

HTML

<div>{{currentShop?.name}}</div>

<ul>
    <li *ngFor="#shop of shopList">
        <a [routerLink]="['/Shop', {shopId: shop.id}]">
            <span>{{shop.name}}</span>
        </a>
    </li>
</ul>