Skip to main content

The Conversion: Trips for Keeps - Part 3

With the core methodology set up in Part 2 to get the trip data from the AngularJS front end to the CF middle tier, we are ready to save to the database. All of our custom data is stored in Oracle, but the following will apply to most, if not all, relational databases. I try to steer away from doing anything overly fancy to minimize refactoring down the road if platforms should change.

At the moment, the trip function only receives the trip data and breaks it down into a CF structure and then returns that structure back to the application.
 <cffunction name="saveTrip" returnformat="json" returntype="struct" access="remote">  
   <cfset _params = getHttpRequestData()>  
   <cfset _content = deserializeJSON(_params.content)>  
   <cfset _trip = deserializeJSON(_content.objectData)>  
   <cfreturn _trip>  
 </cffunction>  
Now I have a design decision to make. I can either create individual functions to save each aspect of the trip such as the trip details, the sponsors, waivers, participants, etc. or throw everything into one, likely very long, function. Hmmm...

I think for maintenance purposes it would be wiser to break each save element out. This would reciprocate the idea that the getTrip() function will have various functions to get all the elements of a trip. So let's start by creating a function saveTripDetails(). There will be two sections of this function: one to insert and one to update. Technically, I could create a PL/SQL stored procedure and handle both those scenarios there. Since it's the same amount of code, I'm going with the more direct route and do the database operations in Coldfusion. In my view, the fewer layers of code and number of code bases to maintain, the better. That said, if my boss came along and said "You must use PL/SQL for database operations.", I'd be okay with that.

As I began writing the saveTripDetails() function, I realized that trip_id was not included as a returned property in getTrip(). The trip_id will be used to determine of an insert or an update is needed. If the trip_id is 0, an insert will be executed; otherwise an update will be performed.

[...The Next Day...]

So after much testing and debugging, especially for a new trip, we finally have code that works. The saveTrip() function remains small with only one additional line added.
 <cffunction name="saveTrip" returnformat="json" returntype="struct" access="remote">  
   <cfset _params = getHttpRequestData()>  
   <cfset _content = deserializeJSON(_params.content)>  
   <cfset _trip = deserializeJSON(_content.objectData)>  
   <cfset _trip = saveTripDetails(_trip)>  
   <cfreturn _trip>  
 </cffunction>  
The new function, saveTripDetails(), is doing all the work.
 <cffunction name="saveTripDetails" returntype="struct">  
   <cfargument name="_tripDetails" type="struct" required="yes">  
   <cfif _tripDetails.id gt 0>  
     <cfquery datasource="#_ds#">  
       update trips  
       set   trip_name = <cfqueryparam value="#_tripDetails.name#" cfsqltype="cf_sql_varchar">,  
             author_pidm = <cfqueryparam value="#_tripDetails.authorID#" cfsqltype="cf_sql_integer">,  
             destination = <cfqueryparam value="#_tripDetails.destination#" cfsqltype="cf_sql_varchar">,  
             purpose = <cfqueryparam value="#_tripDetails.purpose#" cfsqltype="cf_sql_varchar">,  
             trip_type = <cfqueryparam value="#_tripDetails.type#" cfsqltype="cf_sql_varchar">,  
             comments = <cfqueryparam value="#_tripDetails.comments#" cfsqltype="cf_sql_varchar">,  
             last_update = sysdate,  
             last_update_pidm = <cfqueryparam value="#session.intUserPIDM#" cfsqltype="cf_sql_integer">  
       where trip_id = <cfqueryparam value="#_tripDetails.id#" cfsqltype="cf_sql_integer">  
     </cfquery>  
   </cfif>  
   <cfif _tripDetails.id is 0>  
     <cfquery name="_id" datasource="#_ds#">  
       select trips_seq.nextval as nextId from dual  
     </cfquery>  
     <cfloop query="_id">  
       <cfset _tripDetails.id = nextId>  
     </cfloop>  
     <cfif _tripDetails.id gt 0>  
       <cfquery datasource="#_ds#">  
         insert  
         into  trips (  
             trip_id,  
             author_pidm,  
             trip_name,  
             destination,  
             purpose,  
             trip_type,  
             comments,  
             last_update,  
             last_update_pidm  
         )  
         values (  
             <cfqueryparam value="#_tripDetails.id#" cfsqltype="cf_sql_integer">,  
             <cfqueryparam value="#session.userid#" cfsqltype="cf_sql_integer">,  
             <cfqueryparam value="#_tripDetails.name#" cfsqltype="cf_sql_varchar">,  
             <cfqueryparam value="#_tripDetails.destination#" cfsqltype="cf_sql_varchar">,  
             <cfqueryparam value="#_tripDetails.purpose#" cfsqltype="cf_sql_varchar">,  
             <cfqueryparam value="#_tripDetails.type#" cfsqltype="cf_sql_varchar">,  
             <cfqueryparam value="#_tripDetails.comments#" cfsqltype="cf_sql_varchar">,  
             sysdate,  
             <cfqueryparam value="#session.userid#" cfsqltype="cf_sql_integer">  
         )  
       </cfquery>  
     </cfif>  
   </cfif>  
   <cfreturn _tripDetails>  
 </cffunction>  
There is nothing really notable here as it's pretty standard stuff. The function will check to see if a trip id greater than zero was passed in. If so, then an update is made to the trips table. If the id is zero, the we get a new id by getting the next value in the sequence: select trips_seq.nextval as nextId from dual, assigning that value to the id and executing an insert into the trips table.

The function ends by returning the trip details back to saveTrip().

As I work through the application, I will be adding validation and error handling.  But for now, the groundwork has been laid. The trip.js controller has been modified to handle a parameter of zero for the trip id.
 angular.module('core').controller('TripController',['$scope','dataService','$stateParams','$state', function($scope,dataService,$stateParams,$state){  
   $scope.trip = {};  
   if ($stateParams.id == 0){  
     dataService.call('createNewTrip').then(function(data){  
       $scope.trip = data.data;  
     })  
   }  
   if ($stateParams.id > 0){  
     dataService.call('getTrip&_id=' + $stateParams.id).then(function(data){  
       $scope.trip = data.data;  
     })  
   }  
   dataService.call('getTripTypes').then(function(data){  
     $scope.tripTypes = data.data;  
   })  
   $scope.saveTrip = function(_trip){  
     dataService.process('saveTrip',_trip).then(function(data){  
       $scope.trip = data.data;  
       $state.go('trip',{id: $scope.trip.ID})  
     });  
   }  
 }]);  
I do want to point out the following line after the trip has been saved $state.go('trip',{id: $scope.trip.ID}). What this does is reload the page by going to the trip route and passing in the trip id. When a new trip is saved, it's route ended with a 0. So in order to reestablish the route, I am reloading the page with the new trip id to force the new route. By having the page essentially start over, we can ensure that all the ducks are in a row and immediately notice if there are any data concerns.

Next up is taking a second first look at the trip.html template as well as adding a new trip button on the landing page.

Thanks for reading!  Comments and feedback are always welcome.

Cheers!
[Updated Line Counts]

Comments

Popular posts from this blog

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 r...

The Conversion: Lay of the Landing Page - Part 1

In the last post  the groundwork has been laid for real development to begin. The data service, router, and landing pages have been created and are ready for code. The last thing to add for this part of the process is the Coldfusion component. A data folder has been added to store any cfcs relevant to the TRIPS application. The first is to write the CFFUNCTION that will return the list of trips. My development pattern for creating CF functions is fairly routine.  First, decide on a name, parameters and return type.  Sometimes the naming of a function is the hardest part.  I've settled on actionDataObject.  So in this case, the function will be named: getTrips.    At moment, the landing controller (landing.js) is empty. angular.module('core').controller('LandingController',['$scope','dataService', function($scope,dataService){ }]); The basic task of this controller is getting the list of trips.  First is the establishment of an empty tr...

The Conversion: Getting Started

So enough about me and my employer from the first two blog posts.  It's time to get some real work started.  This project is about converting the Adobe Flex based TRIPS application to and AngularJS front end.  Now, I have done some work in Angular 2.x (Typescript), but that was a separate deployment into a different (but similar) framework.  Because of the time frame I have to convert this application and it's overall complexity, I am going to stick with AngularJS. Before I get into details, just a quick note about the application framework.  In order to make the management applications easier, I built a small portal based on the concept of users, roles and functions.  All employees can be users of the system provided they accept the FERPA agreement. Access to functionality depends on the roles the user has and which functions are assigned that role. For example, our faculty users have access to class lists and course information via their FACULTY role. ...