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