#{extends '../main.html'/}# #{set title:'Webpieces QuickStart'/}# #{set tab:'management'/}# #{renderTagArgs 'docHome.html'/}# #{renderTagArgs 'quickStartList.html'/}#

AJAX Crud

Please realize that there is more advanced things to worry about in ajax development like

  1. How to handle no network (easily done in old html)
  2. How to handle clicking a secure page when logged out(ie. need to redirect to login)

Most of these things get forgotten so we take extra care in this pattern to address things like that. Even if someone remembers to do one use-case of a pop-up, others are simply forgotten or left out regarding these errors resulting in a bad user experience.

Lastly, we use a mix of AJAX plus old school POST/REDIRECT/GET intentionally so that you as the developer can write a ton less code to deliver an AJAX experience and still give the user a great experience on usability.

Ok, so let's start the upgrade to AJAX for our AccountDbo. The first thing, the RouteIds file is going to stay exactly the same as before:

public enum AccountRouteId implements RouteId {
	//list
	LIST_ACCOUNTS,
	//add/edit
	GET_ADD_ACCOUNT_FORM, GET_EDIT_ACCOUNT_FORM, POST_ACCOUNT_FORM,
	//delete
	CONFIRM_DELETE_ACCOUNT, POST_DELETE_ACCOUNT
}

Next, we do not need to touch MyMainRoutes.java as it already has this code which again works for the ajax version as well:

CrudRouteIds routeIds = new CrudRouteIds(
        AccountRouteId.LIST_ACCOUNTS, AccountRouteId.GET_ADD_ACCOUNT_FORM,
        AccountRouteId.GET_EDIT_ACCOUNT_FORM, AccountRouteId.POST_ACCOUNT_FORM,
        AccountRouteId.CONFIRM_DELETE_ACCOUNT, AccountRouteId.POST_DELETE_ACCOUNT);
addCrud("account", "CrudAccountController", routeIds);

Finally, we get started with some tweaks. First, modify the accountList method in CrudAccountController.java to have the ability to refresh with a popup model(when needed):

*[public Action accountList() {
		EntityManager mgr = Em.get();
		List accounts = AccountDbo.findAll(mgr);
		boolean showEditPopup = Current.flash().isShowEditPopup();
		return Actions.renderThis(
				"accounts", accounts,
				"showPopup", showEditPopup);
}]*

In the case where we mix in a Post/Redirect/Get, this gives us a way to popup a modal dialogue. Let's modify the accountList.html next to add this modal dialogue as well as some bootstrapModal tags for add/edit/delete that can open up a popup modal dialogue:

*[#{form action:@[POST_ACCOUNT_FORM]@, class:'form-horizontal', style:'min-width:500px;max-width:800px;margin: 0 auto'}#
   
    
    
#{/form}#]*

In this new html, you will see an id="addEditModal" for the add/edit popup and a id="deleteModal" for the delete confirmation popup. You will also notice 3 bootstrapModal tags which all are passed a modalId of addEditModal or deleteModal to connect it with that popup dialogue.

While you can now render your new list page(http://localhost:8080/account/list, all of the bootstrapModal tags have an ajax call to get incorrect not yet updated html so you can click buttons and see weird results.

Let's fix that next. First, the controller accountAddEdit doesn't need to be touched, but the accountAddEdit.html file has a bit too much html since it include an entire html page. Let's remove the first 3 lines and also add some error messaging in like so:

*[#{form action:@[POST_ACCOUNT_FORM]@, class:'form-horizontal', style:'min-width:500px;max-width:800px;margin: 0 auto'}#
   
    
    
#{/form}#]*

Basically, we no longer extend a supertemplate meaning this html has no *[ or ]* html in it. It is literally just a partial html snippet to be fetched from the ajax call and put into the modal dialogue. Lastly, we modified the Cancel as well to dismiss the dialogue

At this point, you should be able to click 'Add Account' and see your new modal popup. Let's barely tweak the post controller method next however to be compatible:

*[public Redirect postSaveAccount(AccountDbo entity) {
		if(entity.getName() == null) {
			Current.validation().addError("entity.name", "password is required");
		} else if(entity.getName().length() < 3) {
			Current.validation().addError("entity.name", "Value is too short");
		}

		if(entity.getContactsName() == null) {
			Current.validation().addError("entity.contactsName", "First name is required");
		} else if(entity.getContactsName().length() < 3) {
			Current.validation().addError("entity.contactsName", "First name must be more than 2 characters");
		}

		if(Current.validation().hasErrors()) {
			Current.flash().setError("Errors in form below");
			Current.flash().setShowEditPopup(true); //ensures we show the edit popup for listUsers on redisplay
			return Actions.redirectFlashAllSecure(AccountRouteId.LIST_ACCOUNTS, Current.getContext(), "password");
		}

		Current.flash().setMessage("User successfully saved");
		Current.flash().keep();

		Em.get().merge(entity);
        Em.get().flush();

		return Actions.redirect(AccountRouteId.LIST_ACCOUNTS);
	}]*

Notice the only thing that changed was inside the hasErrors() if statement. We always redirect back to LIST_ACCOUNTS in this case so we can re-use typical error handling and not do nasty extra javascript validation. We also setShowEditPopup(true) since there were errors in the form and set a global error with Current.flash().setError()

Give it a little spin and add invalid data and then adding 3 valid users. Once you are done, hit the back button 3 times. Wow, doesn't that suck...the beauty of javascript. These are the tradeoffs you are making when choosing a more complex javascript path so beware of that as you can many times make your user experience better in one aspect but worse in another. Personally as a user I get pretty pissed off at websites when I hit the back button and it doesn't go back to what I expected.

Again, we can skip modifying confirmDeleteAccount method as it is the same and go right to stripping the 3 lines from the confirmDeleteAccount.html which will become partial html instead of a full page:

*[#{form action:@[POST_DELETE_ACCOUNT, id:entity.id]@, class:'form-horizontal', style:'min-width:500px;max-width:800px;margin: 0 auto'}#

   

    
    
#{/form}#]*

This template now returns partial html to be stuffed into the modal dialogue and has a cancel button that simply dismisses the dialogue

Now, go ahead and create and delete entities to your hearts content to see the flow of the standard AJAX CRUD code for webpieces. We hope to have a plugin to generate all this on fly in the future for you. Basically follow a wizard, type in your fields and we generate the list page, add/edit/delete pages, controllers, routes, etc. all in one tight little package.

In the next tutorial let's do an ajax CRUD in webpieces which is the same amount of code ironically.

Next Dealing with Login