Making a live edit contact list with Coldbox REST & Vue.js

Similar documents
Front-End UI: Bootstrap

Working Bootstrap Contact form with PHP and AJAX

AngularJS. CRUD Application example with AngularJS and Rails 4. Slides By: Jonathan McCarthy

AngularJS. CRUD Application example with AngularJS and Rails 4. Slides By: Jonathan McCarthy

Quick.JS Documentation

Session 5. Web Page Generation. Reading & Reference

Client Side JavaScript and AJAX

This project will use an API from to retrieve a list of movie posters to display on screen.

web-sockets-homework Directions

Using Visual Studio 2017

Manual Html A Href Onclick Submit Button

Project Part 2 (of 2) - Movie Poster And Actor! - Lookup

UI Course HTML: (Html, CSS, JavaScript, JQuery, Bootstrap, AngularJS) Introduction. The World Wide Web (WWW) and history of HTML

Manual Html A Href Onclick Submit Form

django-rest-framework-datatables Documentation

Ten good practices for ASP.NET MVC applications

AngularJS AN INTRODUCTION. Introduction to the AngularJS framework

Dingle Coderdojo 6. Project Part 2 (of 2) - Movie Poster And Actor! - Lookup. Week 6

Responsive Web Design and Bootstrap MIS Konstantin Bauman. Department of MIS Fox School of Business Temple University

Static Webpage Development

Introduction to AngularJS

Produced by. Agile Software Development. Eamonn de Leastar

Front End Programming

Mobile Web Applications. Gary Dubuque IT Research Architect Department of Revenue

Introduction to Computer Science Web Development

Kendo UI Builder by Progress : Using Kendo UI Designer

ColdBox Platform 4.0 AND BEYOND

Lecture 7. Action View, Bootstrap & Deploying 1 / 40

LAMPIRAN. Index.php. <?php. unset($_session["status"]); //session_destroy(); //session_destroy();

FatModel Quick Start Guide

Paythru Remote Fields

THE HITCHHIKERS GUIDE TO. Harper Maddox CTO, EdgeTheory 30 September 2014

High Performance Single Page Application with Vue.js

AngularJS Intro Homework

THE LAUNCHER. Patcher, updater, launcher for Unity. Documentation file. - assetstore.unity.com/publishers/19358

Understanding Angular Directives By Jeffry Houser

Akumina Digital Workplace

By the end of this Angular 6 tutorial, you'll learn by building a real world example application:

Installation Guide. Sitecore Federated Experience Manager. Installation & Configuration Guide

Human-Computer Interaction Design

Stamp Builder. Documentation. v1.0.0

Simple AngularJS thanks to Best Practices

UX/UI Controller Component

Description: This feature will enable user to send messages from website to phone number.

Markup Language. Made up of elements Elements create a document tree

Unifer Documentation. Release V1.0. Matthew S

Drag and Drop Form Builder. Data Verity #2 Erikka Baker James Miller Jordan Schmerge

Lab 1 - Introduction to Angular

We are assuming you have node installed!

For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to

Advantages: simple, quick to get started, perfect for simple forms, don t need to know how form model objects work

JavaScript Performance

The core of Tapestry's form support is the Form component. The Form component encloses (wraps around) all the other field components such

Nagaraju Bende

Using Development Tools to Examine Webpages

Single Page Applications using AngularJS

BlueMix Hands-On Workshop Lab A - Building and Deploying BlueMix Applications

Spring Data JPA, Spring Boot, Oracle, AngulerJS 적용게시판실습 게시판리스트보기.

P a g e 1. Danish Technological Institute. Scripting and Web Languages Online Course k Scripting and Web Languages

User manual Scilab Cloud API

Jquery Manually Set Checkbox Checked Or Not

Learning Objectives. Description. Your AU Expert(s) Trent Earley Behlen Mfg. Co. Shane Wemhoff Behlen Mfg. Co.

SEEM4570 System Design and Implementation. Lecture 3 Events

PlantVisorPRO Plant supervision

Comprehensive AngularJS Programming (5 Days)

About the Tutorial. Audience. Prerequisites. Copyright & Disclaimer. Laravel

JQUERYUI - WIDGET FACTORY

jmaki Overview Sang Shin Java Technology Architect Sun Microsystems, Inc.

AngularJS Examples pdf

CSE 115. Introduction to Computer Science I

Web API Lab folder 07_webApi : webapi.jsp your testapijs.html testapijq.html that works functionally the same as the page testapidomjs.

Frontend Frameworks. SWE 432, Fall 2016 Design and Implementation of Software for the Web

Modern and Responsive Mobile-enabled Web Applications

Integrated Dashboard Design

HTML 5 Form Processing

Connecting Angular and CFML

SportsStore: Administration

User manual Scilab Cloud API

Reagent. a ClojureScript interface to React. React Amsterdam Meetup 12 Feb. 2015

CSC 615 FINAL EXAM SINGLE PAGE APPS. 1. Introduction

The starter app has a menu + 2 Views : Dashboard. About

RPG Web Apps with AJAX, JSON, and jquery. Lab Examples

SAMPLE CHAPTER IN ACTION. Joachim Haagen Skeie MANNING

Where are my Lucee and Adobe ColdFusion log files on CommandBox servers?

Overview... 4 JavaScript Charting and Metric Insights... 5

REST. Web-based APIs

Want to read more? You can buy this book at oreilly.com in print and ebook format. Buy 2 books, get the 3rd FREE!

Just Mock It! Leveraging Mock Objects

This course is designed for web developers that want to learn HTML5, CSS3, JavaScript and jquery.

Event : Common Europe Speaker : Koen Decorte CD-Invest nv

Serverless Single Page Web Apps, Part Four. CSCI 5828: Foundations of Software Engineering Lecture 24 11/10/2016

Django AdminLTE 2 Documentation

Tutorials Php Y Jquery Mysql Database Without Refreshing Code

The course also includes an overview of some of the most popular frameworks that you will most likely encounter in your real work environments.

Chapter6: Bootstrap 3. Asst.Prof.Dr. Supakit Nootyaskool Information Technology, KMITL

CSS (Cascading Style Sheets)

Roxen Content Provider

PHP for PL/SQL Developers. Lewis Cunningham JP Morgan Chase

Mobile Web Development


Transcription:

Tweet Making a live edit contact list with Coldbox REST & Vue.js Scott Steinbeck Mar 28, 2016 Today we will be making a contact database that you can quickly and easily manage using ColdBox and Vue.js. We will be using bootstrap in our project to make it the UI look a little better but it is completely optional if you want to use this in your own project. For this project I will be using CommandBox to generate all my files. TL;DR: View the repo here Lets Begin. Step 1: You can skip this step if you already have a project set up. From CommandBox run: coldbox create app name=cbvue skeleton=rest --installcoldbox This will give us a minimal project with a handlers\basehandler.cfc (needed to make our life easy when creating a REST API) and an handlers\echo.cfc which is an example usage to get you started. Now that we have our project started we need to tweak a few things.

First, since this is a template that is expecting to be setup for REST only, the Echo.cfc is set to be the default entry point. Since we want to create a view that accesses a REST API we need to point that to a view. Step 2: Create you default Layout & View. coldbox create layout Main This is going to create our default layout. A layout is the outer template that you content will go inside. The layout will be created in layouts/main.cfm. Navigate to that folder and replace the content with this: <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <meta name="viewport" content="width=device-width, initialscale=1"> <title>coldbox & Vue.js</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.mi n.css"> <script type="text/javascript" src="http://cdn.jsdelivr.net/vue/1.0.18/vue.min.js"></script> <script type="text/javascript" src="https://rawgit.com/vuejs/vueresource/master/dist/vue-resource.min.js"></script> </head> <body> <main> <cfoutput>#renderview()#</cfoutput> </main> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js "></script> <script

src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min. js"></script> </body> </html> This contain some boilerplate code to get our page setup and includes all of our libraries that will be needed. Again, the only libraries needed for this project are vue.js & vue-resource, everything else is just for UI. coldbox create view main\index --helper This will create 2 files: view/main/index.cfm, view/main/indexhelper.cfm. index.cfm will contain the HTML table and form we will be interacting with. indexhelper.cfm (a file automatically included when we load index.cfm) will contain all of our code for vue.js. The helper file is a connivence provided by ColdBox to help organize your code into separate files. Now before we go too far, we need to change the default entry point to be our new index so that we will see the correct view. Navigate to config/coldbox.cfc. In our settings we are going to change: defaultevent = "Main.index", From this point is is time to start your server start --rewritesenable --force As a side note if your on CommandBox latest stable version you can also create a server.json file in the root of your project which will enable you to store all of your settings so when you want to start your server up in CommandBox you just type:

start I have included my server.json in the repo if you want to take a look. Step 3. Creating the REST API We need a place to store our data so we will use a model, since we have been using CommandBox to generate our files we will continue to do so: coldbox create model name=contactservice properties=contacts,lastid -- accessors This will create 2 files: models/contactservices.cfc, tests/specs/unit/contactservicestest.cfc. Navigate to our models/contactservices.cfc, file and add this inside your init function: variables.contacts = { "1" : {"id": 1, "firstname": "Scott", "lastname": "Steinbeck", "phone": "661-555-5555", "email": "scottsteinbeck@email.com" "2" : {"id": 2, "firstname": "Scott", "lastname": "Coldwell", "phone": "661-555-5555", "email": "scottcoldwell@email.com" "3" : {"id": 3, "firstname": "Jon", "lastname": "Clausen", "phone": "661-555-5555", "email": "johnclausen@email.com" "4" : {"id": 4, "firstname": "Eric", "lastname": "Peterson", "phone": "661-555-5555", "email": "ericpeterson@email.com" "5" : {"id": 5, "firstname": "Andrew", "lastname": "Dixon", "phone": "661-555-5555", "email": "andrewdixon@email.com" "6" : {"id": 6, "firstname": "Gavin", "lastname": "Pickin", "phone": "661-555-5555", "email": "gavinpickin@email.com" "7" : {"id": 7, "firstname": "Brad", "lastname": "Wood", "phone": "661-555-5555", "email": "bradwood@email.com" "8" : {"id": 8, "firstname": "Luis", "lastname": "Majano", "phone": "661-555-5555", "email": "luismajano@email.com" "9" : {"id": 9, "firstname": "Jorge", "lastname": "Reyes", "phone": "661-555-5555", "email": "jorgereyes@email.com" ;

variables.lastid = 10; This is just some default table rows to get us started seeing some data. Instead of adding a database into the mix i thought it would be easier to persist this in the singleton scope to make it simpler to walk though. This just makes it persist across refreshes and starts us at an increment number when we create new items. In case you are new to ColdBox you may be unfamiliar with the SINGLETON scope, it is one of the many scopes provided by WireBox to persist your data. For more detailed information check out: Wirebox Scopes BTW: This model can easily be modified to use a database instead, and the best part is you can do it without ever making any changes to the front end :) Now that our data is created lets create some helper methods for accessing & changing our data. Below your init function paste the following code: /** * Get all contacts */ struct function getall(){ return variables.contacts; /** * save and return all contacts * @contactid The id to save/update * @data The data record */ struct function save( required contactid, required data ){ // insert, move lastid if( arguments.contactid == 0 ){ arguments.contactid = variables.lastid; arguments.data.id = variables.lastid; variables.lastid++; // Store data variables.contacts[ arguments.contactid ] = arguments.data;

return variables.contacts; /** * Remove a contact * @contactid The incoming ID */ struct function remove( required contactid ){ structdelete( variables.contacts, arguments.contactid ); return variables.contacts; The methods should be pretty self explanatory, they allow us to keep the logic of data manipulation in the model so when we access it we can just simply call getall(), save(), and remove(). This is extremely useful because all of your data logic is separated from the controller and in one place. Since we already had a handler created handlers/echo.cfc I decided to rename this handler to Contacts.cfc and changed its contents. Open up the file once you change the name and copy in the content below: /** * My RESTFul Contact Handler */ component extends="basehandler"{ // Dependency Injection property name="contactservice" inject="contactservice"; This is the base code that will extend our BaseHandler.cfc so we get all our RESTful helpers. Additionally, we inject our ContactService.cfc into our handler to use the helper methods we defined earlier. The first API action is view which will list all of the contacts we have in our data: /** * List All Contacts

*/ any function view( event, rc, prc ){ prc.response.setdata( contactservice.getall() ); This will grab the data from the session and send it back as JSON Our next action is save which, as it suggests, will save an existing row or add a new one. /** * Save A Contact */ any function save( event, rc, prc ){ var requestbody = deserializejson( tostring( gethttprequestdata().content ) ); var scontacts = contactservice.save( requestbody.id, requestbody ); prc.response.setdata( scontacts ); You will notice something weird here event.gethttpcontent( json=true );, normally you would grab the data from the rc scope, but the vue-resource sends the data through the headers instead of the FORM or URL scope so we have to grab it from there. Once we have the data posted we send the contact data to the ContactService so we can check if it has an existing id, if it is 0 we know its a new record, so we will create the new record, otherwise we will just update the existing data, then we send back the new data. Lastly we will make it so you can delete a contact: /** * Delete An Existing Contact */ any function remove( event, rc, prc ){

var scontacts = contactservice.remove( rc.contactid ); prc.response.setdata( scontacts ); Now that our handler is done we need to add our routes for this handler so that we can map our functions to RESTful actions. Open up your config/routes.cfm file and add this line right above the route that says addroute(pattern=":handler/:action?"); -- the order is important. addroute(pattern = 'contacts/:contactid?',handler = 'contacts',action = {GET = 'view', POST = 'save', PUT = 'save', DELETE = 'remove'); Ok, now that we have our RESTful functions ready, we can start working on our front end. Step 4. Creating our view Open up your views/main/index.cfm that you created earlier. Inside you will copy in the html below <div id="app" class="container"> <div class="row"> <div class="col-sm-4"> <div class="panel panel-default"> <div class="panel-heading"> <strong>add/edit Contact</strong> </div> <div class="panel-body"> <div> <div class="form-group"><input v-model="contactitem.firstname" class="form-control" value="" placeholder="first Name"></div> <div class="form-group"><input v-model="contactitem.lastname" class="form-control" value="" placeholder="last Name"></div> <div class="form-group"><input v-model="contactitem.phone" class="form-control" value="" placeholder="phone"></div> <div class="form-group"><input v-model="contactitem.email" class="form-control" value="" placeholder="email"></div>

<button class="btn btn-primary" @click="savecontact()">submit</button> <button class="btn btn-warning" @click="cancelsave()">cancel</button> </div> </div> </div> </div> <div class="col-sm-8"> <table class="table"> <thead> <tr> <th>first Name</th> <th>last Name</th> <th>phone</th> <th>email</th> <th></th> </tr> </thead> <tbody> <tr v-for="contact in contacts"> <td>{{contact.firstname</td> <td>{{contact.lastname</td> <td>{{contact.phone</td> <td>{{contact.email</td> <td><button @click="loadcontact(contact)" type="button" class="btn btn-primary">edit</button></td> <td><button @click="deletecontact(contact)" type="button" class="btn btn-danger">x</button></td> </tr> </tbody> </table> </div> </div> </div> This creates the html for our Contact Table & Contact Editor. You will notice some odd looking code if you are not familar with Vue.js or Angular JS: v-model="contactitem.firstname"

v-for="contact in contacts" @click="savecontact()" {{contact.email This is how Vue.js communicates with your code.. v-model="contactitem.firstname" v-model, which is part of the form, binds together the form field and the Vue.js controller so they are aware of eachother. v-for="contact in contacts" v-for, is how Vue.js loops through a list of data, this can be used for any type of list. @click="savecontact()" @click, is basically equivalent to the onclick method, accept, this way the even is registered with the Vue.js controller so it can do something when you click it. {{contact.email {{contact.email is known as handlebar syntax. This is how you define variables so Vue.js knows to replace them with that contact email for instance. Step 4. Continued The Vue.js Controller We will copy the following code into the views/main/indexhelper.cfm file. This file gets appended after our views/main/index.cfm. <script>

$( document ).ready(function() { new Vue({ // Where the app will be instantiated on the page el: '#app', // Variables that you want it to have access to or start out with data: { contactitem: { id:0, firstname:'', lastname:'', phone:'', email:'' contacts: [] // When this module is ready run this ready: function() { this.loadcontacts(); // All the methods you want the view to have access to, basically an object of functions methods: { loadcontacts: function() { var _this = this; // Get the list of contacts this.$http.get('/contacts').then(function(result) { // Update the list with the new data _this.$set('contacts', result.data.data); ); loadcontact: function(contact) { // Set the form with the contact row information this.contactitem = contact; savecontact: function() { var _this = this; // Save the new contact information

this.$http.post('contacts', _this.contactitem).then(function(result) { // Reset the form to detault _this.contactitem = {id:0, firstname:'', lastname:'', phone:'', email:''; // Update the list with the new data return _this.$set('contacts', result.data.data); ); cancelsave: function(){ // Reset the form to detault return this.contactitem = {id:0, firstname:'', lastname:'', phone:'', email:''; deletecontact: function(contact) { var _this = this; //Delete the contact this.$http.delete('/contacts/' + contact.id).then(function(result) { // Update the list with the new data _this.$set('contacts', result.data.data); ); ); ); </script> Lastly we have the controller. This is where all the magic happens... el: '#app', Tells Vue that all of our logic will be nested within the <div id="app"></div> data: { contactitem: { id:0,

firstname:'', lastname:'', phone:'', email:'' contacts: [] data is a struct of information it is pulling from, here we are providing defaults but you can also populate the data statically and it will use that information. // When this module is ready run this ready: function() { this.loadcontacts(); loadcontacts: function() { var _this = this; // Get the list of contacts this.$http.get('/contacts').then(function(result) { // Update the list with the new data _this.$set('contacts', result.data.data); ); ready is fired once the Vue Component is loaded. What we are doing here is calling the ajax function that will load our table with data from our ColdBox RESTful API. Gotcha** - Vue only knows about nested data changes if you use its built in functions (.$set,.$delete) otherwise you will be scratching your head for a while

_this.$set('contacts', result.data.data); Load Contact - will load in our contact data into the form for editing. Notice the this.contactitem is being set to contact. loadcontact: function(contact) { // Set the form with the contact row information this.contactitem = contact; Here the entire row of data is being sent as an argument when you click on the Edit button. <td><button @click="loadcontact(contact)" type="button" class="btn btn-primary">edit</button></td> So we are setting the this.contactitem = contact; Now in our code we have the form set up with: v-model="contactitem.firstname" which means as soon as the this.contactitem has updated data it is going to update the v-model that is connected to that data. Once the data is populated in the form we can make edits to it and then click either the submit or the cancel button. The respective code is below.

savecontact: function() { var _this = this; // Save the new contact information this.$http.post('contacts', _this.contactitem).then(function(result) { // Reset the form to detault _this.contactitem = {id:0, firstname:'', lastname:'', phone:'', email:''; // Update the list with the new data return _this.$set('contacts', result.data.data); ); cancelsave: function(){ // Reset the form to detault return this.contactitem = {id:0, firstname:'', lastname:'', phone:'', email:''; Since the default data sends an id = 0 we can decide if we need to create a new item or edit an existing item. Last on our list for the Vue Controller is the Delete. Basically here we are just sending back the id to our API with the delete verb and our API will take care of deleting the entry and returning our new data set back to us. deletecontact: function(contact) { var _this = this; //Delete the contact this.$http.delete('/contacts/' + contact.id).then(function(result) { // Update the list with the new data _this.$set('contacts', result.data.data); ); So thats it. All you need is a few files (Most of which can be generated by CommandBox) and you have got yourself a fully functional contacts manager.

The Code: View the repo here www.ortussolutions.com Copyright Ortus Solutions, Corp.