Ember登录后侧边栏下拉JavaScript功能失效问题求助
解决Ember登录后侧边栏下拉菜单失效的问题
这个问题我之前在Ember项目里也碰到过,核心原因是:登录前侧边栏/头部根本没渲染到DOM里,你用来实现下拉菜单的交互逻辑(不管是原生JS、jQuery还是第三方插件),要么只在页面初始化时绑定了一次,要么没有在DOM更新后重新触发初始化,导致登录后新渲染出来的侧边栏元素没有绑定上交互事件。下面给你几个可行的解决方案,按Ember最佳实践的推荐程度排序:
方案一:用Ember组件封装侧边栏交互(最推荐)
Ember是数据驱动的框架,用组件来管理自身的交互是最贴合框架设计的方式,能从根源避免事件绑定失效的问题。
步骤1:创建侧边栏菜单组件
在app/components/sidebar-menu.hbs写组件模板:
<div class="sidebar"> <div class="dropdown"> <button class="dropdown-toggle" {{on "click" this.toggleDropdown}}> 主菜单 </button> {{#if this.isDropdownOpen}} <div class="dropdown-menu"> <a href="/dashboard" {{on "click" this.navigateTo}}>仪表盘</a> <a href="/profile" {{on "click" this.navigateTo}}>个人设置</a> </div> {{/if}} </div> </div>
在app/components/sidebar-menu.js写组件逻辑:
import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; export default class SidebarMenuComponent extends Component { @service router; @tracked isDropdownOpen = false; @action toggleDropdown() { this.isDropdownOpen = !this.isDropdownOpen; } @action navigateTo(event) { event.preventDefault(); this.router.transitionTo(event.target.getAttribute('href')); } }
步骤2:在application.hbs中使用组件
{{#if this.session.isAuthenticated}} <HeaderComponent /> <SidebarMenu /> {{/if}} <Outlet />
这种方式下,组件会在登录后自动渲染,内部的点击事件会随组件初始化自动绑定,完全不会出现失效问题。
方案二:用事件委托处理原生JS/jQuery交互
如果你因为历史原因必须用原生JS或jQuery实现下拉菜单,事件委托是解决动态DOM元素事件绑定的关键——把事件绑定到不会被销毁的父元素(比如body),让父元素捕获子元素的事件。
在app/controllers/application.js中添加逻辑:
import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { on } from '@ember/object/evented'; export default class ApplicationController extends Controller { @service session; @service router; @on('init') setupDropdownDelegate() { document.body.addEventListener('click', (event) => { // 处理下拉菜单展开/收起 if (event.target.matches('.dropdown-toggle')) { event.target.nextElementSibling.classList.toggle('show'); } // 处理菜单跳转 if (event.target.matches('.dropdown-menu a')) { event.preventDefault(); this.router.transitionTo(event.target.getAttribute('href')); } }); } }
方案三:监听认证状态,在DOM渲染后重新初始化
如果你的侧边栏用了第三方UI插件(比如Bootstrap的dropdown),可以在登录状态变化后,等待DOM渲染完成再触发插件初始化:
在app/controllers/application.js中添加监听:
import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { observer } from '@ember/object'; import { scheduleOnce } from '@ember/runloop'; export default class ApplicationController extends Controller { @service session; @observer('session.isAuthenticated') onAuthStatusChanged() { if (this.session.isAuthenticated) { // 等待Ember完成DOM渲染后再执行初始化 scheduleOnce('afterRender', this, () => { // 调用第三方插件的初始化方法,比如Bootstrap的dropdown // $('.dropdown').dropdown(); }); } } }
内容的提问来源于stack exchange,提问作者spacerobot
相关产品推荐
相关产品推荐

