Announcement Announcement Module
Collapse
No announcement yet.
Spring Javascript Best Practices Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Javascript Best Practices

    Hi all,

    Many thanks to anyone who reads this.

    I am looking for a little 'best practice advice' when using Spring Javascript to add decorations. I have been using Spring JS with Tiles for some time to enhance my application with Ajax events.

    Imagine the following scenario:

    A JSP page that lists products in a table. Then on the same page a button that allows the user to add new products. When the user clicks the button it shows a modal/popup dialog asking for the product details. To make the product add for show as a dialog I can make use of the Spring JS like so:

    Code:
    <a id="addProductLink" href="/addProduct">Add</a>
    <script type="text/javascript">
    Spring.addDecoration(new Spring.AjaxEventDecoration({
        elementId: "addProductLink",
        event: "onclick",
        popup: true
    }));
    </script>
    When the popup is shown the user fills in the fields, now if the user fills in any of the fields incorrectly and submits the save the modal dialog should stay displayed but contain (after some @Valid Spring magic ) the Spring errors. My Spring Controllers handle the validation.

    If the user fills in the new product form correctly the modal dialog should then close and refresh the table of products underneath.

    I would assume that this would be something of typical required behaviour for a lot of web applications?

    Currently I have implemented this in my own web application but it feels more like I have hacked it together and not used Spring JS correctly? Currently I have developed this by using Spring MVC and Spring controllers not by using any Web Flow concepts like different flows and flow.xml files.

    Does anyone have any ideas on the best way to do this? I have tried looking over the Spring Travel example and noted that it makes use of Popup when a user changes their search but it seems to be using Spring Web flows and I cannot see how they close the modal dialog after the user submits the form. Also the form doesn't have/require any validation?

    Would be great for any advice or help

    Many thanks,

    Eggsy

  • #2
    My hacky implementation

    To share my current implementation method:

    Products.jsp

    Code:
    <tiles:insertAttribute name="productsList" />
    
    <tiles:insertAttribute name="productEditForm" />
    
    <p>
    	<a id="createNewProductButton" href="<c:url value='/products/-1' />">Create</a>
    </p>
    
    <script type="text/javascript">
    	Spring.addDecoration(new Spring.AjaxEventDecoration({
    		elementId: "createNewProductButton",
    		event: "onclick",
    		popup: true
    	}));
    </script>
    ProductForm.jsp

    Code:
    <div id="productEditForm">
    	<form:form id="productCreationForm" method="POST" modelAttribute="product">
    		<table>
    			<tr>
    				<td>
    					Product name
    				</td>
    				<td>
    					<form:input path="productName"/>
    				</td>
    				<td>
    					<form:errors path="productName" />
    				</td>
    			</tr>
    			<tr>
    				<td colspan="3">
    					<input type="button" onclick="submitForm()" class="dijitButtonNode" id="saveChanges" value="Save Changes" />
    					<input type="button" class="dijitButtonNode" onclick="closeDialog()" id="cancelButton" value="Cancel" />
    				</td>
    			</tr>
    		</table>
    	</form:form>
    	<script type="text/javascript">
    		function submitForm() {
    			Spring.remoting.submitForm('saveChanges', 'productCreationForm', {fragments: "productEditForm,productsList"}); 
    			return false;
    		}
    
    		function closeDialog() {
    			dojo.query(".dijitDialog").forEach(function(element) {
    				dijit.byId(element.id).hide();
    			});
    			return false;
    		}
    	</script>
    	<!-- If the form has been submitted and doesn't have errors then close the dialog -->
    	<c:if test="${not empty hasErrors && !hasErrors}">
    		<script type="text/javascript">
    			dojo.query(".dijitDialog").forEach(function(element) {
    				dijit.byId(element.id).hide();
    			});
    		</script>
    	</c:if>
    </div>
    Tiles templates XML

    Code:
    <definition name="products.view" extends="myTemplate.view">
    	<put-attribute name="main" value="productsListing.view" />
    </definition>
    	
    <definition name="productsListing.view" template="/WEB-INF/pages/products.jsp">
    	<put-attribute name="productsList" value="/WEB-INF/pages/productsList.jsp" />
    	<put-attribute name="productEditForm" value="/WEB-INF/pages/blank.jsp" />
    </definition>
    	
    <definition name="productEditForm.view" extends="productsListing.view">
    	<put-attribute name="productsList" value="/WEB-INF/pages/blank.jsp" />
    	<put-attribute name="productEditForm" value="/WEB-INF/pages/ProductEditForm.jsp" />
    </definition>
    	
    <definition name="productSaveSuccess.view" extends="productsListing.view">
    	<put-attribute name="productsList" value="/WEB-INF/pages/productsList.jsp" />
    	<put-attribute name="productEditForm" value="/WEB-INF/pages/ProductEditForm.jsp" />
    </definition>

    ProductController.java

    Code:
    @Controller
    @RequestMapping(value="/products/{productId}")
    public class ProductController {
    
    	
    	@RequestMapping(method=RequestMethod.GET)
    	public String showProduct(@PathVariable Long productId) {
    		return "productEditForm.view";
    	}
    	
    	@RequestMapping(method={RequestMethod.POST})
    	public String saveProduct(@Valid Product prod, BindingResult results, ModelMap modelMap) {
    		
    		if (results.hasErrors()) {
    			modelMap.addAttribute("hasErrors", true);
    			return "productEditForm.view";
    		}
    		else {
    			// save the product
    			modelMap.addAttribute("hasErrors", false);
    			
    			return "productSaveSuccess.view";
    		}
    	}
    	
    	@ModelAttribute("product")
    	public Product getProduct(@PathVariable Long productId) throws Exception {
    		
    		Product prod = new Product();
    		
    		if( productId != null && productId != -1) {
    			// fetch the product....
    		}
    
    		return prod;
    	}
    }
    The crucial part of my code is the hasErrors item that I place in my modelMap. When the form is submitted if it has errors I return the same edit product view.

    However if it doesn't I return the success view which includes the product edit form and if the hasErrors attribute is set to true it hides the dialog via Javascript.

    I'm sure you'll agree this implementation is somewhat clunky and definately a bit hacky?

    Eggsy

    Comment

    Working...
    X