天道酬勤,学无止境

f:setPropertyActionListener always setting null

I'm trying to put the currently iterated <p:dataTable var> as a property of a managed bean using <f:setPropertyActionListener>. However, it is always set as null.

The view, dentistas.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml"  
    xmlns:h="http://java.sun.com/jsf/html"  
    xmlns:f="http://java.sun.com/jsf/core"  
    xmlns:ui="http://java.sun.com/jsf/facelets"  
    xmlns:p="http://primefaces.org/ui">  

<h:head>  

</h:head>  
<h:body>  
    <ui:composition template="/templates/template.xhtml">  

        <ui:define name="content">  
            <h:form id="formDentistas">  

                <p:growl autoUpdate="true" />  

                <p:commandButton icon="ui-icon-plus" value="Cadastrar"  
                    id="cadastrar" oncomplete="dialogCadastrar.show()" />  


                <p:dataTable var="dentista" value="#{dentistaMB.dentistas}"  
                    paginator="true" emptyMessage="Não foi encontrado nenhum registro"  
                    rows="10" id="dataTableDentistas">  

                    <f:facet name="header">Lista de Dentistas</f:facet>  
                    <p:column headerText="Nome" sortBy="nome" filterBy="nome" id="nome"  
                        width="200px">  
                    #{dentista.pessoaFisica.nome}  
                </p:column>  

                    <p:column headerText="Data Nascimento" sortBy="dataNascimento"  
                        filterBy="dataNascimento" id="dataNascimento" width="60px">  
                    #{dentista.pessoaFisica.dataNascimento}  
                </p:column>  

                    <p:column headerText="CRO" sortBy="cro" filterBy="cro" id="cro"  
                        width="60px">  
                    #{dentista.cro}  
                </p:column>  

                    <p:column headerText="Ações" style="width:50px;">  
                        <p:commandButton value="Alterar" icon="ui-icon-pencil">  
                           <f:setPropertyActionListener target="#{dentistaMB.selectedDentista}" value="#{dentista}" />                                  
                        </p:commandButton>  

                        <p:commandButton value="Remover" icon="ui-icon-trash"  
                            actionListener="#{dentistaMB.deletar}">  
                            <f:setPropertyActionListener target="#{dentistaMB.selectedDentista}" value="#{dentista}" />  
                        </p:commandButton>  
                    </p:column>  

                </p:dataTable>  
            </h:form>  

            <ui:include src="/tabelas/dialog_insert_dentista.xhtml" />  


        </ui:define>  
    </ui:composition>  


</h:body>  
</html>  

The managed bean, DentistaMBImpl:

package br.com.odontonew.mb;  

import java.util.List;  
import java.util.Map;  

import javax.annotation.PostConstruct;  
import javax.faces.bean.ManagedBean;  
import javax.faces.bean.ManagedProperty;  
import javax.faces.bean.RequestScoped;  
import javax.faces.bean.SessionScoped;  
import javax.faces.bean.ViewScoped;  
import javax.faces.context.ExternalContext;  
import javax.faces.context.FacesContext;  
import javax.faces.convert.FacesConverter;  
import javax.faces.event.ActionEvent;  

import org.apache.log4j.Logger;  
import org.primefaces.context.RequestContext;  

import br.com.odontonew.bean.Dentista;  
import br.com.odontonew.bean.EstadoCivil;  
import br.com.odontonew.bean.PessoaFisica;  
import br.com.odontonew.bean.Sexo;  
import br.com.odontonew.bean.SituacaoPessoa;  
import br.com.odontonew.bean.Uf;  
import br.com.odontonew.bo.BasicBO;  
import br.com.odontonew.exception.BOException;  

@ManagedBean(name = "dentistaMB")  
@ViewScoped  
public class DentistaMBImpl extends BasicMBImpl {  

    private Logger logger = Logger.getLogger(DentistaMBImpl.class);  

    @ManagedProperty("#{dentistaBO}")  
    private BasicBO dentistaBO;  
    private Dentista dentista;  
    private Dentista selectedDentista;  
    private List<Dentista> dentistas;  

    // Lists  
    private List<EstadoCivil> estadosCivis;  
    private List<SituacaoPessoa> situacoesPessoa;  
    private List<Sexo> sexos;  
    private List<Uf> ufs;  

    @PostConstruct  
    public void init() {  
        dentista = new Dentista();  
        dentista.setPessoaFisica(new PessoaFisica());  
        dentista.getPessoaFisica().setSexo(new Sexo());  
        dentista.getPessoaFisica().setEstadoCivil(new EstadoCivil());  
        dentista.getPessoaFisica().setSituacao(new SituacaoPessoa());  
        dentista.getPessoaFisica().setUf(new Uf());  
        estadosCivis = (List<EstadoCivil>) dentistaBO  
                .findByNamedQuery(EstadoCivil.FIND_ALL);  
        situacoesPessoa = (List<SituacaoPessoa>) dentistaBO  
                .findByNamedQuery(SituacaoPessoa.FIND_ALL);  
        sexos = (List<Sexo>) dentistaBO.findByNamedQuery(Sexo.FIND_ALL);  
        ufs = (List<Uf>) dentistaBO.findByNamedQuery(Uf.FIND_ALL);  
    }  

    public void salvar(ActionEvent event) {  
        try {  
            dentista = (Dentista) dentistaBO.save(dentista);  
            addInfoMessage("Dentista salvo com sucesso");  
            RequestContext.getCurrentInstance().execute(  
                    "dialogCadastrar.hide()");  

        } catch (BOException e) {  
            addErrorMessage(e.getMessage());  
        }  
    }  

    public void atualizar(ActionEvent event) {  
        try {  
            dentistaBO.update(selectedDentista);  
            addInfoMessage("Dentista atualizado com sucesso");  
        } catch (BOException e) {  
            addErrorMessage(e.getMessage());  
        }  
    }  

    public void deletar(ActionEvent event) {  
        try {             
            dentistaBO.delete(selectedDentista);  
            addInfoMessage("Dentista deletado com sucesso");  
        } catch (BOException e) {  
            addErrorMessage(e.getMessage());  
        }  
    }  

    public List<Dentista> getDentistas() {  
        try {  
            if (dentistas == null)  
                dentistas = (List<Dentista>) dentistaBO  
                        .findByNamedQuery(Dentista.FIND_ALL_COMPLETO);  

            return dentistas;  
        } catch (BOException e) {  
            addErrorMessage(e.getMessage());  
            return null;  
        }  
    }  

    /* gets and sets */  

    public Dentista getSelectedDentista() {  
        return selectedDentista;  
    }  

    public void setSelectedDentista(Dentista selectedDentista) {  
        this.selectedDentista = selectedDentista;  
    }  

    public Dentista getDentista() {  
        return dentista;  
    }  

    public void setDentista(Dentista dentista) {  
        this.dentista = dentista;  
    }  

    public Logger getLogger() {  
        return logger;  
    }  

    public void setLogger(Logger logger) {  
        this.logger = logger;  
    }  

    public List<EstadoCivil> getEstadosCivis() {  
        return estadosCivis;  
    }  

    public void setEstadosCivis(List<EstadoCivil> estadosCivis) {  
        this.estadosCivis = estadosCivis;  
    }  

    public List<SituacaoPessoa> getSituacoesPessoa() {  
        return situacoesPessoa;  
    }  

    public void setSituacoesPessoa(List<SituacaoPessoa> situacoesPessoa) {  
        this.situacoesPessoa = situacoesPessoa;  
    }  

    public List<Sexo> getSexos() {  
        return sexos;  
    }  

    public void setSexos(List<Sexo> sexos) {  
        this.sexos = sexos;  
    }  

    public void setDentistas(List<Dentista> dentistas) {  
        this.dentistas = dentistas;  
    }  

    public List<Uf> getUfs() {  
        return ufs;  
    }  

    public void setUfs(List<Uf> ufs) {  
        this.ufs = ufs;  
    }  

    public BasicBO getDentistaBO() {  
        return dentistaBO;  
    }  

    public void setDentistaBO(BasicBO dentistaBO) {  
        this.dentistaBO = dentistaBO;  
    }  



}
标签

评论

Here,

<p:commandButton value="Remover" icon="ui-icon-trash"  
    actionListener="#{dentistaMB.deletar}">  
    <f:setPropertyActionListener target="#{dentistaMB.selectedDentista}" value="#{dentista}" />  
</p:commandButton>  

you're performing the delete in an actionListener method instead of an action method. This is not right. Business actions should be performed in the action method. All action listeners, including the <f:setPropertyActionListener>, are invoked before action method in the very same order as they are declared and assigned on the command component. So, in effects, the delete is first invoked and then the property is set. That explains why the property is null during the delete.

The fix is simple: make it a real action method:

<p:commandButton value="Remover" icon="ui-icon-trash"  
    action="#{dentistaMB.deletar}">  
    <f:setPropertyActionListener target="#{dentistaMB.selectedDentista}" value="#{dentista}" />  
</p:commandButton>  

Don't forget to remove the ActionEvent argument:

public void deletar() {  
    // ...
}

See also:

  • Differences between action and actionListener

Unrelated to the concrete problem, if you happen to target a Servlet 3.0 / EL 2.2 compatible container, then you can even get rid of that <f:setPropertyActionListener> altogether:

<p:commandButton value="Remover" icon="ui-icon-trash"  
    action="#{dentistaMB.deletar(dentista)}" />

With:

public void deletar(Dentista selectedDentista) {  
    // ...
}

See also point 3 of How can I pass selected row to commandLink inside dataTable?

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 在p:columngroup中未调用p:commandButton操作和f:setpropertyactionlistener(p:commandButton action and f:setpropertyactionlistener not invoked in p:columngroup)
    问题 我需要将子组件放在primefaces子表页脚(p:columngroup type =“ footer”)中,但是标准子表渲染器不提供这种机会。 因此,我重写了org.primefaces.component.SubTableRenderer来添加子级渲染: public class CustomSubTableRenderer extends SubTableRenderer{ @Override protected void encodeColumnFooter(FacesContext context, SubTable table, Column column) throws IOException { ResponseWriter writer = context.getResponseWriter(); String style = column.getStyle(); String styleClass = column.getStyleClass(); styleClass = styleClass == null ? DataTable.COLUMN_FOOTER_CLASS : DataTable.COLUMN_FOOTER_CLASS + " " + styleClass; writer.startElement("td", null); writer
  • p:commandButton action and f:setpropertyactionlistener not invoked in p:columngroup
    I need to put child components in primefaces subtable footer (p:columngroup type="footer"), but standard subtable renderer doesn't provide such opportunity. So I have overrided org.primefaces.component.SubTableRenderer to add children rendering: public class CustomSubTableRenderer extends SubTableRenderer{ @Override protected void encodeColumnFooter(FacesContext context, SubTable table, Column column) throws IOException { ResponseWriter writer = context.getResponseWriter(); String style = column.getStyle(); String styleClass = column.getStyleClass(); styleClass = styleClass == null ? DataTable
  • JSF 核心标签:setPropertyActionListener vs 属性 vs param(JSF Core Tag :setPropertyActionListener vs attribute vs param)
    问题 setPropertyActionListener与attribute与param之间有什么区别? 什么时候使用setPropertyActionListener ? 回答1 1. f:setPropertyActionListener: 使用此标记,您可以直接在支持 bean 中设置属性。 例子: html: <h:commandButton action="page.xhtml" value="OK"> <f:setPropertyActionListener target="#{myBean.name}" value="myname"/> </h:commandButton> 支持豆: @ManagedBean @SessionScoped public class MyBean{ public String name; public void setName(String name) { this.name= name; } } 这会将支持 bean 的name属性设置为值myname 。 2. f:参数: 这个标签简单地设置了请求参数。 例子: html: <h:commandButton action="page.xhtml"> <f:param name="myparam" value="myvalue" /> </h:commandButton>
  • 是否可以在actionListener之前调用setPropertyActionListener(Is it possible to call setPropertyActionListener before actionListener)
    问题 我目前遇到JSF执行顺序的问题。 看我的示例代码: <p:commandButton action="update.xhtml" ajax="false" icon="ui-icon-pencil" actionListener="#{marketingCodeBean.initForUpdate}"> <f:setPropertyActionListener target="#{marketingCodeBean.marketingCode}" value="#{code}"></f:setPropertyActionListener> </p:commandButton> 我想使用setPropertyActionListener设置bean属性,并在actionListener = initForUpdate上进行一些处理。 但是JSF的默认执行顺序是相反的,actionListener首先在setPropertyActionListener之前。 有没有解决此问题的方法? 我正在考虑使用一个actionListener并将bean参数传递给它,但是我不确定这是否是最好的方法。 回答1 这确实是预期的行为。 动作侦听器( actionListener , <f:actionListener>和<f:setPropertyActionListener>
  • JSF PrimeFaces inputText inside dataTable
    JSF-2.0, Mojarra 2.1.19, PrimeFaces 3.4.1 Summary of the problem: Have a p:inputText inside p:dataTable and inputText action fired by p:remoteCommand which passes the dataTable row index as a parameter with f:setPropertyActionListener. But it always passes the last row of the dataTable, not the index of the row which includes currently clicked p:inputText. As it can be seen from my previous questions, I am trying to use p:inputText as a comment taker for a status like in Facebook or etc. Implementation includes a p:dataTable. It's rows represents each status. Seems like: <p:dataTable id=
  • JSF - Ajax call - What is it wrong on this code?
    I think the best way to resolve this problem is just to paste my code : Selector bean @ManagedBean(name="selector") @RequestScoped public class Selector { @ManagedProperty(value="#{param.page}") private String page; private String profilePage; @PostConstruct public void init() { if(profilePage==null || profilePage.trim().isEmpty()) { this.profilePage="main"; } if(page==null || page.trim().isEmpty()) { this.page="homepage"; } } public String getProfilePage() { System.out.println("GET ="+profilePage); return profilePage; } public void setProfilePage(String profilePage) { this.profilePage
  • Why is the getter called so many times by the rendered attribute?
    Related to a previous example, i tried to monitor my get/set methods on the server (when they are called, and how often). So, my actual been look such : @ManagedBean(name="selector") @RequestScoped public class Selector { @ManagedProperty(value="#{param.profilePage}") private String profilePage; public String getProfilePage() { if(profilePage==null || profilePage.trim().isEmpty()) { this.profilePage="main"; } System.out.println("GET "+profilePage); return profilePage; } public void setProfilePage(String profilePage) { this.profilePage=profilePage; System.out.println("SET "+profilePage); } }
  • Is it possible to call setPropertyActionListener before actionListener
    I'm currently experiencing a problem with JSF's order of execution. Looking at my sample code: <p:commandButton action="update.xhtml" ajax="false" icon="ui-icon-pencil" actionListener="#{marketingCodeBean.initForUpdate}"> <f:setPropertyActionListener target="#{marketingCodeBean.marketingCode}" value="#{code}"></f:setPropertyActionListener> </p:commandButton> I would like to set a bean property using setPropertyActionListener, and do some processing on actionListener=initForUpdate. But JSF default sequence of execution is the opposite, actionListener first before setPropertyActionListener. Is
  • 为什么getter被render属性多次调用?(Why is the getter called so many times by the rendered attribute?)
    问题 与前面的示例相关,我试图监视服务器上的get / set方法(何时调用它们以及调用它们的频率)。 所以,我实际看起来像这样: @ManagedBean(name="selector") @RequestScoped public class Selector { @ManagedProperty(value="#{param.profilePage}") private String profilePage; public String getProfilePage() { if(profilePage==null || profilePage.trim().isEmpty()) { this.profilePage="main"; } System.out.println("GET "+profilePage); return profilePage; } public void setProfilePage(String profilePage) { this.profilePage=profilePage; System.out.println("SET "+profilePage); } } 唯一可以调用此方法的页面(它仅在呈现时调用get方法)是: <!DOCTYPE html> <ui:composition xmlns:f="http://java.sun
  • Backing bean in composite component is recreated on every request
    I have two variables "userId" and "name". When I click for example the "SHOW USERID" button it works fine and sets "renderUserId=true" and it shows it with the "render", but then if I click the other "SHOW" button, the Bean is reconstruct and I loose the "renderUserId=true" and it becomes "false" and "renderName=true" so it shows ok, but the USERID is hidden. My question is, how can I avoid loosing the bean values when I render the xhtml? This is a simple simulation of my code. NOTE: if I use "actionListener" instead of "f:setPropertyActionListener" in the "h:commandButton" I have the same
  • JSF2动作参数(JSF2 Action parameter)
    问题 我已经阅读了有关通过actionListener将参数从jsf页面传递到ManagedBean的信息。 是否还可以将参数传递给简单的操作方法? 感谢您的阅读... 谢谢你们的建议! 没有你我会迷路的:-) 以下为我工作: <h:commandLink id="link" action="#{overviewController.showDetails}" > <f:setPropertyActionListener target="#{overviewController.show_id}" value="#{project.id}" /> <h:outputText value="#{project.title}" /> </h:commandLink> 那么,现在谁该当绿色勾号呢? :-P我可以给他们两个吗? 回答1 是的。 任何一个: action="#{bean.method(param)}" 或者 <h:commandButton .. > <f:setPropertyActionListener target="#{bean.targetProperty}" value="#{param}" /> </h:commandbutton> (并在方法中使用bean属性) 回答2 您是在以这种形式谈论参数吗? <h:commandButton action="#
  • 如何将参数与h:commandButton一起传递(How to pass a parameter along with h:commandButton)
    问题 在JSF + Seam中更改语言环境的最常见方法之一-使用<h:selectOneMenu> : <h:form action="#{localeSelector.select}" rendered="false"> <h:selectOneMenu value="#{localeSelector.language}" onchange="submit()"> <f:selectItem itemLabel="English" itemValue="en" /> <f:selectItem itemLabel="Francais" itemValue="fr" /> </h:selectOneMenu> </h:form> 我想用按钮实现语言环境更改。 因此,问题是-如何传递参数(en,fr等)以使用<h:commandButton>更新bean? 也许<h:inputHidden>会有所帮助吗? 回答1 作为方法参数传递(仅当您的环境支持EL 2.2时), <h:commandButton value="English" action="#{localeSelector.change('en')}" /> <h:commandButton value="Deutsch" action="#{localeSelector.change('de')}" /> <h
  • 将值从页面传递到其他页面JSF(Passing Values from page to other page JSF)
    问题 我是Java Server Faces(JSF)的初学者,我需要将文本输入的内容传递给第二页才能显示它,第二页也是如此:我想将单选按钮值传递给第三页。 我搜索并尝试了很多但没有成功。 例如我试过 <h:commandButton value="Next" action="#{myBean.execute(input_id.value)}"/> 执行方法是: public void execute(String value) { // ... try{ FacesContext.getCurrentInstance().getExternalContext().dispatch("/Quizy.xhtml?faces-redirect=true"); } catch(Exception e){ System.out.println("err"); } } 有什么建议么? 回答1 有几种方法可以做到这一点,但这是其中一种。 您将需要将inputText值保存到bean的属性中,并且h:inputText和h:commanButton都应位于同一h:form元素中 这是一个示例代码 在您看来 <h:form> ... <h:inputText value={myBean.someValue} /> .... <h:commandButton value="Next" action=
  • 在每个请求中都将重新创建复合组件中的支持Bean(Backing bean in composite component is recreated on every request)
    问题 我有两个变量“ userId”和“ name”。 例如,当我单击“ SHOW USERID”按钮时,它可以正常工作并设置为“ renderUserId = true”,并使用“ render”显示它,但是如果我单击另一个“ SHOW”按钮,则Bean被重建并且松动“ renderUserId = true”,它变为“ false”和“ renderName = true”,因此显示确定,但USERID被隐藏。 我的问题是,渲染xhtml时如何避免丢失bean值? 这是我的代码的简单模拟。 注意:如果我在“ h:commandButton”中使用“ actionListener”而不是“ f:setPropertyActionListener”,我将得到相同的结果,则该bean将被重建 example.xhtml <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:a4j="http://richfaces.org/a4j" xmlns:cc="http://java
  • 在primefaces中绑定文件下载参数(bind a parameter for file download in primefaces)
    问题 我想在 primefaces 中下载一个名称可能会有所不同的文件。 这是控制器的代码 @ManagedBean(name="fileDownloadController", eager = true) @ViewScoped public class FileDownloadController implements Serializable{ private StreamedContent file; private String fileName; public FileDownloadController() { System.out.println("FileDownloadController sans arg"); System.out.println("getFileName:" + fileName); InputStream stream = null; try { stream = new FileInputStream("D:/myFileDir/"+fileName); } catch (FileNotFoundException ex) { Logger.getLogger(FileDownloadController.class.getName()).log(Level.SEVERE, null, ex); } file = new
  • action和actionListener之间的区别(Differences between action and actionListener)
    问题 action和actionListener什么区别,什么时候应该使用action和actionListener ? 回答1 动作监听器 如果要在执行真正的业务操作之前有一个钩子,请使用actionListener ,例如,将其记录和/或设置其他属性(通过<f:setPropertyActionListener>),和/或访问调用该方法的组件。动作(可通过ActionEvent参数获得)。 因此,纯粹是出于在实际业务操作被调用之前进行准备的目的。 默认情况下, actionListener方法具有以下签名: import javax.faces.event.ActionEvent; // ... public void actionListener(ActionEvent event) { // ... } 并且应该声明为以下内容,且不带任何方法括号: <h:commandXxx ... actionListener="#{bean.actionListener}" /> 请注意,EL 2.2无法传递其他参数。 但是,您可以通过传递并指定自定义参数来完全覆盖ActionEvent参数。 以下示例是有效的: <h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" /> <h:commandXxx
  • 我怎样才能得到一个的嵌套组件在使用 EL 的 facelet 中(How can I get the nested components of a <p:column> in a facelet using EL)
    问题 假设我有这个: <p:column headerText="R" style=" text-align: center;" width="10" rendered="true"> <p:commandLink id="MRepShowButton" update=":form1:display" onclick="EditorDialog.show();" title="Editer le compte rendu"> <f:setPropertyActionListener value="#{exam}" target="#{dyna.selectedExamen}" /> <p:graphicImage id="img1" value="/images/study_Report_icons/Text/0.png" rendered="#{exam.examen.rapport.rapportWrittenState == null}"/> <p:graphicImage id="img2" value="/images/study_Report_icons/Text/#{exam.examen.rapport.rapportWrittenState}.png" rendered="#{exam.examen.rapport.rapportWrittenState !=
  • bind a parameter for file download in primefaces
    I would like to download in primefaces a file whose name can vary. Here is the code for the controller @ManagedBean(name="fileDownloadController", eager = true) @ViewScoped public class FileDownloadController implements Serializable{ private StreamedContent file; private String fileName; public FileDownloadController() { System.out.println("FileDownloadController sans arg"); System.out.println("getFileName:" + fileName); InputStream stream = null; try { stream = new FileInputStream("D:/myFileDir/"+fileName); } catch (FileNotFoundException ex) { Logger.getLogger(FileDownloadController.class
  • View scoped managed bean with setPropertyActionListener
    I cant seem to get the view scoped managed bean to work with setPropertyActionListener: <h:commandButton value="Edit" action="edit-company.xhtml"> <f:setPropertyActionListener target="#{companyHolder.item}" value="#{company}"/> </h:commandButton> This works fine if companyHolder is session or request scoped but doesnt work if its view scoped. Is this normal?
  • Primefaces DataTable, lazy loading and CommandButton per row
    i have this simple page: <h:form id="form"> <p:dataTable value="#{testBean.unitTypeModel}" var="elem" lazy="true" rows="10"> <p:column headerText="class">#{elem.class.simpleName}</p:column> <p:column headerText="code">#{elem.code}</p:column> <p:column headerText="description">#{elem.description}</p:column> <p:column headerText="action"> <p:commandButton action="test2" icon="ui-icon ui-icon-wrench" value="edit"> <f:setPropertyActionListener target="#{testBean.selection}" value="#{elem}"/> </p:commandButton> </p:column> </p:dataTable> <p:commandButton action="test2" icon="ui-icon ui-icon-wrench"