【问题标题】:How to add Css Class to item inside Ember's {{#each}} loop, according to index? - Ember.js如何根据索引将 Css 类添加到 Ember 的 {{#each}} 循环中的项目? - Ember.js
【发布时间】:2015-12-30 15:36:47
【问题描述】:

我有一个带有侧边菜单的 Ember 应用程序。 html 中的菜单项基于 Ember 的 {{each}} 循环。 当用户选择一个菜单项时,我想添加这个项目活动类。

如何将 css 类添加到 Ember 的 {{each}} 中的单个项目 根据控制器的情况。

Html/Hbs:

<ul>
   {{#each menuItem in sideMenuItems}}
      <li {{bind-attr class="isActive: active-class"}} {{ action 'selectMenuItem' _view.contentIndex }}>
         {{ menuItem.text }}
      </li>
   {{/each}}
</ul>

控制器:

Ember.ObjectController.extend({
  selectedMenuIndex: 0,
  isActive: function() {
    return (this.get('selectedMenuIndex') == ? ? ? );
  }.property('???'),

  actions: {
    selectMenuItem: function(itemIndex) {
      this.set('selectedMenuIndex', itemIndex);
    }
  }
})

【问题讨论】:

  • 您使用的是哪个版本的 ember?

标签: javascript ember.js


【解决方案1】:

当用户选择一个菜单项时,我想添加这个项目活动类。

由于您使用的是 Ember 1.5,我将首先向您展示一些在旧版本中实现此目的的方法。

最简单的方法是向项目本身添加一个标志isActive,并将其用作css类的条件。您可以在selectMenuItem 操作中为单击的项目将标志设置为true。如果您的逻辑一次只允许一个项目处于活动状态,您还必须将任何当前活动项目的isActive 标志重置为false

{{#each menuItem in sideMenuItems}}
  <li {{bind-attr class="menuItem.isActive:active-class"}} {{action 'selectMenuItem' _view.contentIndex}}>
     {{menuItem.text}}
  </li>
{{/each}}

控制器:

Ember.ObjectController.extend({

  sideMenuItems: // ... your array of items, each containing an isActive flag

  actions: {
    selectMenuItem: function(itemIndex) {
      var items = this.get('sideMenuItems');
      items.forEach(function(item, idx) {         // go through the items,
          if(idx === itemIndex) {                 // find the clicked item and set it as active
              Ember.set(item, 'isActive', true);
          } else {                                // reset all other items
              Ember.set(item, 'isActive', false);
          }
      });
    }
  }
})   

一种更有效的方法是将当前活动的项目存储为一个单独的变量,这将消除对 sideMenuItems 数组进行两次迭代的需要:

控制器:

Ember.ObjectController.extend({

  sideMenuItems: // ... your array of items, each containing an isActive flag
  activeMenuItem: null,

  actions: {
    selectMenuItem: function(itemIndex) {
      var items = this.get('sideMenuItems');
      var currActive = this.get('activeMenuItem');
      var nextActive = items.objectAt(itemIndex);

      // if there is a currently active item, deactivate it
      if(currActive) {
        Ember.set(currActive, 'isActive', false);
      }

      // if the user clicked on the currently active item, we just reset the selected item
      // otherwise we update the isActive flag for the clicked item
      if(currActive === nextActive) {
          nextActive = null;
      } else {
         Ember.set(nextActive, 'isActive', true);
      }

      // set the clicked item as the currently active menu item
      this.set('activeMenuItem', nextActive);
  }
})  

另一种方法是创建一个自定义组件menu-item,它根据活动索引呈现您的li 项,有无类。

Ember.Component.extend({

    // passed in
    activeIndex: -1,
    index: -1,
    selectMenuItemAction: '',

    // local
    tagName: 'li',
    classNameBindings: ['activeClass'],

    activeClass: function() {
        var idx = this.get('index');
        var activeIdx = this.get('activeIndex');

        // if the current item is the active index, return the active class
        if(idx === activeIdx) {
            return 'active-class';
        }

        // otherwise return no class
        return '';
    }.property('activeIndex')

    actions: {
        selectMenuItem: function() {
            this.sendAction('selectMenuItemAction', this.get('index'));
        }
    }
})

然后像这样使用它:

{{#each menuItem in sideMenuItems}}
    {{#menu-item index=_view.contentIndex activeIndex=selectedMenuIndex selectMenuItemAction="selectMenuItem"}}
        {{menuItem.text}}
    {{/menu-item}}
{{/each}}

控制器:

Ember.ObjectController.extend({
  selectedMenuIndex: 0,

  actions: {
    selectMenuItem: function(itemIndex) {
      this.set('selectedMenuIndex', itemIndex);
    }
  }
})

由于使用 _view.contentIndex 有点像 hack,您可以只传递实际的菜单项,然后比较对象引用而不是索引来确定活动项:

菜单项组件:

Ember.Component.extend({

    // passed in
    activeItem: null,
    item: null,
    selectMenuItemAction: '',

    // local
    tagName: 'li',
    classNameBindings: ['activeClass'],

    activeClass: function() {
        var item = this.get('item');
        var activeItem = this.get('activeItem');

        // if the current item is the active item, return the active class
        if(item === activeItem) {
            return 'active-class';
        }

        // otherwise return no class
        return '';
    }.property('activeItem')

    actions: {
        selectMenuItem: function() {
            this.sendAction('selectMenuItemAction', this.get('item'));
        }
    }
})

然后像这样使用它:

{{#each menuItem in sideMenuItems}}
    {{#menu-item item=menuItem activeItem=selectedMenuItem selectMenuItemAction="selectMenuItem"}}
        {{menuItem.text}}
    {{/menu-item}}
{{/each}}

控制器:

Ember.ObjectController.extend({
  selectedMenuItem: null,

  actions: {
    selectMenuItem: function(item) {
      this.set('selectedMenuItem', item);
    }
  }
})

这是在 Ember > 1.13 中执行此操作的一种更简单的方法

Ember > 1.13 支持内联助手,因此您可以按照自己的意愿实现这一点。 Ember CLI 1.13 还附带了babel,因此我们也可以使用 ES6,从而进一步简化:

控制器:

Ember.Controller.extend({
  selectedMenuIndex: 0,
  menuItems: // ...
  actions: {
    selectMenuItem(itemIndex) {
      this.set('selectedMenuIndex', itemIndex);
    }
  }
});

模板:

{{#each menuItems as |item index|}}
  <li class="{{active-class index selectedMenuIndex}}" {{action "selectMenuItem" index}}>
  {{item.text}}
  </li>
{{/each}}

助手:

activeClass(params) {
  const [ index, active ] = params; // use ES6 destructuring
  return (index === active) ? 'active-class': '';
}

Here is a working twiddle

【讨论】:

  • 感谢您的回答。确实,你的方法会奏效。但是 Ember.js 有没有更好的方法来处理这种情况?类似于 Angular.js ng-class="{isActive: $index==selectedIndex}"。我知道 Ember 不支持 html 内的内联条件,这就是 Ember 在控制器内使用 property() 函数的原因。那么我可以用这个条件写一个 property() 函数吗?
  • 查看我的编辑。您可以制作一个组件来执行此操作。
  • 如果您可以更新到最新的 Ember,它包含许多改进,它将使您的生活更轻松。我在最新的 Ember 中附加了一种实现此目的的方法。
  • 就您关于使用property() 的问题而言,这与我的前几个示例几乎相同。使用第一个示例,如果您将items.forEach(... 代码(在控制器内部)从selectMenuItem 操作移动到一个单独的计算属性,该属性在selectedMenuIndex 更改时重新计算(即.property('selectedMenuIndex'),您将实现您的要求,尽管行为与我的示例并没有真正的不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 2013-02-15
  • 2014-10-29
  • 1970-01-01
  • 2012-09-27
  • 2017-08-25
  • 2020-03-12
相关资源
最近更新 更多