Ember 上的点击事件

Click events on Ember

我发现了 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 本身不处理点击事件?我知道 link 应该将用户重定向到一个新页面(这仍然有争议),但是 DOM 事件仍然被触发,所以 Ember 故意忽略它并选择不让开发商来处理?我想知道这背后的逻辑。
  3. 是否有比我的解决方案更好的方法?

谢谢。

为学习 EmberJS 和发布漂亮、明确的问题干杯!

你的错误

  1. 切勿修改 node_modules/bower_components/ 文件夹中的代码。如果你真的需要猴子修补某些东西,你可以在初始化程序中完成。但是您的用例不需要猴子补丁。

  2. 您试图在菜单项组件中定义一个操作,但您在父模板中应用了它。该操作必须在该父项的模板中定义 component/controller.

  3. 此调用不正确:

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

    问题如下:

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

    2. 您只是将一堆位置参数传递给组件。如果 ddm.link-to 确实支持接受一个动作,正确的调用应该是这样的:

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

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

    3. 不带引号的
    4. 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'}}>
    

    要在浏览器中正确设置 link 样式,需要 href 属性。但是您不必向它传递值 '#'。老式应用程序需要哈希符号以防止 link 覆盖 URL。 Ember 覆盖 URL 为你覆盖,所以你可以简单地传递一个空的 href.

    最后的正确用法是:

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

问题的答案

  1. Why is it that defining actions on both the Component file and the hbs one didn't change the result?

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

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

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

  1. Why doesn't the LinkComponent handle click events natively? I get that a link is supposed to redirect users to a new page (which is still arguable), but the DOM event is still fired, so does Ember deliberately ignore it and choose not to let developers handle it? I want to know the logic behind this.

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

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

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

  1. Is there a better approach than my solution?

请参阅下文了解使用 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