事件处理,JavaScript和Ajax

JSF的最大卖点之一是它是一个基于组件的框架。 这意味着您可以实现您或其他人可以重复使用的组件。 在大多数情况下,这种强大的重用机制在JSF 1中变得无关紧要,因为很难实现组件。

但是,正如您在第2部分中所看到的那样,JSF 2借助一种称为复合组件的新功能,可以轻松地实现组件-无需Java代码也无需配置。 该功能很可能是JSF 2最重要的部分,因为它最终实现了JSF组件的潜力。

在有关JSF 2的第三篇也是最后一篇文章中,我将向您展示如何通过使用JSF 2中还引入的新Ajax和事件处理功能来构建复合组件功能,并提供以下技巧,以充分利用JSF 2:

  • 提示1:组件化
  • 提示2:Ajaxify
  • 提示3:显示进度

在第一个技巧中,我将简要回顾我在第2部分中详细讨论的两个组件。 在随后的技巧中,我将向您展示如何使用Ajax和事件处理来转换那些组件。

提示1:组件化

我在第1部分中介绍的places应用程序包含许多复合组件。 一个是map组件,它显示一个地址地图,并带有一个缩放级别的下拉菜单,如图1所示:

图1. places应用程序的map组件
事件处理,JavaScript和Ajax_第1张图片

清单1中显示了map组件的截断清单:

清单1. map组件

    
   
   
     
   
        
    
   
     
... ...
...

组件的一大优点是,您可以使用功能更强大的替代产品来替换它们,而不会影响任何周围的功能。 例如,在图2中,我已经更换了image组件从清单1用谷歌地图组件,GMaps4JSF的礼貌(参见相关主题 ):

图2. GMaps4JSF的地图图像
事件处理,JavaScript和Ajax_第2张图片

清单2中显示了map组件的更新(和截断)代码:

清单2.用GMaps4JSF组件替换地图图像

     
  
                    
   

...         


     
  
          
                    
       
    

要使用GMaps4JSF组件,我用GMaps4JSF组件集中的标记替换了标记。 通过为标记的zoom属性指定正确的backing-bean属性,将GMaps4JSF组件挂接到zoom下拉菜单也很简单。

说到缩放级别,请注意,当用户更改缩放级别时,我强制使用onchange属性提交表单,如清单1的第一部分黑体所示 。 该表单提交触发了JSF生命周期,最终将缩放级别的新值推入存储在父复合组件中的location bean的zoomIndex属性中。 该bean属性绑定到清单2的第一行中的输入组件。

因为我没有为与缩放级别更改相关联的表单提交指定任何导航,所以JSF在处理请求后刷新了同一页面,重新绘制了地图图像以反映新的缩放级别。 但是,即使唯一的更改是在地图图像中,该页面刷新也会重绘整个页面。 在技巧2:Ajaxify中 ,我将向您展示如何使用Ajax来仅重绘图像以响应缩放级别的变化。

login组件

场所应用程序中使用的另一个组件是login组件。 图3显示了运行中的登录组件:

图3. login组件
事件处理,JavaScript和Ajax_第3张图片

清单3显示了创建图3中所示的login组件的标记:

清单3.极简login :只是必需的属性


  
                

login组件只有两个必需的属性:

  • loginAction :登录操作方法
  • managedBean :具有名称和密码属性的托管Bean

清单3中显示了清单3中指定的托管bean:

清单4. User.groovy
package com.clarity

import javax.faces.context.FacesContext
import javax.faces.bean.ManagedBean
import javax.faces.bean.SessionScoped
 
@ManagedBean() 
@SessionScoped  
               
public class User {    
  private final String VALID_NAME     = "Hiro"
  private final String VALID_PASSWORD = "jsf"
  
  private String name, password;
 
  public String getName() { name }
  public void setName(String newValue) { name = newValue }
  
  public String getPassword() { return password }
  public void setPassword(String newValue) { password = newValue }  
  
  public String login() {
    "/views/places"
  }

  public String logout() {
    name = password = nameError = null
    "/views/login"
  }
}

清单4中的托管bean是Groovy bean。 在这种情况下,使用Groovy代替Java语言不会给我带来很多好处,除了使我摆脱分号和return语句的繁琐工作之外。 但是,在技巧2的“ 验证”部分中,我将向您展示一个更有说服力的理由,将Groovy用于User管bean。

大多数时候,您将需要使用提示和按钮文本来完全配置登录组件,如图4所示:

图4.完全配置的login组件
事件处理,JavaScript和Ajax_第4张图片

清单5显示了生成图4中的login组件的标记:

清单5.配置login组件

  
  
                

在清单5中 ,我从资源包中获取提示字符串和登录按钮文本。

清单6定义了login组件:

清单6.定义login组件



 

    
  
  
  
    
    
    
    
    
          
    
    
    
    
                     
    
    
      
    
    
  
    
  
  
    
#{cc.attrs.loginPrompt}
#{cc.attrs.namePrompt} #{cc.attrs.passwordPrompt}

map组件一样, login组件可以使用Ajax升级。 在下一个技巧中,在Validation下,我将向您展示如何将Ajax验证添加到登录组件。

提示2:Ajaxify

对于非Ajax HTTP请求,Ajax通常需要完成两个通常不执行的步骤:服务器上表单的部分处理,以及客户端上文档对象模型(DOM)的后续部分呈现。

部分处理和渲染

JSF 2通过将JSF生命周期分为两个不同的逻辑部分来支持部分处理和部分渲染:执行和渲染。 图5突出显示了执行部分:

图5. JSF生命周期的执行部分
事件处理,JavaScript和Ajax_第5张图片

图6突出显示了JSF生命周期的呈现部分:

图6. JSF生命周期的呈现部分
事件处理,JavaScript和Ajax_第6张图片

生命周期的执行和呈现部分背后的想法很简单:您可以指定JSF在服务器上执行(处理)的组件,以及当Ajax调用返回时JSF呈现的组件。 使用 ,这是JSF 2的新增功能,如清单7所示:

清单7. Ajax缩放菜单


  
  
             
             
     

清单7是清单2第一行所示菜单的修改:我从清单2中删除了onchange属性,并添加了标记。 标记指定:

  • 触发Ajax调用的事件
  • 在服务器上执行的组件
  • 要在客户端上渲染的组件

当用户从缩放菜单中选择一个项目时,JSF会对服务器进行Ajax调用。 随后,JSF将菜单传递到生命周期的执行部分( @this表示的周围组件),并在生命周期的“更新模型值”阶段更新菜单的zoomIndex 当Ajax调用返回时,JSF渲染地图组件,该组件使用(新设置的)缩放索引重绘地图,现在您有了Ajaxified缩放菜单,其中增加了一行XHTML。

但是事情会变得更加简单,因为JSF为event提供了默认值并execute属性。

每个JSF组件都有一个默认事件,如果您将标记嵌入到组件标记中,则该事件会触发Ajax调用。 对于菜单,该事件为change事件。 这意味着我可以摆脱清单7中的event属性。 execute属性的@this值为@this ,表示的周围组件。 在此示例中,该组件是菜单,因此我也可以摆脱execute属性。

通过使用默认属性值,我可以将清单7简化为清单8:

清单8.一个简单版本的Ajax缩放菜单


  
  
             
             

这就是使用JSF 2将Ajax添加到您的组件中那么容易。当然,前面的示例非常简单:当用户选择缩放级别时,我只是重绘地图而不是整个页面。 一些操作(例如验证表单中的各个字段)更加复杂,因此接下来我将解决该用例。

验证方式

当用户跳出字段时,最好验证字段并提供即时反馈。 例如,在图7中,我使用Ajax来验证名称字段:

图7. Ajax验证
事件处理,JavaScript和Ajax_第7张图片

清单9中显示了name字段的标记:

清单9.名称字段

  #{cc.attrs.namePrompt}
  
    
       
       
       
     
     
     
       
  ... 

再次,我使用只不过这一次的默认输入事件- change -不会做,所以我指定blur的事件触发Ajax调用。 当用户跳出名称字段时,JSF会对服务器进行Ajax调用,并在生命周期的执行部分运行name输入组件。 这意味着JSF将在生命周期的流程验证阶段调用清单9中指定的name输入的值更改侦听器。 清单10显示了值更改侦听器:

清单10. validateName()方法
package com.clarity

import javax.faces.context.FacesContext
import javax.faces.bean.ManagedBean
import javax.faces.bean.SessionScoped
import javax.faces.event.ValueChangeEvent 
import javax.faces.component.UIInput 
 
@ManagedBean()  
@SessionScoped    
       
public class User {    
  private String name, password, nameError;
 
  ...
  
  public void validateName(ValueChangeEvent e) {
    UIInput nameInput = e.getComponent()
    String name = nameInput.getValue()
    
    if (name.contains("_"))   nameError = "Name cannot contain underscores"
    else if (name.equals("")) nameError = "Name cannot be blank"
    else                      nameError = "" 
  }
  
  ...
}

值更改侦听器- user管理的bean的validateName()方法-验证名称字段并更新user管理的bean的nameError属性。

在Ajax调用返回之后,借助于清单9中的标记的render属性,JSF呈现nameError输出。 该输出显示user管理的bean的nameError属性。

多场验证

在前面的小节中,我向您展示了如何在单个字段上执行Ajax验证。 但是,有时您需要同时验证多个字段。 例如,图8显示了places应用程序一起验证名称和密码字段:

图8.验证多个字段
事件处理,JavaScript和Ajax_第8张图片

当用户提交表单时,我将同时验证名称和密码字段,因此本示例不需要Ajax。 相反,我将使用JSF 2的新事件系统,如清单11所示:

清单11.使用

  
  
  ...


在清单11中 ,我使用了 ,它像是JSF 2的新功能。 标记在另一方面也类似于 :很简单使用。

您将一个标记放在一个组件标记内,当该组件发生指定的事件(由type属性指定)时,JSF会调用一个由listener属性指定的方法。 因此,用英语来说, 清单11中的标记的含义是:验证表单之后,在用户传递给此复合组件的托管bean上调用validate()方法。 该方法如清单12所示:

清单12. validate()方法
package com.clarity

import javax.faces.context.FacesContext
import javax.faces.bean.ManagedBean
import javax.faces.bean.SessionScoped
import javax.faces.event.ValueChangeEvent 
import javax.faces.component.UIInput 
 
@ManagedBean()  
@SessionScoped    
       
public class User {    
  private final String VALID_NAME     = "Hiro";
  private final String VALID_PASSWORD = "jsf";
  
  ...
  
  public void validate(ComponentSystemEvent e) {
    UIForm form = e.getComponent() 
    UIInput nameInput = form.findComponent("name")
    UIInput pwdInput = form.findComponent("password")
    
    if ( ! (nameInput.getValue().equals(VALID_NAME) &&
        pwdInput.getValue().equals(VALID_PASSWORD))) {
      
      FacesContext fc = FacesContext.getCurrentInstance()
      fc.addMessage(form.getClientId(), 
        new FacesMessage("Name and password are invalid. Please try again."))
      fc.renderResponse()
    }
  }
  
  ...
}

JSF将清单12中的validate()方法传递给组件系统事件,该方法从该事件获取对该事件所适用的组件的引用-登录表单。 从表单中,我使用findComponent()方法获取名称和密码组件。 如果这些组件的值分别不是Hiro和jsf,则将消息存储在faces上下文中,并告诉JSF立即进入生命周期的Render Response阶段。 这样,我避免了“更新模型值”阶段,该阶段会将错误名称和密码推送到模型中(参见图5 )。

您可能已经注意到, 清单10和清单12中的验证方法是用Groovy编写的。 与清单4不同, 清单4中使用Groovy的唯一优势是无需使用分号和return语句, 清单10和清单12中的Groovy代码使我摆脱了强制转换。 例如,在清单10中 , ComponentSystemEvent.getComponent()UIComponent.findComponent()都返回类型UIComponent 使用Java语言,我将不得不转换那些方法的返回值。 Groovy为我做演员。

提示3:显示进度

在Ajaxify中 ,我向您展示了如何Ajaxify map组件的缩放菜单,以便当用户更改缩放级别时,places应用程序仅重绘页面的地图部分。 另一个常见的Ajax用例是向用户提供有关Ajax事件正在进行中的一些反馈,如图9所示:

图9.进度条
事件处理,JavaScript和Ajax_第9张图片

在图9中 ,我用动画GIF替换了缩放菜单,该动画在Ajax调用进行时显示。 Ajax调用完成后,我将进度指示器替换为缩放菜单。 清单13显示了它是如何完成的:

清单13.监视一个Ajax请求


  
  

  ...             

...

在清单13中 ,我添加了一个最初不显示的进度条图像,并为指定了onevent属性。 该属性引用清单14中所示JavaScript函数,而清单13中发起的Ajax调用进行时,JSF调用了该函数:

清单14.响应Ajax请求JavaScript
function zoomChanging(data) {
  var menuId = data.source.id;
  var progressbarId = menuId.substring(0, menuId.length - "menu".length)
      + "progressbar";

  if (data.name == "begin") {
    Element.hide(menuId);
    Element.show(progressbarId);
  } 
  else if (data.name == "success") {
    Element.show(menuId);
    Element.hide(progressbarId);
  }
}

JSF向清单14中的函数传递一个对象,该对象包含一些信息,例如触发事件的组件的客户端标识符(在本例中为缩放级别菜单)以及Ajax请求的当前状态,由name不佳的name属性。

清单14中所示的zoomChanging()函数计算进度条图像的客户端标识符,然后使用Prototype Element对象在Ajax调用期间隐藏和显示适当HTML元素。

结论

多年来,JSF 1作为难以使用的框架而享有盛誉。 在许多方面,这种声誉是应得的。 JSF 1是在象牙塔中开发的,没有现实使用中的大量后见之明。 结果,JSF使实现应用程序和组件的难度比原先要困难得多。

另一方面,JSF 2是从现实经验的坩埚中诞生的,这些人是在JSF 1之上实施开源项目的人们。现实世界的事后见识导致了一个更为精明的框架,使其易于实施。强大的Ajaxified应用程序。

在整个系列中,我向您展示了一些最杰出的JSF 2功能,例如用于替换配置的注释和约定,简化的导航,对资源的支持,复合组件,内置的Ajax和扩展的事件模型。 但是JSF 2具有我在本系列中没有提到的更多功能,例如View和Page范围,对可标记页面的支持以及Project Stage。 所有这些功能以及更多功能,使JSF 2对其前身进行了巨大的改进。


翻译自: https://www.ibm.com/developerworks/java/library/j-jsf2fu3/index.html

你可能感兴趣的:(java,vue,spring,javascript,python)