灰烬上的点击事件

时间:2018-09-21 02:13:27

标签: javascript ember.js ember-bootstrap

我正在发现EmberJS,并开始将现有网站迁移到此框架。我在基于Bootstrap的下拉菜单中遇到问题。这个问题实际上帮助我更好地了解了Ember的概念,但是我仍然有一些疑问。

我使用了ember-bootstrap模块来生成此下拉列表(除其他外),这就是代码的本意:

{{#bs-dropdown as |dd|}}
  {{#dd.button}}
    Sort by
  {{/dd.button}}

  {{#dd.menu as |ddm|}}
    {{#ddm.item}}{{#ddm.link-to "index"}}Price low to high{{/ddm.link-to}}{{/ddm.item}}
    {{#ddm.item}}{{#ddm.link-to "index"}}Price high to low{{/ddm.link-to}}{{/ddm.item}}
  {{/dd.menu}}
{{/bs-dropdown}}

现在,我希望当用户单击其中一项时执行一些javascript代码。检查the module's documentation后,我发现在哪里定义了菜单项组件,并按如下方式编辑其代码:

export default Component.extend({
  layout,
  classNameBindings: ['containerClass'],

  /* ... */

  actions: {
    // My addition
    sortByPrice(param){
      alert("sorting");
    },
    // End of the addition

    toggleDropdown() {
      if (this.get('isOpen')) {
        this.send('closeDropdown');
      } else {
        this.send('openDropdown');
      }
    },
  },
});

然后我按如下所示更新了hbs文件:

{{#dd.menu as |ddm|}}
   {{#ddm.item action "sortByPrice" low_to_high}}

    {{#ddm.link-to "index"  action "sortByPrice" low_to_high}}
      Prix croissant
    {{/ddm.link-to}}

  {{/ddm.item}}
{{/dd.menu}}

这没有用,这就是为什么我也向*action*元素中添加了link-to并对其组件文件进行了类似声明的原因。

import LinkComponent from '@ember/routing/link-component';

export default LinkComponent.extend({
  actions: {
    sortByPrice(param){
        alert("sorting");
      console.log("sorting");
      },
  },
});

如您所见,*link-to*组件扩展了 LinkComponent 一个。我最终明白,如this thread所述,此元素不可能原生处理点击事件。

出于无奈,我最终采用了一种不太优雅的方法,但仍然可以解决问题:

{{#bs-dropdown id="sort" as |dd|}}
  {{#dd.button}}
    Sort by
  {{/dd.button}}

  {{#dd.menu as |ddm|}}
    {{#ddm.item action "sortByPrice" low_to_high}}
      <a
        class="dropdown-item"
        onclick="sortByPrice('low_to_high'); return false;"
        href="#"
      >
        Price low to high
      </a>
    {{/ddm.item}}
  {{/dd.menu}}
{{/bs-dropdown}}

现在这是我的问题:

  1. 为什么在Component文件和hbs上定义动作都不会改变结果?
  2. LinkComponent为什么不本地处理单击事件?我知道链接应该将用户重定向到新页面(仍然可以争论),但是DOM事件仍然被触发,所以Ember是否故意忽略了它并选择不让开发人员处理它?我想知道这背后的逻辑。
  3. 有比我的解决方案更好的方法吗?

谢谢。

1 个答案:

答案 0 :(得分:5)

为学习EmberJS并发布一个美丽而明确的问题而欢呼!

您的错误

  1. 请勿修改node_modules/bower_components/文件夹中的代码。如果您确实需要猴子修补程序,则可以在初始化程序中进行。但是您的用例不需要猴子打补丁。

  2. 您试图在菜单项组件中定义一个动作,但是您将其应用到了父模板中。该动作必须在该父对象的模板组件/控制器中定义。

  3. 此调用不正确:

    {{#ddm.link-to "index"  action "sortByPrice" low_to_high}}
    

    这里是问题:

    1. ddm.link-to组件应该创建到另一条路线的链接。它似乎不支持将动作传递给它。

    2. 您只是将一堆位置参数传递给组件。如果ddm.link-to支持接受操作,则正确的调用应如下所示:

      {{#ddm.link-to "index" argName=(action "sortByPrice" low_to_high)}}
      

      在这种情况下,"index"是位置参数,argName是命名参数。

    3. low_to_high(不带引号)是对在当前作用域上定义的属性的引用。您可能是用字符串代替:"low_to_high"

  4. 请勿直接在模板中使用JS代码。您永远不会在Ember中这样做:

    <a onclick="sortByPrice('low_to_high'); return false;">
    

    相反,传递一个动作(在本地范围内定义:在组件或控制器中):

    <a onclick={{action 'sortByPrice' 'low_to_high'}}>
    

    onclick属性名称是可选的。没有属性定义的操作暗含onclick(仅在需要将操作附加到其他事件时才需要提供属性名称):

    <a {{action 'sortByPrice' 'low_to_high'}}>
    

    要在浏览器中正确设置链接的样式,必须使用href属性。但是您不必将值'#'传递给它。老式应用程序中需要使用井号,以防止链接覆盖URL。 Ember会为您覆盖URL覆盖,因此您只需传递一个空的href

    这是最终的正确用法:

    <a href {{action 'sortByPrice' 'low_to_high'}}>
    

回答您的问题

  
      
  1. 为什么在Component文件和hbs上定义动作都不会改变结果?
  2.   

因为您在不同的范围内定义了它们。

如果您在app/components/foo-bar.js中定义动作,则必须在app/templates/components/foo-bar.hbs中应用该动作。

如果您在app/controllers/index.js中定义动作,则必须在app/templates/index.hbs中应用该动作。

  
      
  1. 为什么LinkComponent不本地处理点击事件?我知道链接应该将用户重定向到新页面(仍然可以争论),但是DOM事件仍然被触发,所以Ember是否故意忽略了它并选择不让开发人员处理它?我想知道这背后的逻辑。
  2.   

在PWA中,您不执行实际的页面重定向。这样的重定向将重新加载整个应用程序。

相反,LinkComponent会覆盖该点击,并告诉Ember的路由系统执行过渡。必须正确设置路由,并且传递到LinkComponent的路由必须存在。

您的目标似乎不是执行转换而是更改变量,因此LinkComponent在这里不适用。除非您将排序顺序属性连接到URL查询参数,否则在这种情况下,可以通过转换到其他查询参数来更改排序顺序。

  
      
  1. 有比我的解决方案更好的方法吗?
  2.   

有关使用ember-bootstrap下拉菜单的最简单方法,请参见下文。


一个有效的例子

控制器:

export default Ember.Controller.extend({
  isSortAccending: true,

  actions: {
    changeSortDirection (isSortAccending) {
      this.set('isSortAccending', isSortAccending);
    }
  }
});

模板:

<p>
  Current sort order:
  {{if isSortAccending "ascending" "descending"}}
</p>

{{#bs-dropdown as |dd|}}
  {{#dd.button}}
    Sort by
  {{/dd.button}}

  {{#dd.menu as |ddm|}}
    {{#ddm.item}}
      <a href {{action "changeSortDirection" true}}>
        Price high to low
      </a>
    {{/ddm.item}}

    {{#ddm.item}}
      <a href {{action "changeSortDirection" false}}>
        Price high to low
      </a>
    {{/ddm.item}}
  {{/dd.menu}}
{{/bs-dropdown}}

这里是working demo