# 12. Spring JavaScript 快速引用

## 12.1.导言

*spring-js-resources*模块是一个旧模块，不再推荐使用，但仍作为可选模块提供，以实现向后兼容。其最初的目标是提供一个客户端编程模型，用于通过行为和 Ajax 远程处理逐步增强 Web 页面。

Spring JS API 的使用在[样品库](https://github.com/spring-projects/spring-webflow-samples)中进行了演示。

## 12.2.服务 JavaScript 资源

Spring 框架提供了一种用于服务静态资源的机制。参见[Spring Framework documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-static-resources)）。使用新的 \<mvc:resources\>元素，资源请求由`DispatcherSevlet`处理。下面是 XML 中的配置示例（也可以使用 Java 配置）：

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<mvc:annotation-driven/>

	<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/web-resources/" />

	...

</beans>

		
```

此传入将对`/resources`的请求映射到在 Classpath 上`/META-INF/web-resources`下找到的资源。这就是捆绑 Spring JavaScript 资源的地方。然而，你可以在上述配置中修改位置属性，以便为来自任何 Classpath 或 Web 应用程序相对位置的资源提供服务。

请注意，完整的资源 URL 取决于如何映射 DispatcherServlet。在 MVC-Booking 示例中，我们选择将其映射为默认的 Servlet 映射“/”：

```
<servlet>
	<servlet-name>DispatcherServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
	<servlet-name>DispatcherServlet</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

		
```

这意味着要加载`Spring.js`的完整 URL 是`/myapp/resources/spring/Spring.js`。如果你的`DispatcherServlet`被映射到`/main/*`，那么完整的 URL 将是`/myapp/main/resources/spring/Spring.js`。

当使用默认的 Servlet 映射时，还建议将此添加到你的 Spring MVC 配置中，这确保你的 Spring MVC 映射未处理的任何资源请求都将被委派回 Servlet 容器。

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	...

	<mvc:default-servlet-handler />

</beans>

		
```

## 12.3.在页面中包括 Spring JavaScript

Spring JS 的设计使得可以为任何流行的 JavaScript 工具包构建其 API 的实现。 Spring.js 的初始实现建立在 Dojo 工具箱的基础上。

在页面中使用 Spring JavaScript 需要正常地包括底层工具包、底层工具包的`Spring.js`基本接口文件和`Spring-(library implementation).js`文件。作为示例，下面包括使用`ResourceServlet`获得 Spring.js 的 Dojo 实现：

```
<script type="text/javascript" src="<c:url value="/resources/dojo/dojo.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring-Dojo.js" />"> </script>
		
```

在使用底层库的小部件系统时，通常还必须包括一些 CSS 资源，以获得所需的外观和感觉。对于 Booking-MVC 引用应用程序，Dojo 的`tundra.css`包含在其中：

```
<link type="text/css" rel="stylesheet" href="<c:url value="/resources/dijit/themes/tundra/tundra.css" />" />
		
```

## 12.4. Spring JavaScript 装饰

Spring JavaScript 中的一个核心概念是将装饰应用到现有 DOM 节点的概念。这种技术用于逐步增强网页，使网页在功能较差的浏览器中仍能正常工作。使用`addDecoration`方法应用装饰。

下面的示例演示如何使用丰富的建议行为来增强 Spring MVC`<form:input>`标记：

```
<form:input id="searchString" path="searchString"/>
<script type="text/javascript">
	Spring.addDecoration(new Spring.ElementDecoration({
		elementId: "searchString",
		widgetType: "dijit.form.ValidationTextBox",
		widgetAttrs: { promptMessage : "Search hotels by name, address, city, or zip." }}));
</script>
		
```

`ElementDecoration`用于将丰富的小部件行为应用到现有的 DOM 节点。此装饰类型的目的不是完全隐藏底层工具包，因此直接使用该工具包的本机小部件类型和属性。这种方法允许你使用一个通用的装饰模型，以一致的方式集成底层工具包中的任何小部件。请参阅`booking-mvc`参考应用程序，以获取从建议到客户端验证的应用装饰的更多示例。

当使用`ElementDecoration`应用具有丰富验证行为的小部件时，一个常见的需求是在验证通过之前防止表单提交到服务器。这可以用`ValidateAllDecoration`来完成：

```
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" />
<script type="text/javascript">
	Spring.addDecoration(new Spring.ValidateAllDecoration({ elementId:'proceed', event:'onclick' }));
</script>
		
```

这将使用一个特殊的 onclick 事件处理程序来装饰“Proceed”按钮，该处理程序将触发客户端验证器，并且在表单通过之前不允许表单提交。

`AjaxEventDecoration`应用客户端事件侦听器，该侦听器向服务器触发远程 Ajax 请求。它还自动注册了一个回调函数，以便在响应中链接：

```
<a id="prevLink" href="search?searchString=${criteria.searchString}&page=${criteria.page - 1}">Previous</a>
<script type="text/javascript">
	Spring.addDecoration(new Spring.AjaxEventDecoration({
		elementId: "prevLink",
		event: "onclick",
		params: { fragments: "body" }
	}));
</script>
		
```

这将通过 Ajax 调用来装饰“上一个结果”链接的 onclick 事件，传递一个特殊的参数，该参数指定要在响应中重新呈现的片段。请注意，如果 JavaScript 在客户机中不可用，那么这个链接仍然是完全有效的。（有关如何在服务器上处理此请求的详细信息，请参见[第 12.5 节，“处理 Ajax 请求”](spring-js.html#spring-js-ajax)。

也可以对一个元素应用多个装饰。下面的示例显示了一个正在用 Ajax 和 Validate-All Submit Suppression 进行修饰的按钮：

```
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" />
<script type="text/javascript">
	Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
	Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed', event:'onclick',formId:'booking', params:{fragments:'messages'}}));
</script>
		
```

还可以使用 Dojo 的 QueryAPI 对单个语句中的多个元素应用装饰。下面的示例将一组复选框元素装饰为 Dojo 复选框小部件：

```
<div id="amenities">
<form:checkbox path="amenities" value="OCEAN_VIEW" label="Ocean View" /></li>
<form:checkbox path="amenities" value="LATE_CHECKOUT" label="Late Checkout" /></li>
<form:checkbox path="amenities" value="MINIBAR" label="Minibar" /></li>
<script type="text/javascript">
	dojo.query("#amenities input[type='checkbox']").forEach(function(element) {
		Spring.addDecoration(new Spring.ElementDecoration({
			elementId: element.id,
			widgetType : "dijit.form.CheckBox",
			widgetAttrs : { checked : element.checked }
		}));
	});
</script>
</div>
		
```

## 12.5.处理 Ajax 请求

Spring JavaScript 的客户端 Ajax 响应处理建立在从服务器接收“碎片”的概念上。这些片段只是标准的 HTML，旨在替换现有页面的部分内容。服务器上所需的关键部分是一种确定需要将完整响应中的哪些部分拉出以进行部分呈现的方法。

为了能够呈现完整响应的部分片段，必须使用模板化技术构建完整响应，该模板化技术允许使用组合来构造响应，并且组合的成员部分可以被引用并单独呈现。 Spring JavaScript 提供了一些简单的 Spring MVC 扩展，它们利用磁贴来实现这一点。理论上，同样的技术可以用于任何支持合成的模板化系统。

Spring JavaScript 的 Ajax 远程处理功能是建立在这样一种理念之上的：Ajax 请求的核心处理代码不应与标准的浏览器请求不同，因此，在代码中不需要直接提供有关 Ajax 请求的特殊知识，并且可以对两种类型的请求使用相同的方法。

### 12.5.1.提供特定于库的 AjaxHandler

将各种 Ajax 库与 Web 流的 Ajax 感知行为集成在一起的关键接口是`org.springframework.js.AjaxHandler`。默认情况下配置了`SpringJavascriptAjaxHandler`，该配置能够检测通过 Spring JS 客户端 API 提交的 Ajax 请求，并且能够在需要重定向的情况下做出适当响应。为了集成不同的 Ajax 库（无论是纯 JavaScript 库，还是更高级别的抽象，例如支持 Ajax 的 JSF 组件库），可以将自定义的`AjaxHandler`注入到`FlowHandlerAdapter`或`FlowController`中。

### 12.5.2.用 Spring 个 MVC 控制器处理 Ajax 请求

为了用 Spring MVC 控制器处理 Ajax 请求，所需要的只是在你的 Spring 应用程序上下文中配置所提供的 Spring MVC 扩展，以呈现部分响应（请注意，这些扩展需要使用用于模板化的磁贴）：

```
<bean id="tilesViewResolver" class="org.springframework.webflow.mvc.view.AjaxUrlBasedViewResolver">
	<property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTiles3View"/>
</bean>
			
```

这将配置`AjaxUrlBasedViewResolver`，后者反过来解释 Ajax 请求并创建`FlowAjaxTilesView`对象，以处理适当片段的呈现。注意，`FlowAjaxTilesView`能够同时处理 Web 流和纯 Spring MVC 请求的呈现。这些片段对应于瓷砖视图定义的各个属性。例如，以下面的 Tiles 视图定义为例：

```
<definition name="hotels/index" extends="standardLayout">
	<put-attribute name="body" value="index.body" />
</definition>

<definition name="index.body" template="/WEB-INF/hotels/index.jsp">
	<put-attribute name="hotelSearchForm" value="/WEB-INF/hotels/hotelSearchForm.jsp" />
	<put-attribute name="bookingsTable" value="/WEB-INF/hotels/bookingsTable.jsp" />
</definition>
			
```

Ajax 请求可以指定要作为请求中的片段呈现的“Body”、“HotelSearchForm”或“BookingStable”。

### 12.5.3.用 Spring MVC+ Spring Web 流处理 Ajax 请求

Spring Web 流通过使用`render`元素直接在流定义语言中处理片段的可选呈现。这种方法的好处是，片段的选择与客户端代码完全解耦，这样就不需要以纯 Spring MVC 控制器方法当前必须采用的方式与请求一起传递特殊参数。例如，如果你希望将前面的示例磁贴视图中的“HotelSearchForm”片段呈现为一个富 JavaScript 弹出窗口：

```
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
	<on-entry>
		<render fragments="hotelSearchForm" />
	</on-entry>
	<transition on="search" to="reviewHotels">
		<evaluate expression="searchCriteria.resetPage()"/>
	</transition>
</view-state>
			
```