在 Angular2 中交流三个组件(有两个孩子的父级)



我正在 Angular 2 中处理购物车.应用程序组件中包含 2 个组件(类别和产品列表)(第 3 个)。现在的问题是我无法传达两个子组件。我尝试了以下两种解决方案..

解决方案1:我在类别组合中使用了产品组件作为提供者,除了类别选择之外,所有工作正常,产品组件的视图没有更新/刷新。

解决方案2:我在共享服务中使用了"rxjs/Subject"并将选定的类别传递给产品组件,但不知道如何在类别选择/更改时调用产品组件的函数(getProducts())。任何帮助将不胜感激。谢谢

参考你今天的问题

我在javascript中有一个代码,如下var Output1; window.setInterval(function () { Output1 = Math.random(); },1000); console.log(Output1); 并希望在外部访问Output1,但它不起作用。请告诉我有什么方法可以在外面访问输出 1 吗?

我建议以下解决方案:

var output1;
function setCurrentValue() {output1 = Math.random();}
function sendNumber(){ $.post( "test.php", {rng: output1} ); }
var currInterval = window.setInterval(function () { setCurrentValue(); },1000);
<button onclick="sendNumber();return false;"> Send</button>

如果你想监控输出1(仅适用于控制台!),只需这样做:

var monitor = window.setInterval(function () { console.log(output1); },500);

您需要使用@Input()@Output()进行类别(作为子组件)和产品(作为父组件)进行交互。

父 html- [product.component.html]

<app-filter-categories [categoryResult]="categoryFilterValue" (clicked)="onClickedCategoryFilter($event)">
</app-filter-categories>
<div class="row">
<!--your code will come here for list of products-->
</div>

父组件 - [product.component.ts]

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'all-products',
templateUrl: 'all-products.component.html',
changeDetection: ChangeDetectionStrategy.Default
})
export class AllProductsComponent implements OnInit {
constructor(private _productsService: ProductsService,,
private _router: Router) {
}
ngOnInit() {
this.fillAllProducts(0);
}
fillAllProducts(pageIndex) {
this.loadingProgress = true;
var searchParams = new SearchParametersCategoryModel();
searchParams.CategoryFilter = this.categoryFilterValue;
searchParams.PageIndex = pageIndex;
//TODO - service call
}
onClickedCategoryFilter(filterValue: any) {
this.categoryFilterValue = filterValue;
this.allProductData$ = [];
this.currentPageIndex = 0;
this.fillAllProducts(this.currentPageIndex);
}
selectProduct(productId: any, selectedProductCart) {
//TODO
}
}

Child html- [category.component.html]

<div class="row">
<!--your code will come here for different type of categories-->
</div>

子组件 - [category.component.ts]

// libs
import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import { CategoriesService } from '../service/categories.service';
@Component({
moduleId: module.id,
selector: 'app-filter-categories',
templateUrl: 'category.component.html'
})
export class CategoryFilterComponent implements OnInit, AfterViewInit {
toggle = true;
@Input() categoryResult: any = '';
@Output() clicked = new EventEmitter<any>();
Category = [];
SelectedCategoryItems = [];
categoryFilterList: DictionaryFilter<string> = {};    //here u will add list of selected categories with key-value
constructor(private _categoriesService : CategoriesService) {
}
ngOnInit() {
//todo - fill categories list
}
onClickSelectCategory(searchType: any, event: any): any {
if (!(event.target.parentElement.className.search('active') > 1)) {
//this.removeFilterElementWithKey(searchType);
//above line to add logic for remove deselected data from categoryFilterList
} else {
this.categoryFilterList.push(element);
}
this.clicked.emit(this.categoryFilterList);
}
}

我想,这将解决你的问题。

好的,我的演示应用程序来了。这只是一个示例,向您展示应用应如何构建以正常工作。我知道你调用Web服务,但你适应它应该没有问题:

我有一个订阅 CategoryService 的 CategoryComponent。

模板

<H2>Category Component</H2>
<div *ngFor="let category of categories; let ndx=index;">
<label>{{category?.name}}</label>
<input type="checkbox" id="cat.{{ndx}}" [(ngModel)]="category.selected" (change)="emitChange()">
</div>

打字稿文件

import { Component, OnInit, OnDestroy } from '@angular/core';
import { CategoryService } from '../services/category.service';
import { Category } from '../models/models';
@Component({
moduleId: module.id,
selector: 'pm-category',
templateUrl: 'category.component.html',
styleUrls: ['category.component.scss']
})
export class CategoryComponent implements OnInit, OnDestroy {
private categories: Array<Category> = [];
constructor(private categoryService: CategoryService) {
}
ngOnInit() {
this.categoryService.getCategories().subscribe(list => {
if (list) {
this.categories = list;
}
});
}
ngOnDestroy() { }
private emitChange(): void {
this.categoryService.setCategories(this.categories);
}
}

每当单击类别时,服务都会更新。

接下来,我有一个订阅 CategoryService 和 ProductsService 的产品组件,并包含一个子组件 ProductComponent。

模板

<H2>Products Component</H2>
<div *ngFor="let product of products; let ndx=index;">
<label>{{product?.name}}</label>
<input type="radio" name="productGroup" id="prod.{{ndx}}" value="product" (change)="setSelectedProduct(product)">
</div>
<pm-product [product]="product"></pm-product>

打字稿文件

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ProductsService } from '../services/products.service';
import { CategoryService } from '../services/category.service';
import { Product } from '../models/models';
@Component({
moduleId: module.id,
selector: 'pm-products',
templateUrl: 'products.component.html',
styleUrls: ['products.component.scss']
})
export class ProductsComponent implements OnInit, OnDestroy {
public product: Product;
private products: Array<Product> = [];
constructor(
private productsService: ProductsService,
private categoryService: CategoryService
) { }
ngOnInit() {
this.productsService.getProducts().subscribe(list => {
if (list) {
this.products = list;
}
});
this.categoryService.getCategories().subscribe(list => {
const allProducts = this.productsService.getProductsValue();
const filteredProducts: Array<Product> = [];
let tempProducts: Array<Product> = [];
list.forEach(cat => {
tempProducts = allProducts.filter(prod => (prod.categoryId === cat.id) && (cat.selected === true));
if (tempProducts.length > 0) {
tempProducts.forEach(el => {
filteredProducts.push(el);
});
}
});
this.products = filteredProducts;
});
}
ngOnDestroy() { }
private setSelectedProduct(product: Product): void {
this.product = product;
}
}

当您选择产品时,其详细信息将通过产品组件和产品组件之间的输入绑定显示在产品组件中。当类别组件中的类别发生更改时,您的产品列表会根据选定的复选框而加宽或缩小。

产品组件来了

模板

<H2>Product Component</H2>
<div>Price: {{product?.details?.price}}</div>
<div>Age: {{product?.details?.age}}</div>

和打字稿文件

import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { Product } from '../models/models';
@Component({
moduleId: module.id,
selector: 'pm-product',
templateUrl: 'product.component.html',
styleUrls: ['product.component.scss']
})
export class ProductComponent implements OnInit, OnDestroy {
@Input() product: Product;
ngOnInit() { }
ngOnDestroy() { }
}

请注意,我省略了所有sass文件,因为它们是空的。但是你必须拥有它们才能编译你的应用程序!

以下是我使用的服务和 model.ts。

类别服务

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Category } from '../models/models';
@Injectable()
export class CategoryService {
private categories: BehaviorSubject<Array<Category>> = new BehaviorSubject([]);
constructor() {
const list: Array<Category> = [];
const categoryA: Category = new Category('CategoryA', false);
categoryA.id = 1000;
list.push(categoryA);
const categoryB: Category = new Category('CategoryB', false);
categoryB.id = 2000;
list.push(categoryB);
const categoryC: Category = new Category('CategoryC', false);
categoryC.id = 3000;
list.push(categoryC);
this.setCategories(list);
}
getCategories(): Observable<Array<Category>> {
return this.categories.asObservable();
}
getCategoriesValue(): Array<Category> {
return this.categories.getValue();
}
setCategories(val: Array<Category>) {
this.categories.next(val);
}
}

产品服务

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Product, Details } from '../models/models';
@Injectable()
export class ProductsService {
private products: BehaviorSubject<Array<Product>> = new BehaviorSubject([]);
constructor() {
const list: Array<Product> = [];
const detailsA: Details = new Details(33, 12);
const productA: Product = new Product('ProductA', detailsA, 1000);
productA.id = 200;
list.push(productA);
const detailsB: Details = new Details(1002, 56);
const productB: Product = new Product('ProductB', detailsB, 1000);
productB.id = 400;
list.push(productB);
const detailsC: Details = new Details(9, 4);
const productC: Product = new Product('ProductC', detailsC, 2000);
productC.id = 600;
list.push(productC);
this.setProducts(list);
}
getProducts(): Observable<Array<Product>> {
return this.products.asObservable();
}
getProductsValue(): Array<Product> {
return this.products.getValue();
}
setProducts(val: Array<Product>) {
this.products.next(val);
}
}

和我的模型.ts

export class Category {
constructor(
public name: string,
public selected: boolean,
public id?: number
) { }
}
export class Details {
constructor(
public price: number,
public age: number
) { }
}
export class Product {
constructor(
public name: string,
public details: Details,
public categoryId: number,
public id?: number
) { }
}

最后,我的主页.module.ts,我将其导入到app.module.ts中。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { MainPageComponent } from './main-page.component';
import { CategoryComponent } from '../category/category.component';
import { ProductComponent } from '../product/product.component';
import { ProductsComponent } from '../products/products.component';
import { CategoryService } from '../services/category.service';
import { ProductsService } from '../services/products.service';
@NgModule({
imports: [
FormsModule,
BrowserModule,
HttpClientModule,
RouterModule.forRoot([
{ path: 'pm-main-page', component: MainPageComponent },
]),
],
declarations: [
MainPageComponent,
CategoryComponent,
ProductComponent,
ProductsComponent
],
exports: [
MainPageComponent,
CategoryComponent,
ProductComponent,
ProductsComponent
],
providers: [
CategoryService,
ProductsService
]
})
export class MainPageModule {
} 

如果你把所有这些放在一起,你将有一个小型的工作应用程序,它完全符合你在帖子中描述的内容。我希望这对你有帮助。

PS:当然,如果类别列表更改等时已经做出的选择会发生什么,它当然是可以优化的,但它只应该是一个演示。 ;)

创建一个新服务,例如sibling-data-exchange.service.ts,并将以下代码放入其中:

import { Observable } from 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Injectable } from '@angular/core';
@Injectable()
export class SiblingDataExchangeService {
private info = new BehaviorSubject('information');
getInfo(): Observable<string> {
return this.info.asObservable();
}
getInfoValue(): string {
return this.info.getValue();
}
setInfo(val: string) {
this.info.next(val);
}
}

在这两个组件中,导入和订阅此服务,并添加一个方法以将值存储在服务中。

import { SiblingDataExchangeService } from 'sibling-data-exchange.service';
constructor(
private siblingDataExchangeService: SiblingDataExchangeService
) { 
// stay updated whenever your sibling changes value
this.siblingDataExchangeService.getInfo().subscribe(value => {
// do something with this value
this.copyOfValue = value;
});
}
// update value for your sibling
private communicateValue(value: string): void {
siblingDataExchangeService.setInfo(value);
}

PS:不要忘记在相应的模块中提供新服务!

import { SiblingDataExchangeService } from 'sibling-data-exchange.service';
@NgModule({
imports: [
...
],
exports: [
...
],
declarations: [
...
],
providers: [
SiblingDataExchangeService
]

现在,由于订阅了 BehaviorSubject,您的组件通过服务进行通信。

你说,你的 CategoryComponent 将一个列表传递给 ProductsComponent,一旦这个列表到达,getProducts()-方法就会被调用。好吧,为此,我真的建议使用我上面描述的服务通信。

然后,在产品组件中,您必须按如下方式修改订阅:

this.siblingDataExchangeService.getInfo().subscribe(value => {
// pass the value to your local list. (supposed you have one)
this.categories = value; 
// then call your method
this.getProducts(); 
});

这种变体也是可能的。在这里,您可以直接使用"值"作为参数。但因此,getProducts() 的签名必须匹配,例如

getProducts(categories: Array<string>)

然后

this.siblingDataExchangeService.getInfo().subscribe(value => {
this.getProducts(value); 
});

请注意,此订阅意味着每次都会调用您的方法 类别组件传入新列表。这应该正是您正在寻找的。

最新更新