能否在JavaScript文件中使用tal:conditions?上下文菜单开发疑问
问题:动态上下文菜单的权限控制与tal逻辑复用
我正在开发一款高可访问性的新上下文菜单,旧菜单在Index.html中通过固定ID结合tal:conditions实现权限和显示条件判断。现在新菜单需要为每个实例生成唯一ID,不再依赖固定的action与ID,但遇到了以下问题:
- 尝试在tal条件内的
<script>标签声明var emp_menu_items = [];,并从Employee类中移除该变量,仍收到"变量未定义"的错误 - 想在
Index.html中构建新菜单,但需要用到Employee.js内的参数,因此想知道有没有办法在JavaScript文件中复用tal:conditions的判断逻辑
相关代码片段
Index.html(旧菜单实现)
<a metal:fill-slot="context_menus" tal:omit-tag=""> <ul id="employee_context_menu" class="hidden" tal:define="sec_posted python:container.util.checkSecurity(mn_name='Posted')"> <li tal:condition="python:layout == 'np'"><a href="#assign_pat">Assign Patients</a></li> <li tal:condition="python:container.util.checkSecurity(mn_name='File')"><a href="#hr_file">HR File</a></li> <li tal:condition="python:sec_posted"><a href="#schedule">Pay Period Schedule</a></li> <li><a href="#Close">Close Menu</a></li> </ul> </a>
Employee.js(旧菜单逻辑)
$name.contextMenu({ menu: "employee_context_menu", leftClick: true }, function(action, elem, position) { if(action == 'Close') { jQuery(".contextMenu").hide(); return true; } else if(action === 'hr_file') { $iframe.attr('src', 'Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id).load(function() {resizeIframe(this)}); } else if(action === 'assign_pat') { $j.PatientAssignment.assign_check = emp.id; $name.addClass('highlight'); var title_str ='Assign Patients to ' + employee_name; $j.PatientAssignment.sub_title = '' var mheight = $j(window).height()*0.7; $j('#beds').dialog('close'); $j('#beds').css('max-height',mheight).dialog({ modal: false, title: title_str, height:'auto', width: 'auto', close: function() { $name.removeClass('highlight'); } }); syncPatientAssignments(); syncBedAssignments(); return true; } else if(action === 'schedule') { $iframe.attr('src', 'ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&szID='+emp.id+'&args=-t').load(function() {resizeIframe(this)}); } else if(action === 'assign_work') { $iframe.attr('src', 'ScheduleStaff/cgi_daystaff?args=-t OneEmployee -I'+emp.id+' -B'+emp.shiftdate).load(function() {resizeIframe(this)}); } else { } $name.addClass('highlight'); $j("#modal_window").dialog({ modal: true, width: 'auto', show: 'scale', hide: 'scale', height: 'auto', close: function() {window.location.reload(); $name.removeClass('highlight'); } }); });
Employee.js(新菜单修改版本)
employeeContextMenu($div, emp, employee_name, count) { var emp_menu_items = []; var emp_menu = {}; emp_menu_items.push({ name: 'Assign Patients', title: 'Assign Patients', fn: function(el) { $j.PatientAssignment.assign_check = emp.id; var title_str ='Assign Patients to ' + employee_name; $j.PatientAssignment.sub_title = '' var mheight = $j(window).height()*0.7; if ($j('#beds').hasClass("ui-dialog-content") && $j( "#beds" ).dialog( "isOpen" )){ $j('#beds').dialog('close'); } $j('#beds').css('max-height',mheight).dialog({ modal: false, title: title_str, height:'auto', width: 'auto', position: [670, 115], focus: function(event,ui) { $j(".ui-icon-closethick").focus(); }, buttons: { OK: function(){ $j(this).dialog("close"); } } }); syncPatientAssignments(); syncBedAssignments(); } }); emp_menu_items.push({ name: 'HR File', title: 'HR File', fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);}}); emp_menu_items.push({ name: 'Pay Period Schedule', title: 'Pay Period Schedule', fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&szID='+emp.id+'&args=-t');}}); emp_menu_items.push({ name: 'Close Menu', title: '', fn: function(el){return false}, }); emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]}); }
解决方案
首先明确:tal:condition是服务器端模板渲染阶段执行的逻辑,JavaScript是客户端运行的代码,无法直接在JS文件中使用tal语法。之前的错误是因为当tal条件不满足时,对应的<script>标签不会被渲染,导致客户端某些场景下emp_menu_items未定义。
下面是三种可行的实现方案:
方案1:将权限判断结果注入DOM,在JS中读取
把tal的权限判断结果作为data-*属性绑定到员工元素上,在JS中读取这些属性来动态生成菜单项:
- 修改Index.html的员工元素:
<div class="employee-item" data-layout-np="${layout == 'np'}" data-sec-file="${container.util.checkSecurity(mn_name='File')}" data-sec-posted="${container.util.checkSecurity(mn_name='Posted')}" <!-- 其他原有属性 --> > <!-- 员工元素内容 --> </div>
- 修改Employee.js的
employeeContextMenu函数:
employeeContextMenu($div, emp, employee_name, count) { var emp_menu_items = []; // 从DOM读取权限标识(注意tal输出的布尔值可能是字符串形式的"True"/"False") const canAssignPat = $div.data('layout-np') === 'True'; const canViewHrFile = $div.data('sec-file') === 'True'; const canViewSchedule = $div.data('sec-posted') === 'True'; // 根据权限动态添加菜单项 if (canAssignPat) { emp_menu_items.push({ name: 'Assign Patients', title: 'Assign Patients', fn: function(el) { // 原有Assign Patients逻辑... } }); } if (canViewHrFile) { emp_menu_items.push({ name: 'HR File', title: 'HR File', fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);} }); } if (canViewSchedule) { emp_menu_items.push({ name: 'Pay Period Schedule', title: 'Pay Period Schedule', fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&szID='+emp.id+'&args=-t');} }); } // 固定添加关闭菜单 emp_menu_items.push({ name: 'Close Menu', title: '', fn: function(el){return false}, }); emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]}); }
方案2:后端生成全局权限配置,注入客户端
在Index.html的tal模板中生成全局权限对象,供JS直接使用:
- 在Index.html中添加全局配置:
<script tal:omit-tag=""> window.employeePermissions = { canAssignPat: ${layout == 'np'}, canViewHrFile: ${container.util.checkSecurity(mn_name='File')}, canViewSchedule: ${container.util.checkSecurity(mn_name='Posted')} }; </script>
- 在Employee.js中直接使用全局变量判断:
employeeContextMenu($div, emp, employee_name, count) { var emp_menu_items = []; if (window.employeePermissions.canAssignPat) { emp_menu_items.push({/* Assign Patients菜单项 */}); } // 其余权限判断逻辑同上... }
注意:如果页面存在多员工实例,需要确保全局配置是针对当前上下文的,或者改成每个元素单独的data属性方案。
方案3:tal模板生成菜单项配置,JS绑定逻辑
用tal生成菜单项的JSON结构,作为data属性存储,JS解析后绑定对应的函数:
- 修改Index.html的员工元素:
<div class="employee-item" data-menu-config='<tal:define="sec_posted python:container.util.checkSecurity(mn_name='Posted')"> [ <tal:if condition="python:layout == 'np'">{"name":"Assign Patients","title":"Assign Patients","action":"assign_pat"},</tal:if> <tal:if condition="python:container.util.checkSecurity(mn_name='File')">{"name":"HR File","title":"HR File","action":"hr_file"},</tal:if> <tal:if condition="python:sec_posted">{"name":"Pay Period Schedule","title":"Pay Period Schedule","action":"schedule"},</tal:if> {"name":"Close Menu","title":"","action":"close"} ] </tal:define>'> </div>
- 修改Employee.js的
employeeContextMenu函数:
employeeContextMenu($div, emp, employee_name, count) { // 解析tal生成的菜单配置 const menuConfig = JSON.parse($div.data('menu-config')); var emp_menu_items = menuConfig.map(item => { // 根据action绑定对应的处理函数 switch(item.action) { case 'assign_pat': return { ...item, fn: function(el) { // 原有Assign Patients逻辑... } }; case 'hr_file': return { ...item, fn: function(el){return pop_over_window('Personnel/cgi_utran/cgi_utran?args=-i'+$div.data('obj').id);} }; case 'schedule': return { ...item, fn: function(el){return pop_over_window('ScheduleStaff/cgi_daystaff?szDate='+emp.shiftdate+'&szID='+emp.id+'&args=-t');} }; case 'close': return { ...item, fn: function(el){return false} }; default: return item; } }); emp_menu = new ContextMenu('employee_context_menu_'+count, 'Employee Context Menu Options', '.name', emp_menu_items, {container:$div[0]}); }
内容的提问来源于stack exchange,提问作者JThao
相关产品推荐
相关产品推荐

