Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Go From Zero to Serverless with Backand, Part 4

DZone's Guide to

How to Go From Zero to Serverless with Backand, Part 4

Welcome to Part 4! We'll be going over how to modify our UI using HTML and JavaScript, and adding some extra functionality and dynamic behaviors.

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

With our app hooked up to a Backand application, we now have the world at our fingertips in terms of possible functionality. We can use the pattern we established in the last part of this tutorial (Part 3) in conjunction with the SDK to implement any type of functionality we desire. In this stop along the way, we'll add a UI to create new to-do items, enable editing and deleting to-do items, and add some dynamic behavior to our jQuery UI.

Adding Creation

Now that we're hooked up to the Backand SDK, adding to-do item creation is as easy as adding the UI for a new item and wiring it up to the SDK call needed. The Backand SDK provides backand.object.create() for this purpose. The create method wraps an HTTP POST call, which creates an object in a Backand application. Let's add the code for that now:

var dataService = {
  // other data service functions here
  create: function(data) {
    backand.object.create("items", data);
  }
};

Now, we can simply call dataService.create() with our object's data, and we'll have a new item created in our back end. Next, we'll need to create a simple UI for adding a new to-do item. We'll put this at the top of the page, and separate it from the list using an inline form:

    <form class="form-inline">
      <label class="sr-only" for="nameInput">Name</label>
      <input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="nameInput" placeholder="Name">
      <label class="sr-only" for="descriptionInput">Description</label>
      <input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="descriptionInput" placeholder="Description">
      <button type="submit" class="btn btn-primary" id="createItem">Create</button>
    </form>

As you can see, we create a very simple form consisting of an input for the to-do item's name and description. I've opted not to include support for the "completed" property of an item - we're actually going to tackle that later. Now that we have the UI in place, we need to connect the UI to Backand. We'll do this with a jQuery .click() handler, defined in our document.ready handler:

$(document).ready( function() {
  // other page load code above...
  // Define the click handler
  $("#createItem").click(function(){
    // get data from the form
    objectData = {}
    nameField = $("#nameInput");
    descriptionField = $("#descriptionInput");
    objectData.name = nameField.val();
    objectData.description = descriptionField.val();

    // reset the form
    nameField.val('');
    descriptionField.val('');

    // create the item, then reload the view
    myDataService.create(objectData).then(function(){myDataService.getData();});

    // return false to stop propogation
    return false;
  });
});

This click handler binds to the button we created using the #createItem selector. Then, we obtain the name and description from the form, and use it to build our new object. Next, we send the new object to Backand for creation, and reset the form. Finally, we reload the to-do list in the success handler for the create call, updating our UI with a fresh copy of our to-do list. The page now looks like the following:

Screen Shot 2017-04-12 at 3.08.32 PM.png

Simply enter a name and a description in the provided fields, then hit "Create" to enter the item into the database!

Deletion

As you've probably added several test items you'd prefer to do without as you worked through the prior section, let's handle deleting an object next. We'll use a glyph to drive the deletion UI. Update the to-do item rendering code in app.js to include a delete icon at the beginning of each row:

  const ITEM_TAG =  "<a href='#' class='list-group-item list-group-item-action'>"
                    + "<div class='d-flex w-100 justify-content-between'>"
                    + "<i class='fa fa-times delete-icon' aria-hidden='true' id='delete-ITEM_ID'></i>"
                    + "<h5 class='mb-1 col5'>ITEM_NAME</h5>"
                    + "<button type='button' class='btn btn-success'>Expand</button>"
                    + "</div></a>";

We also need to update our HTML population to account for the new ITEM_ID variable:

for( var i = 0; i < length; i++ ) {
    finalHtml.push(ITEM_TAG.replace('ITEM_NAME', itemList[i].name).replace('ITEM_ID', itemList[i].id));
  }

Finally, this approach uses Font Awesome, a free open-source font library. To properly display the delete icon, we need to include this library's CSS in index.html:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"  crossorigin="anonymous" />

Now, we render a small delete icon next to every entry in the list:

Screen Shot 2017-04-12 at 4.04.42 PM.png

Each delete icon is given a class of delete-icon, as well as an ID of the form delete-ITEM_ID, where ITEM_ID is the integer ID of the current object. Next, we'll add the delete function to our dataService class, using the provided backand.object.remove() method:

var dataService = {
  // other data service code here
  delete: function(id) {
    // Delete the entry, and return the promise
    return backand.object.remove("items", id);
  }
};

At this point, we're faced with a somewhat tricky technical issue. jQuery binds event handlers to DOM selectors, which are XPath representations that can be used to identify singular or groups of HTML elements on a page. As we build our to-do list dynamically, we don't have this option. To get around the problem, we'll instead key off of the CSS class that we define - .delete-icon - to drive our dynamic deletion. If we set our detector on a parent element that we know exists, like body, we can then use the on handler with a selector for our icon class. Then all we need to do is parse the ID to pull out the numeric ID of the to-do item being deleted:

  $("body").on("click",".delete-item",function(event) {
    deletion_icon_id = event.target.id;
    entry_id = deletion_icon_id.split('-')[1];
    myDataService.delete(entry_id).then(function(){myDataService.getData();});
    // Return false to stop propogation
    return false;
  });

Once again, we use the success handler to trigger a page refresh, thus dynamically updating our list as it is modified.

Updates

Now that we have added item creation, and item deletion, we have only one more feature we need to have a fully-functional to-do list application - marking items completed. We'll do this using the backand.object.update() function to update the completed property of our object model. First, we'll add a new button to the dynamic item HTML that we can use to mark an item completed:

const ITEM_TAG =  "<a href='#' class='list-group-item list-group-item-action'>"
                    + "<div class='d-flex w-100 justify-content-between'>"
                    + "<i class='fa fa-times delete-item' aria-hidden='true' id='delete-ITEM_ID'></i>"
                    + "<h5 class='mb-1 col5'>ITEM_NAME</h5>"
                    + "<button type='button' class='btn btn-success'>Expand</button>"
                    + "<button type='button' class='btn btn-primary complete-button' id='complete-ITEM_ID' style='IS_COMPLETED'>Complete</button>"
                    + "<button type='button' class='btn btn-primary uncomplete-button' id='uncomplete-ITEM_ID' style='IS_NOT_COMPLETED'>Reopen</button>"
                    + "</div></a>";

You'll see that I actually added two buttons above. We'll use some dynamic logic to change these buttons to be either a "completion" button, or an "uncompletion" button. The logic to do this is as follows:

  for( var i = 0; i < length; i++ ) {
    itemId = itemList[i].id;
    itemName = itemList[i].name;
    isCompleted = !!itemList[i].completed;
    style = "display: none;"
    item_string = ITEM_TAG.replace('ITEM_NAME', itemName);
    item_string = item_string.replace('ITEM_ID', itemId);
    item_string = item_string.replace('IS_COMPLETED', (isCompleted) ? style : '');
    item_string = item_string.replace('IS_NOT_COMPLETED', (!isCompleted) ? style : '');
    finalHtml.push(item_string);
  }

As you can see, this is the same loop we used to generate our HTML before, just with a minor refactor. We've pulled all of the relevant data attributes from the object at the top of the loop, and we use the completed flag to dynamically assign an empty display style to the appropriate button, effectively hiding the affected buttons. The logic for this is in the ternary operators used in replacing IS_COMPLETED and IS_NOT_COMPLETED - the button gets the correct display attributes based upon the value of isCompleted as it relates to the individual object. Another notable item is that we have had to change our replace functionality. As we are now replacing multiple instances of the same string with every .replace() call, we need to change these methods to operate using a regular expression applied globally to the string. To do so, change the .replace calls in the for loop above with these:

    item_string = ITEM_TAG.replace(/ITEM_NAME/g, itemName);
    item_string = item_string.replace(/ITEM_ID/g, itemId);
    item_string = item_string.replace(/IS_COMPLETED/g, (isCompleted) ? style : '');
    item_string = item_string.replace(/IS_NOT_COMPLETED/g, (!isCompleted) ? style : '');

Next, we need to call the update method from the SDK. Modify the dataService object to include the following function:

var dataService = {
  update: function(id, data) {
    return backand.object.update("items", id, data);
  }
};

Similar to the delete method, this is just a passthrough to the SDK that returns the promise to be resolved.

Now that we've added our UI and our deletion method, we can use a technique similar to what we did with deletion to register both the "complete" and "reopen" handlers:

  $("body").on("click",".complete-button",function(event) {
    complete_icon_id = event.target.id;
    entry_id = complete_icon_id.split('-')[1];
    newData = {};
    newData.completed = true;
    myDataService.update(entry_id, newData).then(function(){myDataService.getData();});
    // Return false to stop propogation
    return false;
  });

  $("body").on("click",".uncomplete-button",function(event) {
    uncomplete_icon_id = event.target.id;
    entry_id = uncomplete_icon_id.split('-')[1];
    newData = {};
    newData.completed = false;
    myDataService.update(entry_id, newData).then(function(){myDataService.getData();});
    // Return false to stop propogation
    return false;
  });

We can now see the individual completion status for each entry in your to-do list app:

Screen Shot 2017-04-12 at 4.48.05 PM.png


Conclusion and Next Steps

With that, the basic functionality of our to-do list application is created. We have augmented our simple to-do list app to allow for creating new items, deleting items, and marking items completed. At this point, we can go in any direction we like - we have the full power of a data model at our app's beck and call, and can build out whatever we wish. In the next article in the series, we'll do some cleanup of the code, add some more dynamic behavior, and we'll look at integrating user authentication into the app.

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:
serverless frameworks ,jquery ,backand ,javascript ,web dev

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}