Angular 2 - 点击后更改图标

6
我在我的angular2应用程序中有一个materialize可折叠列表。我想要做的是,当有人点击时更改列表项的图标。到目前为止,我已经将一个点击事件附加到了列表上。当我将其打印到控制台时,我看到元素引用。因此,我能够查看子节点并找到具有活动类附加到其中的li元素。我的想法是一旦找到活动元素,我就可以将子节点的图标更改为新的图标。这样我就不会改变所有图标。然而,当我尝试将图标设置为新图标时,我得到了 TypeError: 0 is read-only 。有人知道如何通过angular 2来做到这一点吗?

Plunker
https://plnkr.co/edit/jQWf7uIRZIwr4fhyFT03?p=preview

列表
  <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>

期望的行为: 我正在尝试使用Materlize可折叠列表制作下拉树。当您单击某个项目时,它会将其图标从加号更改为减号,以模拟您正在展开和收缩该项目。

示例图片链接


你想在什么时候更改图标?当你打开一个部分时吗?你想用另一个图标替换它还是隐藏它? - BogdanC
我对材料图标不太熟悉,但是我猜想在'i'元素之间的文本是指定图标形状的吧?比如 whatshot、place 和 filter_drama?从plunker上看不清楚,因为它只显示文本,没有显示图标。 - diopside
@diopside - 在 material icon 导入中添加 https 而不是 http,否则 Chrome 将阻止该资源。 - BogdanC
3个回答

14

Angular解决方案(不需要jQuery)

您可以使用在单击时更改的变量来简单地在两个图标之间切换:

<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>

如果你只想隐藏图标,那么只需要像隐藏一个图标一样操作即可:
<div class="collapsible-header" (click)="showSecond=!showSecond"><i class="material-icons" *ngIf="!showSecond">place</i>Second Section</div>

我复制了你的Plunker并在这里进行了更改。
PS:在你的Plunker中,你正在从一个http URL加载材料图标,这会导致Chrome拒绝加载资源。将URL更改为https即可解决。

编辑

@Judson Terrell-对我来说,仅使用Angular看起来更简洁。

仅限Angular

<div class="collapsible-header" (click)="!show=show"><i class="material-icons" *ngIf="show">filter_drama</i>First</div>

Angular + jQuery

HTML

<div class="collapsible-header" id="clickedId"><i class="material-icons" id="someId">filter_drama</i>First</div>

JavaScript
$( "#clickedId" ).click(function() {
  $("#someId").toggleClass('someIconClass');
});

"

+ jQuery库-以及它可能引起的性能问题

编辑2-澄清问题后

你想要的可以仅使用CSS实现:

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:

.collapsible-header.active i.more{
display:none;
}

.collapsible-header.active i.less{
display:block;
}

.collapsible-header i.less{
display:none;
}

更新的 Plunker 可以在 这里 查看。
灵感来自于 这个答案

你的 Plunker 示例可以工作,但只有在单击该项以切换状态,然后再次单击该项以切换状态时才有效。如果我先单击第一项,然后单击第二项,则切换不按预期工作。有什么想法如何解决这个问题吗? - AJ_
@Andrew - 取决于你想要实现什么目标。这就是为什么我问你所需的行为。一旦你描述清楚了,我们就可以尝试找到解决方案。 - BogdanC
1
我更新了我的问题。如果不够清楚,请告诉我。 - AJ_

0
我建议根据您想要添加或删除的图标在标签上切换类更改someIcon类。
在jQuery中,它是
$(el).toggleClass('someIconClass')

就我个人而言,在 Angular 4 中,我并没有使用 elementRef 等方法取得太多成功,因此不得不在我的应用程序中使用 jQuery。这只是一个建议(并非最佳实践,我相信有人会提出反对意见)。


3
Angular的众神对这个答案感到愤怒,因为你对“Angular方式”的圣名TM不敬,这是亵渎。 - diopside
我相信会有人争辩说 Angular 并不完美。 - Judson Terrell
我必须同意你的观点,伙计。我自己也说不出更好的话了。 - Judson Terrell
Angular 和我之间有一种爱恨交加的关系。 - Judson Terrell
希望你正在使用 TypeScript :-) - Judson Terrell
显示剩余9条评论

0

我查看了Angular Materialize的代码,它似乎是一种使用jQuery和其他解决方法的Angular“shim”,以使CSS/JS框架在Angular 2中正常工作。这使得像您想要的东西有点棘手,如果它是纯Angular 2,那么就会更容易。

“Angular方式”是将每个菜单项作为单独的组件来跟踪其自身状态。它将跟踪其展开或折叠的状态,甚至可以基于该状态输出事件,这将使更改图标变得微不足道。它甚至可以跟踪被点击的次数,并随着点击次数的增加而缓慢改变颜色。当所有内容都封装在自己的组件中时,这样的东西真的很容易实现。

由于这个问题,你将不得不手动管理菜单状态,使用类似布尔数组的东西。问题在于,你将有两个不同的视图状态在不同的地方管理相同的事情 - 它们可能不总是同步的。一个在你的图标组件中,另一个在...那个materialize代码来自的地方... materialize代码可能使用CSS :active选择器在单击时更改某些内容,而你可能会在Angular中使用(click)处理程序在单击时更改某些内容,这两个东西不总是保证同步,因为有许多原因。所以如果它们失去同步,图标将会倒置,很难修复。
实际上,这可能是一种情况,其中从Angular内部使用jQuery实际上更容易,因为你正在使用的技术也使用jQuery。
但是,如果你真的想在Angular中使用这个框架使其工作,这里是如何做到的,只是要提前警告它将非常脆弱。
我会在你的组件中创建一个对象数组,表示菜单。每个对象可以保存其菜单文本、展开文本和表示其展开/折叠状态的布尔值。
你可以在组件外部定义一个接口,以便在需要时进行类型检查。
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
     }   
 }

看起来...当你在Angular中让它工作的时候,你可能已经可以构建自己的菜单项组件了,这样更容易处理并且更具可扩展性 :) 你正在创建一个包含多个属性的对象数组,并尝试通过每次点击来管理它们。这就是为什么组件被创建的原因!你可以为每个项目创建一个简单的包装器组件,但我不确定它会如何工作,因为materialize使用jQuery和CSS来选择东西。使用普通组件视图封装可能会不可预测。

与使用ngIf切换图标的替代方法是绑定innerText属性,并根据状态切换文本。

<i class="material-icons"  [innerText]="item.state ? 'plus-icon' : 'minus-icon'"></i>

但我不确定materialize框架是否会快速或完全地捕捉到innerText的更改。如果您尝试混合这些技术,最好同时呈现并在它们之间切换。

此外,我不知道“plus-icon”或“minus-icon”是否是您引用这些图标的方式,但您可能明白我的意思:p


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接