In the early days of AngularJS, one of the most celebrated features was the ability to filter and sort data on the page using only template variables and filters. The magic of two-way data binding won over many converts to AngularJS and helped it spread like wildfire.
在AngularJS的早期,最著名的功能之一就是能够仅使用模板变量和过滤器对页面上的数据进行过滤和排序。 双向数据绑定的魔力赢得了许多转换为AngularJS的转换,并帮助其像野火一样传播。
Today, though, the front end world prefers one-way data binding, and those orderBy
and filter
filters have ridden off into the sunset with the advent of Angular. (Note: throughout this article I'll be using "AngularJS" to refer to 1.x and just "Angular" to refer to 2+.)
但是,今天,前端世界更喜欢单向数据绑定,并且随着Angular的出现,那些orderBy
和filter
器已经消失了。 (注意:在本文全文中,我将使用“ AngularJS”来指代1.x,而仅使用“ Angular”来指代2+。)
But how are we supposed to achieve that same effect? The answer lies in our components, so let's look at an ngUpgrade project and learn how to do this!
但是我们应该如何达到同样的效果呢? 答案在于我们的组件,因此让我们看一个ngUpgrade项目并学习如何做!
![](https://cdn.scotch.io/1/VPlsSVxRQGCXrq4cnQtA_0EZmP93.png) ![](https://cdn.scotch.io/1/VPlsSVxRQGCXrq4cnQtA_0EZmP93.png)我们的出发点 (Our Starting Point)
We're going to step through updating the template of a freshly rewritten component. Then, we'll add sorting and filtering to restore all of the functionality it had in AngularJS. This is a key skill to develop for the ngUpgrade process.
我们将逐步更新新编写的组件的模板。 然后,我们将添加排序和过滤以恢复它在AngularJS中具有的所有功能。 这是为ngUpgrade流程开发的一项关键技能。
To get started, take a moment to clone the sample project for my course Upgrading AngularJS (don't forget to run npm install
in both the public
and server
folders). Check out this commit for our starting point:
要开始,花一点时间来复制我的课程示例项目升级AngularJS (不要忘了运行npm install
在两个public
和server
文件夹)。 查看此提交作为我们的起点:
git checkout 9daf9ab1e21dc5b20d15330e202f158b4c065bc3
This sample project is an ngUpgrade hybrid project that uses both AngularJS 1.6 and Angular 4. It's got a working Express API and a Webpack builds for both development and production. Feel free to explore, fork it, and use the patterns in your own projects. If you'd like to look at a version of this project that uses Angular 5, check out this repo. For the purposes of this tutorial, the differences between the two versions won't matter (I'll point out anything minor).
该示例项目是一个使用AngularJS 1.6和Angular 4的ngUpgrade混合项目。它具有有效的Express API和可用于开发和生产的Webpack构建。 随意探索,使用它并在您自己的项目中使用这些模式。 如果您想查看使用Angular 5的该项目的版本,请查看此repo 。 就本教程而言,两个版本之间的差异无关紧要(我会指出一些次要问题)。
(And, if you're confused on how to set up an ngUpgrade project with working builds, head over to Upgrading AngularJS. I've got detailed, step-by-step videos in the course to help you.)
(而且,如果您对如何通过有效的构建来建立ngUpgrade项目感到困惑,请继续学习AngularJS的升级 。本课程中有详细的分步视频可以为您提供帮助。)
替换AngularJS语法 (Replace the AngularJS Syntax)
At this stage in our application, our orders component is rewritten in Angular, with all of its dependencies injected and resolved. If we were to try to run our application, though, we'd see errors in the console indicating problems with our template. That's what we need to fix first. We're going to replace the AngularJS syntax in the orders template (orders/orders.html
) so we can get the route loading and the orders displayed on the page. We'll fix the filtering and sorting next.
在我们应用程序的此阶段,我们的订单组件将用Angular重写,并注入并解决所有依赖项。 但是,如果我们尝试运行应用程序,则会在控制台中看到错误,表明模板存在问题。 这就是我们首先需要解决的问题。 我们将替换订单模板( orders/orders.html
)中的AngularJS语法,以便获得路线加载和页面上显示的订单。 接下来,我们将解决过滤和排序问题。
The first thing we need to do is get rid of all of the instances of $ctrl
in this template. They're no longer necessary in Angular. We can just do a find and replace to find for $ctrl.
(note the dot), and replace it with nothing.
我们需要做的第一件事是摆脱此模板中$ctrl
的所有实例。 在Angular中不再需要它们。 我们可以进行查找并替换以查找$ctrl.
(注意点),然后将其替换为任何内容。
Now let's replace the data-ng-click
in our button on line 13. In Angular, instead of ng-click
, we just use the click
event, with parentheses to indicate that it's an event. Brackets indicate an input, and parentheses indicate an output or an event.
现在,让我们替换第13行中的按钮中的data-ng-click
。在Angular中,我们仅使用click
事件,而不是ng-click
,并用括号将其表示为事件。 方括号表示输入,括号表示输出或事件。
<button type="button" (click)="goToCreateOrder()" class="btn btn-info">Create Order</button>
We're just saying here that on the click event, fire off the goToCreateOrder
function on our orders component.
我们在这里只是说,在click事件上,在订单组件上goToCreateOrder
函数。
Before we keep going, let's take a minute to prove that our component is actually loading. Comment out the whole div
that loads our orders (from line 17 on). To run the application, open a terminal and run the following commands:
在继续之前,让我们花一点时间来证明我们的组件实际上正在加载。 注释掉加载订单的整个div
(从第17行开始)。 要运行该应用程序,请打开一个终端并运行以下命令:
cd server
npm start
That will start the Express server. To run the Webpack dev server, open another terminal and run:
这将启动Express服务器。 要运行Webpack开发服务器,请打开另一个终端并运行:
cd public
npm run dev
(You can keep these processes running for the remainder of this tutorial.)
(您可以在本教程的其余部分中保持这些进程的运行。)
You should see that our application is loading again. If you go to the orders route, you'll see that the orders component is displaying correctly.
您应该看到我们的应用程序正在重新加载。 如果转到订单路线,则会看到订单组件显示正确。
We can also click the Create Order button and it will send us correctly over to our Create Order route and form.
我们还可以单击“创建订单”按钮,它将正确地将我们发送到“创建订单”路线和表单。
Okay, let's get back to the HTML. Un-comment that div
(our app will be broken again).
好的,让我们回到HTML。 取消对该div
的注释(我们的应用将再次损坏)。
Let's replace all of the rest of the instances data-ng-click
with the (click)
event handler. You can either use Find & Replace or just use your editor's shortcut for selecting all occurrences (in VS Code for Windows, this is Ctrl+Shift+L).
让我们用(click)
事件处理程序替换所有其余实例data-ng-click
。 您可以使用“查找并替换”,也可以只使用编辑器的快捷方式来选择所有出现的对象(在Windows的VS Code中,这是Ctrl + Shift + L)。
Next, replace all of the occurrences of data-ng-show
with *ngIf
. There's actually no direct equivalent to ng-show
in Angular, but that's okay. It's preferable to use *ngIf
, because that way you're actually adding and removing elements from the DOM instead of just hiding and showing them. So, all we need to do is find our data-ng-show
s and replace with *ngIf
.
接下来,将所有出现的data-ng-show
替换为*ngIf
。 实际上在Angular中没有直接等效于ng-show
的方法,但这没关系。 最好使用*ngIf
,因为那样您实际上是在DOM中添加和删除元素,而不仅仅是隐藏和显示它们。 因此,我们所需要做的就是找到我们的data-ng-show
并替换为*ngIf
。
Finally, we need to do two things to fix our table body. First, replace data-ng-repeat
with *ngFor="let order of orders"
. Note that we're also removing the orderBy
and filter
filters in that line so that the entire tr
looks like this:
最后,我们需要做两件事来固定桌子主体。 首先,将data-ng-repeat
替换为*ngFor="let order of orders"
。 请注意,我们还删除了该行中的orderBy
和filter
器,因此整个tr
如下所示:
<tr *ngFor="let order of orders">
Second, we can delete the data-ng
prefix before the href
link to the order detail route. AngularJS is still handling the routing here, but we don't need to use that prefix anymore since this is now an Angular template.
其次,我们可以删除href
链接到订单详细信息路径之前的data-ng
前缀。 AngularJS仍在此处处理路由,但是我们现在不再需要使用该前缀,因为它现在是Angular模板。
If we look at the application again, you can see that the orders are loading correctly on the screen (yay!):
如果我们再次查看该应用程序,则可以在屏幕上看到订单正在正确加载(是!):
There are a couple things wrong with it, of course. The sorting links no longer work, and now our currency is kind of messed up because the currency pipe in Angular is slightly different than its AngularJS counterpart. We'll get to that. For now, this is a great sign, because it means that our data is getting to the component and loading on the page. So, we've got the basics of this template converted to Angular. Now we're ready to tackle our sorting and filtering!
当然,有几处错误。 排序链接不再起作用,现在我们的货币有点混乱了,因为Angular中的货币管道与AngularJS中的货币管道略有不同。 我们将解决这个问题。 目前,这是一个好兆头,因为这意味着我们的数据正在到达组件并加载到页面上。 因此,我们已经将该模板的基础知识转换为Angular。 现在,我们准备解决我们的排序和过滤!
添加排序 (Adding Sorting)
We've got our orders loading on the screen, but we don't have a way of ordering or sorting them yet. In AngularJS, it was really common to use the built-in orderBy
filter to sort the data on the page. Angular no longer has an orderBy
filter. This is because it's now strongly encouraged to move that kind of business logic into the component instead of having it on the template. So, that's what we're going to do here. (Note: we're going to be using plain old functions and events here, not a reactive form approach. This is because we're just trying to take baby steps into understanding this stuff. Once you've got the basics down, feel free to take it further with observables!)
屏幕上已经加载了订单,但是还没有订购或排序的方法。 在AngularJS中,通常使用内置的orderBy
过滤器对页面上的数据进行排序。 Angular不再具有orderBy
过滤器。 这是因为现在强烈建议将这种业务逻辑移入组件,而不是将其放在模板上。 因此,这就是我们在这里要做的。 (注意:我们将在这里使用简单的旧函数和事件,而不是被动形式的方法。这是因为我们只是试图采取一些简单的步骤来理解这些内容。一旦您掌握了基础知识,就可以可以自由地将其与可观察物更进一步!)
在组件中排序 (Sorting in the Component)
We already removed the orderBy
filter from ng-repeat
when we changed it to *ngFor
. Now we're going to make a sorting function on the orders component. We can use the click events on our table headers to call that function and pass in the property that we want to sort by. We're also going to have that function toggle back and forth between ascending and descending.
当我们将其更改为*ngFor
时,已经从ng-repeat
删除了orderBy
过滤器。 现在,我们将对订单组件进行排序。 我们可以使用表头上的click事件来调用该函数,并传入我们要作为排序依据的属性。 我们还将使该功能在上升和下降之间来回切换。
Let's open the orders component (./orders/orders.component.ts
) and add two public properties to the class. These are going to match the two properties that our template already references. The first one will be sortType
of type string
. The second one will be sortReverse
of type boolean
and we'll set the default value to false. The sortReverse
property just keeps track of whether to flip the order - don't think of it as a synonym for ascending or descending. *(Also, side note: in the sample code we mark this variable as private to demonstrate that it won't work with Angular's AOT compiler, which we cover later in the course. You can ignore that here and keep it public.) *
让我们打开订单组件( ./orders/orders.component.ts
),然后向该类添加两个公共属性。 这些将匹配我们的模板已经引用的两个属性。 第一个将是sortType
类型的string
。 第二个将是boolean
类型的sortReverse
,我们将默认值设置为false。 sortReverse
属性只是跟踪是否翻转顺序-不要认为它是升序还是降序的同义词。 *(此外,请注意:在示例代码中,我们将此变量标记为私有,以证明它不适用于Angular的AOT编译器,我们将在本课程的后面部分进行介绍。您可以在此处忽略它并使其保持公开。)*
So you now should have this after the declaration of the title in the class:
因此,您现在应该在该类中的标题声明之后使用以下代码:
sortType: string;
sortReverse: boolean = false;
Next, we'll add the function that we'll use with the Array.sort prototype function in JavaScript. Add this after the goToCreateOrder
function (but still within the class):
接下来,我们将添加与JavaScript中的Array.sort原型函数一起使用的函数。 将其添加到goToCreateOrder
函数之后(但仍在类中):
dynamicSort(property) {
return function (a, b) {
let result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result;
}
}
This dynamic sort function will compare the property value of objects in an array. The nested ternary function can be a little bit tricky to understand at first glance, but it's basically just saying that if the value of our property of A is less than B, return -1. Otherwise, if it's greater, return 1. If the two are equal, return 0.
此动态排序功能将比较数组中对象的属性值。 乍一看,嵌套三元函数可能有点棘手,但基本上只是说如果我们的A的属性值小于B,则返回-1。 否则,返回1。如果两者相等,则返回0。
Now, this isn't super sophisticated or deep comparison. There are way more sophisticated helper functions you could write to sort for you, and feel free to experiment with how you can break this one. It will do for our purposes, though, and you can just swap out this logic with whatever custom sorting logic you like.
现在,这不是超级复杂或深入的比较。 您可以编写一些更复杂的帮助程序函数来为您排序,并随意尝试如何打破这一功能。 但是,它将为我们的目的而做,您可以将此逻辑替换为您喜欢的任何自定义排序逻辑。
So that's our helper function. The sort function on the Array prototype can be passed a function that it can then use to compare items in an array. Let's make a function called sortOrders
on our class that takes advantage of that with the new dynamicSort
function:
这就是我们的辅助功能。 可以将Array原型上的sort函数传递给函数,然后可以使用该函数比较数组中的项目。 让我们在类上创建一个名为sortOrders
的函数,通过新的dynamicSort
函数利用该函数:
sortOrders(property) { }
The first thing we need to do is set the sortType
property on our class equal to the property that's passed in. Then we want to toggle the sortReverse
property. We'll have this:
我们需要做的第一件事是将类上的sortType
属性设置为与sortType
属性相等。然后,我们要切换sortReverse
属性。 我们将拥有:
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
}
Now we can call the sort
function on this.orders
, but pass in our dynamic sort function with our property:
现在我们可以在this.orders
上调用sort
函数,但是将我们的属性传递给我们的动态sort函数:
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
this.orders.sort(this.dynamicSort(property));
}
And there's one last thing we need to do. We need to modify our dynamicSort
function just a little bit to be able to reverse the order of the array for ascending or descending. To do this, we'll tie the result of the dynamicSort
to the sortReverse
property on the class.
我们需要做的最后一件事。 我们只需要稍微修改dynamicSort
函数就可以颠倒数组的升序或降序。 为此,我们将把dynamicSort
的结果dynamicSort
到类的sortReverse
属性上。
The first thing we'll do is declare a variable:
我们要做的第一件事是声明一个变量:
let sortOrder = -1;
let sortOrder = -1;
Then, we can check if our sortReverse
property on our class is true or false. If it's true, we'll set our sort order variable equal to 1:
然后,我们可以检查类上的sortReverse
属性是true还是false。 如果是真的,我们将排序顺序变量设置为等于1:
if (this.sortReverse) {
sortOrder = 1;
}
We're tying our functions together like this because we're only doing a simple toggle in our sort function for the sake of simplicity. To be more thorough, another approach would be to have a variable called sortDescending
instead of sortReverse
that's controlled through a separate function. If you go this route, you'll do the opposite -- sortOrder
would be 1 unless sortDescending
was true.
我们将这样的函数绑定在一起是因为为了简单起见,我们仅在sort函数中进行了简单的切换。 为了更全面,另一种方法是拥有一个名为sortDescending
的变量,而不是通过单独的函数控制的sortReverse
。 如果走这条路线,您将做相反的操作sortOrder
将为1,除非sortDescending
为true。
We could also combine these last two things into a ternary expression, but for the sake of clarity, I'm going to leave it a little bit more verbose. And then to just make our result the opposite of what it normally would be, I can just multiply result
by our sortOrder
. So our dynamicSort
function now looks like this:
我们也可以将最后两件事组合成一个三元表达式,但是为了清楚起见,我将使其更加冗长。 然后,为了使我们的结果与通常的结果相反,我可以将result
乘以我们的sortOrder
。 因此,我们的dynamicSort
函数现在如下所示:
dynamicSort(property) {
let sortOrder = -1;
if (this.sortReverse) {
sortOrder = 1;
}
return function(a, b) {
let result = a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
return result * sortOrder;
};
}
Again, this is a simple implementation of sorting, but I want to be sure you understand the key concepts of using a custom sorting function on your component.
同样,这是排序的简单实现,但是我想确保您了解在组件上使用自定义排序功能的关键概念。
让我们看看排序是否有效 (Let's see if the sort works)
So far, we've added a dynamicSort
helper function and a sortOrders
function to our class so that we can sort on our component instead of on our template.
到目前为止,我们已经在类中添加了dynamicSort
辅助函数和sortOrders
函数,以便我们可以对组件进行排序,而不是对模板进行排序。
To see if these functions are working, let's add a default sorting to our ngOnInit
function.
要查看这些功能是否正常工作,让我们向ngOnInit
函数添加默认排序。
Inside of our forkJoin
subscription, after the forEach
where we add the customer name property, let's call this.sortOrders
and pass in the total items property:
在forkJoin
订阅中,在forEach
之后添加客户名称属性,让我们调用this.sortOrders
并传入total items属性:
this.sortOrders('totalItems');
this.sortOrders('totalItems');
When the screen refreshes, you should see that the orders are being sorted by the total items (yay!).
屏幕刷新时,您应该看到订单正在按总项目排序(是!)。
Now we just need to implement this sorting on our template by calling the sortOrders
function in the from the table header links.
现在,我们只需要在表头链接中通过调用sortOrders
函数来对模板进行排序。
将排序添加到模板 (Add sorting to the template)
We've got our sortOrders
function working correctly on our orders component, which means we're now ready to add it to our template so that the table headers are clickable again.
我们的sortOrders
函数在orders组件上正常运行,这意味着我们现在可以将其添加到模板中,以便再次单击表头。
Before we do that, let's change the default sorting in our ngOnInit
function to just be ID:
在此之前,让我们将ngOnInit
函数中的默认排序更改为ID:
this.sortOrders('id');
this.sortOrders('id');
That's a little bit more normal than using the total items.
这比使用总计项目要正常一些。
Now we can work on our template. The first thing we want to do is call the sortOrders
function in all of our click events. You can select the instances of sortType =
and replace them with sortOrders(
. Then, you can replace the instances of ; sortReverse = !sortReverse
with a simple )
.
现在我们可以处理我们的模板了。 我们要做的第一件事是在所有单击事件中调用sortOrders
函数。 您可以选择sortType =
的实例,并用sortOrders(
。替换它们。然后,您可以将; sortReverse = !sortReverse
的实例替换为一个简单的)
。
We also need to fix two of the property names that we're passing in here, as well as in the *ngIf
instances. Replace the 3 instances of orderId
with id
and the 3 instances of customername
with customerName
.
我们还需要修复在此处以及*ngIf
实例中传递的两个属性名称。 将3个orderId
实例替换为id
,并将3个customername
实例替换为customerName
。
The last thing I need to do is wrap each of the href
tags in the headers in brackets so that Angular will take over and these links won't actually go anywhere. The click event will be the thing that's fired. So, the headers should follow this pattern:
我需要做的最后一件事是将每个href
标记包装在标题的方括号中,以便Angular接管,并且这些链接实际上不会到任何地方。 点击事件将被触发。 因此,标头应遵循以下模式:
<th>
<a [href]="" (click)="sortOrders('id')">
Order Id
<span *ngIf="sortType == 'id' && !sortReverse" class="fa fa-caret-down"></span>
<span *ngIf="sortType == 'id' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
Hop over to the browser and test out all of your table header links. You should see that each one of our properties now sorts, both in ascending and descending order. Awesome!
跳到浏览器并测试所有表头链接。 您应该看到,我们的每个属性现在都按升序和降序排序。 太棒了!
This is great, but we did lose one thing - our cursor is a selector, not a pointer. Let's fix that with some CSS.
很好,但是我们确实失去了一件事-光标是选择器,而不是指针。 让我们用一些CSS修复它。
修复光标 (Fix the Cursor)
We've got our sorting working correctly on our orders page, but our cursor is now a selector instead of a pointer, and that's annoying.
我们的订单页面上的排序工作正常,但是光标现在是选择器而不是指针,这很烦人。
There are a couple of different ways we could use CSS to fix this:
我们可以使用两种不同的方法来使用CSS来解决此问题:
- We could make a class in our main app SCSS file. 我们可以在主应用程序SCSS文件中创建一个类。
- We could write in-line CSS, although that's almost never preferable. 我们可以编写内联CSS,尽管这几乎从来都不是可取的。
- We could take advantage of Angular's scoped CSS using the styles option in the component decorator 我们可以使用组件装饰器中的styles选项来利用Angular的作用域CSS
We're going to go with the last option, because it's really simple and all we need to do is add one rule to our styles for this particular component.
我们将使用最后一个选项,因为它非常简单,我们要做的就是为该特定组件的样式添加一条规则。
Open up the orders component class again. In the component decorator, we can add a new property called styles
. Styles is an array of strings, but the strings are CSS rules. To fix our cursor, all we need to do is write out a rule that says that in a table row, if we have a link, then change the cursor property to pointer. Our decorator will now look like this:
再次打开订单组件类。 在组件装饰器中,我们可以添加一个称为styles
的新属性。 样式是字符串数组,但是字符串是CSS规则。 要修复游标,我们所需要做的就是写出一条规则,该规则说在表行中,如果有链接,则将cursor属性更改为pointer。 现在,我们的装饰器将如下所示:
@Component({
selector: 'orders',
template: template,
styles: ['tr a { cursor: pointer; }']
})
Now, when we hover over our row headers, you see we have the pointer cursor. What's cool about this approach is that this CSS rule won't affect any other components. It will just apply to our orders component!
现在,当我们将鼠标悬停在行标题上时,您将看到指针光标。 这种方法的优点在于,此CSS规则不会影响任何其他组件。 它将仅适用于我们的订单组件!
Now, let's see if we can do something about our filtering. That "filter filter" was removed from Angular, so we're going to have to be creative and come up with a way to implement it on our component.
现在,让我们看看是否可以对过滤进行一些处理。 该“过滤器过滤器”已从Angular中删除,因此我们将必须具有创造力,并提出一种在组件上实施该过滤器的方法。
添加过滤 (Add Filtering)
We're ready to replace our filter box that used to use the AngularJS filter to search through orders collection based on a string that we were searching. The AngularJS filter lived on our template and didn't require any code in our controller or component. Nowadays, that kind of logic in the template is discouraged. It's preferred to do that kind of sorting and filtering on our component class.
我们已经准备好替换以前使用AngularJS过滤器的过滤器框,该过滤器框基于我们正在搜索的字符串来搜索订单集合。 AngularJS过滤器位于我们的模板上,并且在控制器或组件中不需要任何代码。 如今,不鼓励在模板中使用这种逻辑。 最好对我们的组件类进行这种排序和过滤。
添加过滤功能 (Add a Filter Function)
Back in our component, we're going to make a new array of orders called filteredOrders
. Then we're going to pass our orders
array into a filter function that sets the filteredOrders
array. Finally, we'll use the filteredOrders
on our template in our *ngFor
instead of our original array. That way we're not ever modifying the data that comes back from the server, we're just using a subset of it.
回到我们的组件中,我们将创建一个新的订单数组,称为filteredOrders
。 然后,我们将orders
数组传递给一个用于设置filteredOrders
数组的filteredOrders
器函数。 最后,我们将在模板中的*ngFor
而不是原始数组中使用filteredOrders
。 这样,我们就永远不会修改从服务器返回的数据,而只是使用其中的一部分。
The first thing we'll do is declare the new property on our class :
我们要做的第一件事是在类上声明新属性:
filteredOrders: Order[];
Then, in our forkJoin
that sets our original array of orders, we can set the initial state of filteredOrders
to our orders array:
然后,在设置原始订单数组的forkJoin
中,我们可以将filteredOrders
的初始状态设置为我们的订单数组:
this.filteredOrders = this.orders;
this.filteredOrders = this.orders;
Now we're ready to add our function that will actually do the filtering for us. Paste this function in right after our sorting functions at bottom of our component:
现在,我们准备添加将实际为我们进行过滤的函数。 将此功能粘贴到组件底部的排序功能之后:
filterOrders(search: string) {
this.filteredOrders = this.orders.filter(o =>
Object.keys(o).some(k => {
if (typeof o[k] === 'string')
return o[k].toLowerCase().includes(search.toLowerCase());
})
);
}
Let's talk about what's going on in this function. First, we're giving the function a string property of search
. Then, we loop through our orders and then find all of the keys of the objects. For all of the keys, we're going to see if there are some
values of those properties that match our search term. This bit of JavaScript can look a little confusing at first, but that's basically what's going on.
让我们谈谈此函数的功能。 首先,我们为该函数提供search
的字符串属性。 然后,我们遍历订单,然后找到对象的所有键。 对于所有键,我们将查看这些属性中是否有some
与我们的搜索字词匹配的值。 刚开始时,这种JavaScript有点令人困惑,但是基本上就是这样。
Note that, in our if
statement, we're explicitly testing for strings. In our example right now we're just going to limit our query to strings. We're not going to try to deal with nested properties, number properties, or anything like that. Our search term will match on our customer name property, and if we ever choose to display our address or any other string property it'll search through those as well.
请注意,在我们的if
语句中,我们正在显式测试字符串。 现在在我们的示例中,我们仅将查询限制为字符串。 我们不会尝试处理嵌套属性,数字属性或类似的东西。 我们的搜索字词将与我们的客户名属性匹配,并且如果我们选择显示地址或任何其他字符串属性,它也会搜索这些内容。
Of course, we could also modify this function to test for numbers, or look through another layer of nested objects, and that's totally up to you. Just like with our sorting, we're going to start with a simple implementation and let you use your imagination to make it more complex.
当然,我们也可以修改此功能以测试数字,或查看另一层嵌套对象,这完全取决于您。 就像我们的排序一样,我们将从一个简单的实现开始,让您利用自己的想象力使其变得更复杂。
Speaking of the sortOrders
function, before we move on, we need to do one last thing on the component. We just need to modify sortOrders
to use filteredOrders
now and not our original orders
, because we want the filter to take priority over the sorting. Just change it to this:
说到sortOrders
函数,在继续之前,我们需要对组件做最后一件事。 我们只需要修改sortOrders
即可立即使用filteredOrders
而不是我们的原始orders
,因为我们希望过滤器优先于排序。 只需将其更改为:
sortOrders(property) {
this.sortType = property;
this.sortReverse = !this.sortReverse;
this.filteredOrders.sort(this.dynamicSort(property));
}
Now we're ready to implement this filtering on the template.
现在,我们准备在模板上实现此过滤。
将过滤添加到模板 (Add Filtering to the Template)
Let's move back to our template and fix it up to use our filtering.
让我们回到模板并对其进行修复以使用我们的过滤。
The first thing we need to do is replace data-ng-model
. Instead of that, we're going to use the keyup
event, so we'll write, “keyup” and surround it parentheses ((keyup)
). This is a built-in event in Angular that lets us run a function on the key up of an input. Since we named our function filterOrders
, which used to be the name of the property that we were passing into the AngularJS filter, we just need to add parentheses next to it. Our input looks like this so far:
我们需要做的第一件事是替换data-ng-model
。 取而代之的是,我们将使用keyup
事件,因此我们将编写“ keyup”并将其括在括号( (keyup)
)中。 这是Angular中的内置事件,使我们可以在输入的键上运行函数。 由于我们命名函数filterOrders
,该函数曾经是要传递到AngularJS过滤器中的属性的名称,因此我们只需要在其旁边添加括号即可。 到目前为止,我们的输入看起来像这样:
<input type="text" class="form-control" placeholder="Filter Orders (keyup)="filterOrders()">
But what do we pass into the filter orders function? Well, by default, events pass something called $event
. This contains something called a target
, which then contains the value of the input. There's one problem with using $event
. It's very difficult to keep track of those nebulous types because target.value
could really be anything. This makes it tough to debug or know what type of value is expected. Instead, Angular has a really nifty thing we can do, which is to assign a template variable to this input.
但是我们要传递给过滤器订单功能什么呢? 好吧,默认情况下,事件传递一个称为$event
东西。 它包含一个叫做target
东西,然后包含输入的值。 使用$event
有一个问题。 跟踪那些模糊的类型非常困难,因为target.value
确实可以是任何东西。 这使得调试或知道期望值的类型变得困难。 相反,Angular有一件我们可以做的非常漂亮的事情,就是为该输入分配一个模板变量 。
Luckily, Angular provides a really easy way to do this. After our input tag, we can add the hash sign (#) and then the name of our desired model. Let's call it #ordersFilter
. It really doesn't matter where in the tag you put this or what you call it, but I like to put it after the input so that it's easy to catch which model is associated with which input if I just glance down the page.
幸运的是,Angular提供了一种非常简单的方法来执行此操作。 在输入标签之后,我们可以添加井号(#),然后添加所需模型的名称。 我们称之为#ordersFilter
。 将该标签放置在标签中的什么位置或调用它的名称确实无关紧要,但是我喜欢将其放置在输入之后,以便轻松浏览一下哪个模型与哪个输入相关联。
Now I can pass that variable into our filterOrders
function on the keyup
event. We don't need the hash symbol before it, but we do need to add .value
. This will pass the actual value of the model and not the entire model itself. Our finished input looks like this:
现在,我可以在keyup
事件中将该变量传递到我们的filterOrders
函数中。 我们不需要在其前面的哈希符号,但是我们确实需要添加.value
。 这将传递模型的实际值,而不是整个模型本身。 我们完成的输入如下所示:
<input #ordersFilter type="text" class="form-control"
placeholder="Filter Orders" (keyup)="filterOrders(ordersFilter.value)">
Finally, we need to modify our *ngFor
to use the filteredOrders
array instead of the regular orders
array:
最后,我们需要修改*ngFor
以使用filteredOrders
数组而不是常规的orders
数组:
<tr *ngFor="let order of filteredOrders">
都行吗? (Does it all work?)
You can see how much cleaner our template is now that our filtering and sorting is in the component.
您现在可以看到组件中的过滤和排序功能使模板更加干净。
Now let's check this out in the browser. If you enter some text in the box, like "sally," you should see that our orders are changing and that the sorting works on top of it:
现在,让我们在浏览器中检查一下。 如果您在框中输入一些文字(例如“ sally”),则应该看到我们的订单正在更改,并且排序在其顶部进行:
Now we've just got one last thing we need to do on this component - fix the currency pipe.
现在,我们需要在此组件上做的最后一件事-修复货币管道。
奖励:修复货币管道 (Bonus: Fix the Currency Pipe)
Our final touch is to update the former currency filter, which is now called the currency pipe in Angular. We just need to add a couple of paramters to the pipe in the template that we didn't have to specify in AngularJS. This part differs if you're using Angular 4 or Angular 5:.
我们的最后一步是更新以前的货币过滤器,现在将其称为Angular中的货币管道 。 我们只需要在模板中的管道中添加几个参数,而不必在AngularJS中指定。 如果您使用的是Angular 4或Angular 5,则此部分有所不同。
In Angular 4, do this: <td>{{order.totalSale | currency:'USD':true}}</td>
在Angular 4中,执行以下操作: <td>{{order.totalSale | currency:'USD':true}}</td>
<td>{{order.totalSale | currency:'USD':true}}</td>
In Angular 5+, do this: <td>{{order.totalSale | currency:'USD':'symbol'}}</td>
在Angular 5+中,请执行以下操作: <td>{{order.totalSale | currency:'USD':'symbol'}}</td>
<td>{{order.totalSale | currency:'USD':'symbol'}}</td>
The first option is the currency code (there's lots, you're not limited to US dollars!). The second one is the symbol display. In Angular 4, this is a boolean that indicates whether to use the currency symbol or the code. In Angular 5+, the options are symbol
, code
, or symbol-narrow
as strings.
第一种选择是货币代码(有很多,您不仅限于美元!)。 第二个是符号显示。 在Angular 4中,这是一个布尔值,指示是使用货币符号还是使用代码。 在Angular 5+中,选项是symbol
, code
或symbol-narrow
作为字符串。
You should now see the expected symbol:
现在,您应该看到预期的符号:
And we're done! To see the finished code, check out this commit.
我们完成了! 要查看完成的代码, 请签出此commit 。
从这往哪儿走 (Where to Go From Here)
You did a great job sticking with this to the end! Here's what we've accomplshed in this guide:
坚持到底,您做得很棒! 这是本指南中的内容:
- Replacing AngularJS template syntax with Angular syntax 用Angular语法替换AngularJS模板语法
- Moving sorting to the component 将排序移至组件
- Using scoped CSS styles 使用范围CSS样式
- Moving filtering to the component 将过滤移至组件
- Replacing the AngularJS currency filter with the Angular currency pipe 用Angular货币管道替换AngularJS货币过滤器
Wow! That's a lot - you should feel super proud!
哇! 太多了-您应该感到非常自豪!
Where should you go from here? There are lots of things you could do:
你应该从这里去哪里? 您可以做很多事情:
- Make the sorting more sophisticated (for example: should the ordering reset or stay the same when the user clicks a new header?) 使排序更加复杂(例如:当用户单击新的标题时,排序应该重置还是保持不变?)
- Make the filtering more sophisticated (search for numbers or nested properties) 使过滤更加复杂(搜索数字或嵌套属性)
- Change to a reactive approach. You could listen to an observable of value changes instead of the
keyup
function and do sorting and filtering in there. Using observables would also let you do really cool things like debounce the input! 更改为被动方法。 您可以监听可观察到的值变化,而不是keyup
函数,然后在其中进行排序和过滤。 使用可观察变量还可以使您做一些非常酷的事情,例如对输入进行去抖动!
If you love this guide, I’ve got 200+ detailed videos, quiz questions, and more for you in my comprehensive course Upgrading AngularJS. I created it for everyday, normal developers and it’s the best ngUpgrade resource on the planet. Head on over and sign up for our email list to get yourself a free Upgrade Roadmap Checklist so you don’t lose track of your upgrade prep. And, while you’re there, check out our full demo.
如果您喜欢本指南,则在我的综合课程《 升级AngularJS》中 ,有200多个详细的视频,测验问题以及更多内容供您选择。 我为日常的普通开发人员创建了它,它是地球上最好的ngUpgrade资源。 前往并注册我们的电子邮件列表,以获取免费的升级路线图清单,以免您忘记升级准备。 而且,当您在那里时,请查看我们的完整演示。
See you next time, Scotchers!
下次见,苏格兰人!
翻译自: https://scotch.io/tutorials/upgrade-angularjs-sorting-filters-to-angular