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}}
下面是我的问题:
- 为什么在 Component 文件和 hbs 上定义操作没有改变结果?
- 为什么 LinkComponent 本身不处理点击事件?我知道 link 应该将用户重定向到一个新页面(这仍然有争议),但是 DOM 事件仍然被触发,所以 Ember 故意忽略它并选择不让开发商来处理?我想知道这背后的逻辑。
- 是否有比我的解决方案更好的方法?
谢谢。
为学习 EmberJS 和发布漂亮、明确的问题干杯!
你的错误
切勿修改 node_modules/
和 bower_components/
文件夹中的代码。如果你真的需要猴子修补某些东西,你可以在初始化程序中完成。但是您的用例不需要猴子补丁。
您试图在菜单项组件中定义一个操作,但您在父模板中应用了它。该操作必须在该父项的模板中定义 component/controller.
此调用不正确:
{{#ddm.link-to "index" action "sortByPrice" low_to_high}}
问题如下:
ddm.link-to
组件应该创建到另一条路由的 link。它似乎不支持将动作传递给它。
您只是将一堆位置参数传递给组件。如果 ddm.link-to
确实支持接受一个动作,正确的调用应该是这样的:
{{#ddm.link-to "index" argName=(action "sortByPrice" low_to_high)}}
在这种情况下,"index"
是位置参数,argName
是命名参数。
不带引号的 low_to_high
是对在当前作用域上定义的 属性 的引用。您可能指的是一个字符串:"low_to_high"
。
切勿直接在模板中使用 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'}}>
问题的答案
- 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
中应用该操作。
- 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 查询参数,在这种情况下,您可以通过转换到不同的查询参数来更改排序顺序。
- 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。
我发现了 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}}
下面是我的问题:
- 为什么在 Component 文件和 hbs 上定义操作没有改变结果?
- 为什么 LinkComponent 本身不处理点击事件?我知道 link 应该将用户重定向到一个新页面(这仍然有争议),但是 DOM 事件仍然被触发,所以 Ember 故意忽略它并选择不让开发商来处理?我想知道这背后的逻辑。
- 是否有比我的解决方案更好的方法?
谢谢。
为学习 EmberJS 和发布漂亮、明确的问题干杯!
你的错误
切勿修改
node_modules/
和bower_components/
文件夹中的代码。如果你真的需要猴子修补某些东西,你可以在初始化程序中完成。但是您的用例不需要猴子补丁。您试图在菜单项组件中定义一个操作,但您在父模板中应用了它。该操作必须在该父项的模板中定义 component/controller.
此调用不正确:
{{#ddm.link-to "index" action "sortByPrice" low_to_high}}
问题如下:
ddm.link-to
组件应该创建到另一条路由的 link。它似乎不支持将动作传递给它。您只是将一堆位置参数传递给组件。如果
ddm.link-to
确实支持接受一个动作,正确的调用应该是这样的:{{#ddm.link-to "index" argName=(action "sortByPrice" low_to_high)}}
在这种情况下,
"index"
是位置参数,argName
是命名参数。
不带引号的 low_to_high
是对在当前作用域上定义的 属性 的引用。您可能指的是一个字符串:"low_to_high"
。
切勿直接在模板中使用 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'}}>
问题的答案
- 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
中应用该操作。
- 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 查询参数,在这种情况下,您可以通过转换到不同的查询参数来更改排序顺序。
- 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。