发布时间:2023-02-07
Kendo UI致力于新的开发,来满足不断变化的需求。Kendo UI for Angular是Kendo UI系列商业产品的最新产品。Kendo UI for Angular是专用于Angular开发的专业级Angular组件。telerik致力于提供纯粹的高性能Angular UI组件,无需任何jQuery依赖关系。
虽然Kendo UI for Angular网格带有内置的过滤功能,但开发者有时需要允许用户控制过滤器显示的内容。
在本文中,我们将看到一个现实的示例,如何将过滤集成到应用程序中,以便当用户与应用程序的UI交付时,代码可以控制Grid(网格)显示哪些行。
Telerik_KendoUI产品技术交流群:726377843 欢迎一起进群讨论
我们的案例研究应用程序相对简单:有一个显示客户列表和国家下拉列表的Grid,当用户从下拉列表中选择一个国家时,代码将更新Grid来只显示该国家的客户。同时还将添加一个复选框,允许用户根据客户是否加入公司的忠诚度计划来选择客户,下面是UI的样子:
这个网格显示的Customer类有六个属性,然而对于这个案例研究,只对国家和inLoyaltyProgram属性感兴趣:
export class Customer { constructor( public id: number, public country: string, public inLoyaltyProgram: Boolean, public custName: string, public signUpDate: Date | null, public amountPurchased: number ) { } }
定义下拉列表和复选框的标记看起来像这样:
Select Country: <kendo-dropdownlist [data]="countries" defaultItem=”Pick a Country” (selectionChange)="CountryChange($event)"> </kendo-dropdownlist><br/><br/> Loyalty Program Only: <input type="checkbox" #loyalty kendoCheckBox (change)="LoyaltyChange($event)"/>
这些组件中的每一个都调用一个函数,并在用户更改组件的值时传递相关的事件对象。我将把这两个函数(CountryChange用于下拉列表,LoyaltyChange用于复选框)称为“过滤器函数”。
还需要两个客户数组,第一个数组是客户的“完整/未过滤”数组(可能从某些Web服务获取),但是我们将Grid绑定到第二个名为selectCustomers的数组,该数组显示当前选定的客户。最初我们是希望两个数组是相同的,所以我像这样初始化数组:
public customers: Customer[]; public selectCustomers: Customer[] = []; this.customers = [new Customer(1, "Peter Vogel", "USA", new Date(), 3250, true), …more Customers… ]; this.selectCustomers = this.customers;
最后用kendo-Grid组件定义Grid,并将Grid绑定到selectCustomers列表。同时还定义了Grid中的每一列,以便可以将每一列绑定到公司的一个属性,控制列中的数据格式,在列的顶部设置标题,并指定列的宽度。
所有的Grid标记看起来像这样:
<kendo-Grid [kendoGridBinding]="selectCustomers" [height]="250" (filterChange)= "this.filterCustomers($event)"> <kendo-Grid-column title="Id" field="id" [width]="50"></kendo-Grid-column> <kendo-Grid-column title="Name" field="custName" [width]="150"></kendo-Grid-column> <kendo-Grid-column title="Purchase" field="amountPurchased" [width]="125" format="99,999"></kendo-Grid-column> <kendo-Grid-column title="Signed Up" field="signUpDate" [width]="200" format="MMM-dd-yyyy"></kendo-Grid-column> <kendo-Grid-column title="In Program" field="inLoyaltyProgram" [width]="100"></kendo-Grid-column> </kendo-Grid>
要过滤一个网格,需要一个CompositeFilterDescriptor,它包含0个或多个FilterDescriptors(如果CompositeFilterDescriptor没有FilterDescriptors,那么没有行被“过滤掉”)。我们需要导入这两个类,因为稍后还需要filterBy函数,所以也将导入它:
import { CompositeFilterDescriptor, FilterDescriptor, filterBy } from "@progress/kendo-data-query";
FilterDescriptor有三个属性对这个案例研究很重要:
将两个filterdescriptor设置为属性是有意义的,一个用于按客户筛选,一个用于按忠诚度计划筛选,这可以将每个FilterDescriptor上的所有属性设置为一些默认值。这可以简化后面的代码:当用户在下拉列表和复选框中进行更改时,两个过滤器函数中唯一需要更改的属性是每个FilterDescriptor的value属性。
这两个FilterDescriptor属性的声明如下:
countryFilter: FilterDescriptor = {field:"country", operator:"eq", value:"Pick a country"}; loyaltyFilter: FilterDescriptor = {field:"inLoyaltyProgram", operator:"eq", value:false};
现在在过滤器函数中,只是从传递给函数的事件中检索当前值,并在适当的FilterDescriptor中设置value属性。这对于下拉列表来说非常简单,因为下拉列表的选择事件只是传递当前在下拉列表中选择的值:
CountryChange(e:string): void { this.countryFilter.value = e; this.CreateFilter(); }
对于复选框来说有点复杂,因为该函数传递的是一个Event对象。然而,可以将事件对象的目标属性转换为HtmlInput对象,然后检索元素的checked属性来设置FieldDescriptor的value属性:
LoyaltyChange(e:Event): void { const cbx = e.target as HTMLInputElement; this.loyaltyFilter.value = cbx.checked; this.CreateFilter(); }
在每个函数末尾调用的CreateFilter函数将两个FilterDescriptors组装成一个CompositeFilter,并实际过滤数据(您将在接下来的短暂中断后看到该函数)。
我们只给了用户筛选客户是否在忠诚度计划中的功能——用户不能筛选不在计划中的客户,这是因为在用户界面中使用了一个复选框来控制这个过滤选项。
虽然Kendo UI下拉列表有一个“不确定”的状态,允许复选框表明它还没有被触摸,一旦用户选中复选框,复选框只在true和false状态之间交替。如果我使用false将列表过滤到那些不在忠诚度计划中的客户,那么一旦用户选中复选框,用户将被限制在使用true查看计划中的客户和使用false查看计划中没有的客户之间切换,但不会是所有客户。
如果想让用户可以选择忠诚度计划过滤器的三种状态(在忠诚度计划中,不在忠诚度计划中,并且“不关心忠诚度计划”),我们将不得不使用一些其他组件组合(可能是两个单选按钮或另一个下拉列表)。
现在已经构建了FilterDescriptors,将它们组合在CreateFilter方法中。
在CreateFilter函数中,检查了两个FilterConditions中四个可能的值组合,并使用它们来加载CompositeFilterDescriptor的Filters数组:
当Filters条件中确实有多个项时,必须将CompositeFilterDescriptor的逻辑属性设置为” and “或” or “,以指示如何组合过滤器。当使用两个过滤器时,希望它们是“and”一起的,所以在CompositeFilterDescriptor中已经将逻辑属性设置为“and”。
在只有一个(或没有)过滤器的情况下,将逻辑属性设置为什么并不重要——将属性设置为” and “就可以了。
下面是构建ComponsiteFilterDescriptor的代码(可能有更复杂的方法,但这个版本的优势是显而易见的):
GridFilter:CompositeFilterDescriptor = {filters:[],logic:"and"}; CreateFilter() { if (!this.loyaltyFilter.value && this.countryFilter.value == "Pick a country") { this.GridFilter.filters = []; } if (!this.loyaltyFilter.value && this.countryFilter.value != "Pick a country") { this.GridFilter.filters = [this.countryFilter]; } if (this.loyaltyFilter.value && this.countryFilter.value == "Pick a country") { this.GridFilter.filters = [this.loyaltyFilter]; } if (this.loyaltyFilter.value && this.countryFilter.value != "Pick a country") { this.GridFilter.filters = [this.loyaltyFilter, this.countryFilter]; }
在CreateFilter函数的末尾,终于可以将完整的客户数组过滤到Grid所绑定的selectCustomers数组中。为此,使用前面导入的filterBy函数,传递完整的customers数组和CompositeFilterDescriptor,使用调用filterBy的结果来设置Grid绑定到的数组的代码如下所示:
… { this.GridFilter.filters = [this.loyaltyFilter, this.countryFilter]; } this.selectCustomers = filterBy(this.customers, this.GridFilter); }
现在,当用户与应用程序UI的其他部分交互时,代码将控制Grid显示的行。