我正在发现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}}
现在这是我的问题:
谢谢。
答案 0 :(得分:5)
为学习EmberJS并发布一个美丽而明确的问题而欢呼!
请勿修改node_modules/
和bower_components/
文件夹中的代码。如果您确实需要猴子修补程序,则可以在初始化程序中进行。但是您的用例不需要猴子打补丁。
您试图在菜单项组件中定义一个动作,但是您将其应用到了父模板中。该动作必须在该父对象的模板组件/控制器中定义。
此调用不正确:
{{#ddm.link-to "index" action "sortByPrice" low_to_high}}
这里是问题:
ddm.link-to
组件应该创建到另一条路线的链接。它似乎不支持将动作传递给它。
您只是将一堆位置参数传递给组件。如果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'}}>
要在浏览器中正确设置链接的样式,必须使用href
属性。但是您不必将值'#'
传递给它。老式应用程序中需要使用井号,以防止链接覆盖URL。 Ember会为您覆盖URL覆盖,因此您只需传递一个空的href
。
这是最终的正确用法:
<a href {{action 'sortByPrice' 'low_to_high'}}>
- 为什么在Component文件和hbs上定义动作都不会改变结果?
因为您在不同的范围内定义了它们。
如果您在app/components/foo-bar.js
中定义动作,则必须在app/templates/components/foo-bar.hbs
中应用该动作。
如果您在app/controllers/index.js
中定义动作,则必须在app/templates/index.hbs
中应用该动作。
- 为什么
LinkComponent
不本地处理点击事件?我知道链接应该将用户重定向到新页面(仍然可以争论),但是DOM事件仍然被触发,所以Ember是否故意忽略了它并选择不让开发人员处理它?我想知道这背后的逻辑。
在PWA中,您不执行实际的页面重定向。这样的重定向将重新加载整个应用程序。
相反,LinkComponent
会覆盖该点击,并告诉Ember的路由系统执行过渡。必须正确设置路由,并且传递到LinkComponent
的路由必须存在。
您的目标似乎不是执行转换而是更改变量,因此LinkComponent
在这里不适用。除非您将排序顺序属性连接到URL查询参数,否则在这种情况下,可以通过转换到其他查询参数来更改排序顺序。
- 有比我的解决方案更好的方法吗?
有关使用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。