中间人模式的限制是必须有共同的父组件,在实战中搜索组件和商品列表组件没有共同的父组件(搜索组件的父组件是app, 而商品列表的组件的父组件是Home组件,所以两者没有共同的组件), 所以他们只能使用服务来实现传递。
父子组件的数据传递使用属性传值,而兄弟组件的数据传递是使用中间人模式。而两者之间没有直接关系则是用服务(两者共同的依赖)来联系。
b. 商品列表组件(是一个单独的组件,显示商品的列表)
2. 本次迭代的需求,当在搜索组件中填写相关的内容,然后单击"搜索"按钮,然后在商品的列表的组件中显示符合你选择的条件的商品,当都不选择时, 显示所有的 商品列表。
搜索组件和产品组件拥有共同的服务,服务的文件的名称为:product.service.ts,在这个组件中,要实现的是:
在这个服务的文件中首先要实现的功能是:
一,在服务中,含有向服务器发送http请求的函数
1 search(params: ProductSearchParams): Observable<Product[]> { 2 return this.http.get('/apa/products', {search: this.encodeParams(params)}).map((res) => res.json()); 3 } 4 5 private encodeParams(params: ProductSearchParams) { 6 let result: URLSearchParams; 7 result = Object.keys(params) 8 .filter(key => params[key]) 9 .reduce((sum:URLSearchParams, key:string) => { 10 sum.append(key, params[key]); 11 return sum; 12 }, new URLSearchParams()); 13 return result; 14 }
1 export class ProductSearchParams { 2 constructor( 3 public title: string, 4 public price: number, 5 public category: string 6 ){ 7 8 } 9 }
注解:这个服务中的search函数的参数是响应式表单返回的对象(第二个代码是搜索的对象的数据格式), 返回的数据的格式是流,他的泛型是Product的数组,返回的是通过http实现的流。encodeParams的函数的返回的是search的参数,数据的传递是通过url地址传参的格式,实现url的参数的组合。
二,
1 searchEvent: EventEmitter<ProductSearchParams> = new EventEmitter();
在product.service.ts中定义了发射器,EventEmitter既可以实现数据的发射也可以实现数据的接收。在这个文件中实现事件发射是因为使搜索组件和商品组件实现解耦合。
三,在搜索组件中给搜索按钮添加click事件,通过响应式表单实现,此时要注意的是key值的统一,即表单的字段的key和搜索表单的formControlName的名字key值要一样,在这个文件中,不把EventEmitter添加到这个文件中的作用是实现搜索组件和商品组件的解耦:
1 onSubmit() { 2 const valid: boolean = this.formModel.valid; 3 if (valid) { 4 console.log(this.formModel.value); 5 this.productService.searchEvent.emit(this.formModel.value); // 事件的发射 6 } 7 }
四,在商品的组件中实现对搜索组件发射的流的订阅:
1 ngOnInit() { 2 // 首次登陆的时候对商品的展示 3 this.productService.getProducts() 4 .subscribe(data => { 5 this.products = data; 6 }); 7 // 通过搜索按钮实现的发射的流的接收和订阅 8 this.productService.searchEvent.subscribe( 9 params => this.productService.search(params).subscribe(data => this.products = data) 10 ) 11 }
在上述的函数中,实现了对搜索组件中搜索按钮触发的单击事件的订阅,在订阅的函数中,通过获取了事件流的数据,即搜索的参数,在发射事件的订阅函数中实现对
获取http的流的触发,即订阅服务中的search事件函数,通过subscribe来触发从服务器中获取数据。
注: 1. 在获取的products数组展示在模板中,由于是从服务器中获取的数据,是异步的操作,所以存在页面的展示的时候,后端的数据仍然没有返回的时候,所以在模板上使用(?.)的格式来展示数据,而*ngFor指令则不需这么做,是因为它自己有自己的判断空值的操作,所以不会报错。
五,Node服务器的设置的代码的编写:
1 app.get('/apa/products',function(req, res) { 2 var result = products; 3 var params = req.query; 4 console.log(params); 5 if (params.title) { 6 result = result.filter(function(p) { 7 return p.title.indexOf(params.title) != -1; 8 }) 9 } 10 11 if (params.price && result.length > 0) { 12 result = result.filter(function(p) { 13 return p.price <= params.price; 14 }) 15 } 16 17 if (params.category && params.category != '-1' && result.length > 0) { 18 result = result.filter(function(p) { 19 console.log(p.categories); 20 console.log(params.category); 21 return p.categories.indexOf(params.category) != -1; 22 }) 23 } 24 25 res.json(result); 26 })
本文中最重要的是通过服务来传送和订阅来实现数据的传递。将事件的触发和接收订阅都放在服务中,而使搜索组件和商品组件的解耦,而这是组件的编写的核心思想。