Skip to main content

The Conversion: Trips for Keeps - Part 1

With the development of the landing page, we are ready to add the trip details page. This page will be used to edit the trip information, manage sponsors, activities, and waivers. Due to the amount of data displayed on the page, I think that the participants listing will get their own page.

We've already, in the last post, added the trip route to the router.  But just as a refresher, it looks like the following:
 angular.module('core').config(function($stateProvider, $urlRouterProvider) {  
   $urlRouterProvider.otherwise("/trips/landing");  
   $stateProvider  
     .state('landing', {url: "/trips/landing", templateUrl: "apps/trips/templates/landing.html"})  
     .state('trip', {url: "/trips/trip/:id", templateUrl: "apps/trips/templates/trip.html"})  
 });  
The next steps are to create the trip template and controller files.  Because a parameter is being passed to the trip controller, the $stateParams need to be injected.  What does that mean?  Well, it's a complicated but short way of saying that we have to let the controller know what it can use.  And we do this by adding to the object array and function parameters just we did with the dataService.
 angular.module('core').controller('TripController',['$scope','dataService','$stateParams', function($scope,dataService,$stateParams){  
   $scope.trip = {};  
   dataService.call('getTrip&_id=' + $stateParams.id).then(function(data){  
     $scope.trip = data.data;  
   })  
 }]);  
The $stateParams stores the parameters passed into the route. In this case, we need the id of the trip being selected by the user.  The id will be passed to the getTrip function via the dataService. The next step is to create the CF function.
 <cffunction name="getTrip" returnformat="json" returntype="struct" access="remote">  
   <cfargument name="_id" required="yes" type="string">  
   <cfset _trip = structNew()>  
   <cfquery name="_qTrip" datasource="#_ds#">  
     select trip_name,  
         author_pidm,  
         destination,  
         trip_type,  
         purpose,  
         comments  
     from trips t  
     where  t.trip_id = <cfqueryparam value="#_id#" cfsqltype="cf_sql_integer">  
   </cfquery>  
   <cfloop query="_qTrip">  
     <cfset _trip.name = trip_name>  
     <cfset _trip.authorID = author_pidm>  
     <cfset _trip.destination = destination>  
     <cfset _trip.type = trip_type>  
     <cfset _trip.comments = comments>  
     <cfset _trip.purpose = purpose>  
   </cfloop>  
   <cfreturn _trip>  
 </cffunction>  
This is the beginning of the trip function as there will be a lot more data elements to gather. But in my development pattern, I like to test things out before writing too much code. In this round of testing and after a bunch of "why isn't this working?!?!" I discovered I had forgotten to add the trip.js controller to the main.cfm page.

So now it's time to begin building out the trip.html template. The editing paradigm that I've been messing around with of late is simply get the JSON object from the CF, edit the object via the model directly on the template, and then pass the object back to CF.  Rinse and repeat.
 <div ng-controller="TripController as ctrlTrip">  
   <h2>{{trip.NAME}}<br><small>{{trip.DESTINATION}}</h2>  
   <div class="input-group" style="padding-bottom:10px">  
     <span class="input-group-addon" style="width:125px">Name</span>  
     <input class="form-control" ng-model="trip.NAME" style="width:400px">  
   </div>  
   <div class="input-group" style="padding-bottom:10px">  
     <span class="input-group-addon" style="width:125px">Destination</span>  
     <textarea class="form-control" ng-model="trip.DESTINATION" style="width:400px"></textarea>  
   </div>  
   <div class="input-group" style="padding-bottom:10px">  
     <span class="input-group-addon" style="width:125px">Comments</span>  
     <textarea class="form-control" ng-model="trip.COMMENTS" style="width:400px"></textarea>  
   </div>  
   <div class="input-group" style="padding-bottom:10px">  
     <span class="input-group-addon" style="width:125px">Trip Type</span>  
     <select class="form-control" style="width:300px" ng-model="trip.TYPE">  
       <option ng-repeat="tripType in tripTypes" value="{{tripType.CODE}}">{{tripType.NAME}}</option>  
     </select>  
   </div>  
   <button class="btn btn-primary" ng-click="saveTrip(trip)">Save Trip</button>  
 </div>  
With a very basic template in place, which will be expanded to accommodate the other aspects of a trip, we can begin written the save code, both in the dataService and the CFC. Since there are going so many many values that need to be saved, it doesn't make sense to pass those values via the url string. Doing so would required breaking down the object into strings, etc.

We are going to pass the trip object to the dataService, and then the dataService is going to pass the object to Coldfusion. The first thing to do is create the dataService method to accept the object. Until now, I have always made a new method for each object that needs processing to happen. However, as I was sitting here looking at the code, I realized I could abstract this just a little further and make it a bit more generic, like the this.call() method.

So I have changed this:
   //params template  
   this.saveTrip = function(_trip){  
     var _params = {trip: JSON.stringify(_trip)}  
     return $http({  
       method: 'POST',  
       url: 'trips.cfc?method=saveTrip',  
       headers: {  
         'Content-Type': undefined  
       },  
       data: _params  
     }).then(function successCallback(response){  
         console.log(response.data);  
         return response;  
       },  
       function errorCallback(response){  
         console.log('ruh-roh')  
       }  
     );  
   }  
to this:
   this.process = function(_method, _data){  
     var _params = {objectData: JSON.stringify(_data)}  
     return $http({  
       method: 'POST',  
       url: 'trips.cfc?method=' + _method,  
       headers: {  
         'Content-Type': undefined  
       },  
       data: _params  
     }).then(function successCallback(response){  
         console.log(response.data);  
         return response;  
       },  
       function errorCallback(response){  
         console.log('ruh-roh')  
       }  
     );  
   }  
Notice the subtle differences.  Rather than doing a this.saveTrip and passing the trip object, we are going to process something, this.process, via some method, _method.  In both cases, the object gets stringified into the _params variable. Next we are going to post to the trips.cfc. For the this.saveTrip we explicitly state the saveTrip method as part of the url.  But for the this.process method, we put the _method value into the url. With this simple level of abstraction, we are going to save a lot of time coding, debugging and maintaining.

Now technically, we could abstract this.process() even further and allow arguments for the CFC and path. But to me this is over-abstraction - if that's a thing. I think it's important to maintain context toward the application when developing. Just like a database can be normalized to the point of being a bunch of tables with columns and columns of numbers, code can be abstracted to the point that it requires layers and layers of coding to get Hello World on the screen. Abstraction is a good thing because it yields us libraries like AngularJS. But too much of a good thing can be unproductive.

Ok, rant over - back to the code. Just one final contrast for this post concerning the dataService call.

dataService.saveTrip(_trip).then(function(data){});
dataService.process('saveTrip',_trip).then(function(data){});

Both make sense and are easy to understand. However, the latter one is going to save quite a bit of development time.

Hmmm...I hadn't expected this post to be as long as it is. So a Part 2 will be forthcoming and focus on the data coming into Coldfusion and saving it back to the database.

Cheers!

Comments

Popular posts from this blog

The Conversion: MAIN.CFM

Within the current development pattern I have for our portal system, the main.cfm is the starting point for an application. For the most part, this is really boilerplate and serves to get the application off the ground. The way the portal works is that the user lands on the main menu page and provide with a menu of options. Each option is called a function and is assigned a unique id. When the user clicks on a menu option, the page is reloaded with the function id. The process checks to see if the user is actually allowed to have access to the function. It then accordingly looks up the path and displays the page via a . In the case of the TRIPS application, the included file is the main.cfm page for the application. So far the project looks like this: we have the folder structure defined with no files aside from the main.cfm. The main.cfm contains a few javascript includes, the overarching controller for the application as well as the route viewer. The next blog post will foc

The Conversion: Trips for Keeps - Part 2

Ack! A few days off work and then updates to other applications that needed to be made has delayed more work on TRIPS. But we're back and ready to continue. In the last post , we started to build out the trip.html template and it's corresponding controller. The dataService was updated with an abstraction that would allow a CFC method to be called and data to be passed.  This post will focus on retrieving that data from the dataService to the CFC method. Before we get started, I thought it would be kind of cool to keep a count of the number of lines of code for the project.  I'm not sure if it will provide any insight, but it might be nice to compare the line count to the number of lines in the Flex application.  Rather than posting the counts on each blog post, I have created a separate post tracking the line count per post . In review, let's take a look at the dataService code that's going to call the Coldfusion function to save the trip information. this.p

The Conversion: Introduction

While working as the senior level developer for a small liberal arts college, I've had the luxury of deciding the technology to be used internally for custom applications. When I first started, the college was using Coldfusion 7, HTML, and many MS Access databases. Over the course of the past 11 years, we've moved to Oracle, are on CF11, and utilizing AngularJS / Angular for our front ends.  Coldfusion is still doing the heavy lifting in interfacing with Oracle. During those 11 years, we also found ourselves developing Adobe (now Apache) Flex applications.  Flex was a great tool - easy to develop in and fairly easy to deploy when Flash was ubiquitous. But Flex was not great on mobile platforms.  Hence our move to Angular due to the idea that we wanted to give our users a rich web experience. So over the course of the past two years, we've been either retiring Flex applications altogether due to the direction that some departments have taken (read outside vendor); or ret