javascript Angular 2 - 单击时更改图标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45829915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angular 2 - Change icon on click
提问by AJ_
I have a materialize collapsible list in my angular2 application. What I want to do, is change the icon of the list item when someone clicks on it. So far I attached a click event onto the list. When I print this out to the console, I see the element reference. So I'm able to look down the child nodes and find the li element that has the active class attached to it. My thinking is that once I find the active element I can just change the child node's icon to the new one. This way I don't change all the icons. However, when I try to set the icon to the new icon, I get TypeError: 0 is read-only. Does anyone know how to do this via angular 2?
我的 angular2 应用程序中有一个实现可折叠列表。我想要做的是当有人点击它时更改列表项的图标。到目前为止,我在列表中附加了一个点击事件。当我将其打印到控制台时,我看到了元素引用。因此,我可以查看子节点并找到附加了活动类的 li 元素。我的想法是,一旦找到活动元素,我就可以将子节点的图标更改为新的。这样我就不会更改所有图标。但是,当我尝试将图标设置为新图标时,我得到TypeError: 0 is read-only. 有谁知道如何通过 angular 2 做到这一点?
Plunker
https://plnkr.co/edit/jQWf7uIRZIwr4fhyFT03?p=preview
Plunker
https://plnkr.co/edit/jQWf7uIRZIwr4fhyFT03?p=preview
List
列表
<ul class="collapsible" data-collapsible="accordion">
<li>
<div class="collapsible-header"><i class="material-icons">filter_drama</i>First</div>
<div class="collapsible-body"><span>Lorem ipsum dolor sit amet.</span></div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">place</i>Second</div>
<div class="collapsible-body"><span>Lorem ipsum dolor sit amet.</span></div>
</li>
<li>
<div class="collapsible-header"><i class="material-icons">whatshot</i>Third</div>
<div class="collapsible-body"><span>Lorem ipsum dolor sit amet.</span></div>
</li>
</ul>
Desired Behavior: Basically I'm trying to make a drop down tree using materlize collapsible list. So you click on an item it changes its icon from a plus sign to a minus sign to simulate that you are expanding and contracting the item.
期望的行为:基本上我正在尝试使用 materlize 可折叠列表制作下拉树。因此,您单击一个项目,它会将其图标从加号更改为减号,以模拟您正在扩展和收缩该项目。
回答by BogdanC
Angular only solution (no jQuery needed)
仅 Angular 解决方案(不需要 jQuery)
You can simply toggle between two icons using a variable that you change on click:
您可以使用单击时更改的变量在两个图标之间简单地切换:
<div class="collapsible-header" (click)="showFirst=!showFirst"><i class="material-icons" *ngIf="showFirst">filter_drama</i><i class="material-icons" *ngIf="!showFirst">place</i>First Section</div>
If you want only to hide the icon, you do it the same way with only one icon:
如果你只想隐藏图标,你可以只用一个图标来做同样的事情:
<div class="collapsible-header" (click)="showSecond=!showSecond"><i class="material-icons" *ngIf="!showSecond">place</i>Second Section</div>
I forked your plunker and edited the changes here
我分叉了你的 plunker 并在这里编辑了更改
PS: On your plunker you were loading material icons from a httpurl, making chrome to refuse to load the resource. Change the url to httpsand it will work.
PS:在您的 plunker 上,您正在从httpurl加载材料图标,使 chrome 拒绝加载资源。将 url 更改为https,它将起作用。
Edit
编辑
@Judson Terrell - for me, Angular only solution looks cleaner
@Judson Terrell - 对我来说,只有 Angular 的解决方案看起来更干净
Angular only
仅角度
<div class="collapsible-header" (click)="!show=show"><i class="material-icons" *ngIf="show">filter_drama</i>First</div>
Angular + jQuery
角度 + jQuery
html
html
<div class="collapsible-header" id="clickedId"><i class="material-icons" id="someId">filter_drama</i>First</div>
js
js
$( "#clickedId" ).click(function() {
$("#someId").toggleClass('someIconClass');
});
+ jQuery lib- and the presumable performance problems it induces
+ jQuery lib- 以及它可能导致的性能问题
Edit2 - after clearing the question
Edit2 - 清除问题后
What you want is achievable using CSS only:
您想要的仅使用 CSS 即可实现:
html:
html:
<div class="collapsible-header"><i class="material-icons more">expand_more</i><i class="material-icons less">expand_less</i>First Section</div>
css:
css:
.collapsible-header.active i.more{
display:none;
}
.collapsible-header.active i.less{
display:block;
}
.collapsible-header i.less{
display:none;
}
Updated plunker here
在这里更新了 plunker
Credit for idea to this answer
归功于这个答案的想法
回答by Judson Terrell
I would recommend toggling a class change based on the icon you want to add or remove someIconclass on the itag.
我建议根据您要someIcon在i标签上添加或删除类的图标来切换类更改。
In jQuery it's
在 jQuery 中它是
$(el).toggleClass('someIconClass')
I personally have not had much luck with elementRefetc in Angular 4 and have resorted to putting jQuery in my apps. Just a suggestion (not best practice I am sure someone will argue)
我个人elementRef在 Angular 4 中没有太多运气,并且已经诉诸于将 jQuery 放入我的应用程序中。只是一个建议(不是最佳实践,我相信有人会争论)
回答by diopside
I peeked at the code for Angular materialize, and it appears its a sort of angular 'shim' that uses jQuery and some other workarounds to make this CSS/JS framework work properly in Angular 2. That makes something like what you want a little trickier than it would be if it was pure Angular 2.
我偷看了 Angular 实现的代码,它似乎是一种使用 jQuery 和其他一些变通方法使这个 CSS/JS 框架在 Angular 2 中正常工作的角度“垫片”。这使得像你想要的东西有点棘手与纯 Angular 2 相比。
The 'Angular way', would be to have each menu item a separate component that keeps track of its own state. It would keep track of whether its expanded or collapsed, and it could even output an event based on that state, which would make changing the icon trivial. It could even keep track of the number of times its been clicked and slowly change colors as its clicked more and more. Stuff like that is really easy when everything is encapsulated in its own component.
“角度方式”是让每个菜单项都有一个单独的组件来跟踪自己的状态。它会跟踪它是展开还是折叠,甚至可以根据该状态输出一个事件,这将使更改图标变得微不足道。它甚至可以跟踪它被点击的次数,并随着点击次数的增加而慢慢改变颜色。当一切都封装在自己的组件中时,这样的事情真的很容易。
In lieu of that you'll have to manage the menu states manually, using something like an array of booleans. The problem with this is that you'll have 2 separate view states managed in different places for the same thing - and they may not always be in sync. One in the component for your icons, and one in ... wherever that materialize code is coming from ... The materialize code may be using a CSS :active selector to change something on click, and you may use a (click) handler in Angular to change something on a click, and those 2 things aren't always guaranteed to be in sync for a number of reasons. So if they ever get out of sync the icons will be backwards and it will be hard to fix.
取而代之的是,您必须手动管理菜单状态,使用类似布尔数组的东西。这样做的问题是,对于同一件事,您将在不同的地方管理 2 个单独的视图状态 - 它们可能并不总是同步的。一个在你的图标的组件中,一个在......物化代码来自哪里......物化代码可能使用 CSS :active 选择器在点击时更改某些内容,你可以使用(点击)处理程序在 Angular 中单击即可更改某些内容,并且由于多种原因,这两项内容并不总是能保证同步。因此,如果它们不同步,图标将向后移动,并且很难修复。
In fact, this may be a situation where it actually iseasier to use jQuery from within Angular, simply bc the technology you are working with uses jQuery as well.
事实上,这可能是一种情况,它实际上是更容易使用jQuery从内部角度,只要你BC与用途jQuery的工作,以及该技术。
But if you reaalllly want it to work in Angular using this framework, here's how you could do it, just be forewarned that it will be very fragile.
但是,如果你真的希望它使用这个框架在 Angular 中工作,那么你可以这样做,只是预先警告它会非常脆弱。
I would create an array of objects in your component that represented the menu. Each object could hold its menu text, expanded text, and a boolean representing its expanded/collapsed state.
我会在你的组件中创建一个代表菜单的对象数组。每个对象都可以保存其菜单文本、展开的文本和一个代表其展开/折叠状态的布尔值。
You could define an interface outside the component to give you type checking on this if you wanted.
如果需要,您可以在组件外部定义一个接口,以便对此进行类型检查。
import { Component } from '@angular/core'
export interface CollapsibleItem {
label: string;
text: string;
state: boolean;
}
@Component({
selector:'whateveryouwant',
template: `
<ul class="collapsible" data-collapsible="accordion">
<li *ngFor="let item of menuItems; let i = index" (click)="menuClick(i)">
<div class="collapsible-header">
<i class="material-icons" *ngIf="item.state"> minus-icon </i>
<i class="material-icons" *ngIf="!item.state"> plus-icon </i>
{{ item.label }}
</div>
<div class="collapsible-body">
<span> {{ item.text }} </span>
</div>
</li>
`,
styles: ['']
})
export class YourComponentName {
constructor() { }
menuItems: CollapsibleItem[] = [
{ label: 'First', text: 'Lorem Ipsum', state: false },
{ label: 'Second', text: 'Lorem Ipsum', state: false },
{ label: 'Third', text: 'Lorem Ipsum', state: false },
];
menuClick(clickedItem: number) {
this.menuItems[clickedItem].state = !this.menuItems[clickedItem].state // flips the boolean value for the clicked item
for (let item of this.menuItems) {
if ( item !== this.menuItems[clickedItem] ) {
item.state = false;
}
}
// the for loop goes through the array and sets each item to false *if* its not the item that was clicked
}
}
See... by the time you get this working in Angular, you probably could have just built your own menu item components which would be easier to work with and far more scaleable :) You're creating an array of objects with several different properties per object and trying to manage them with each click. This is why components were created! You could create a simple wrapper component for each item, but I'm not sure how that would work since the materialize thing is using jQuery and CSS to select for stuff. It might be unpredictable using normal component view encapsulation.
看...当您在 Angular 中使用它时,您可能已经构建了自己的菜单项组件,它更易于使用且可扩展性更强:) 您正在创建具有多个不同属性的对象数组每个对象并尝试通过每次单击来管理它们。这就是创建组件的原因!您可以为每个项目创建一个简单的包装器组件,但我不确定这将如何工作,因为具体化事物使用 jQuery 和 CSS 来选择内容。使用普通的组件视图封装可能是不可预测的。
An alternative to switching out the icons with ngIf would be binding to the innerText property, and switching the text based on the state.
使用 ngIf 切换图标的替代方法是绑定到 innerText 属性,并根据状态切换文本。
<i class="material-icons" [innerText]="item.state ? 'plus-icon' : 'minus-icon'"></i>
But I'm not sure if the materialize framework would pick up on the changes to the innerText quickly or at all. Better to have both render, and switch between them, if you're trying to mix technologies like this.
但我不确定具体化框架是否会很快或根本不会接受对 innerText 的更改。如果您尝试混合这样的技术,最好同时进行渲染并在它们之间切换。
Also, i have no idea if 'plus-icon' or 'minus-icon' are how you would refer to those icons, but you probably get the point :p
另外,我不知道“加号图标”或“减号图标”是否是您引用这些图标的方式,但您可能明白这一点:p

