Skip to main content

The Conversion: I see UI/X - Part 1

For as much development and blogging that's been happening, I haven't moved the project too far along. In fact, I am behind on my goal of being completed by the end of August.  That said, the new TRIPS application is almost at the top of the development hill - much of the ground work has been developed and the more I work on it, the clearer the vision becomes.

This blog post will revolve around the user interface and experience for the landing and trip templates. Starting with the landing page, a New Trip button needs to be added which routes to the trip template with an id of 0. Remember, a 0 id will inform the saveTrip() code to insert a trip as opposed to updating it.
The new trip button isn't something the user is going to use all the time, but it still needs to be obvious. I initially thought the right side of the filter would be a good place, but the filtering capability may be expanded in the near future with options. So I will start with putting the button on the left side of the page under the instructional text.
 <div style="padding-bottom:10px">  
   <a ui-sref="trip({id: 0})">  
     <button class="btn btn-primary">  
       <i class="fa fa-lg fa-plus-square" aria-hidden="true"></i>  
       Create New Trip  
     </button>  
   </a>  
 </div>  
Because the route has already been set up and the trip.js controller is set load an existing or new trip, there isn't much to do. The key is ui-sref="trip({id: 0})" which is simply routing to the /trip route and passing a trip id of 0.

Next, we will switch gears over to the trip.html template and trip.js controller. At the moment, the data from the trip loads directly onto the page for editing. However, it most cases, this information isn't going to be updated once it's set, so there is really no reason to allow editing as a default state...unless of course it's a new trip, in which case we would want the fields to be immediately available for editing.

To achieve this, I've added $scope.editTrip boolean to the trip.js controller with it's default value set to false. If the trip id is 0, a new trip, editTrip is set to true. On the template, I've wrapped the form controls for editing in a div using the ng-if directive: ng-if="editTrip". If editTrip is true, the contents of the div will be included in the DOM. Conversely, if it's false, the block will not be included in the DOM. So now the default trip.html template went from this:
...to this:
Clicking on the edit icon on the right of the page will yield the editing page.
The Save Trip button will call the saveTrip() function in the controller. Cancel Save will call cancelSave().

 <div ng-controller="TripController as ctrlTrip">  
   <h2>{{trip.NAME}} <a class="pull-right" ng-click="setEditTrip(true)"><i class="fa fa-lg fa-pencil-square-o" aria-hidden="true"></i></a><br><small>{{trip.DESTINATION}}</small></h2>  
   <h4>{{trip.TYPE}}</h4>  
   <div ng-if="!editTrip">  
     <div class="list-group">  
       <div class="list-group-item"><i>Purpose</i>: {{trip.PURPOSE}}</div>  
       <div class="list-group-item"><i>Stops and Comments</i>: {{trip.COMMENTS}}</div>  
     </div>  
   </div>  
   <div ng-if="editTrip">  
     <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">Purpose</span>  
       <textarea class="form-control" ng-model="trip.PURPOSE" style="width:400px"></textarea>  
     </div>  
     <div class="input-group" style="padding-bottom:10px">  
       <span class="input-group-addon" style="width:125px">Stops and<br>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-selected="trip.TYPE == tripType.CODE" 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>  
     <button class="btn btn-danger" ng-click="cancelSave()">Cancel Save</button>  
   </div>  
 </div>  

 angular.module('core').controller('TripController',['$scope','dataService','$stateParams','$state', function($scope,dataService,$stateParams,$state){  
   $scope.trip = {};  
   $scope.editTrip = false;  
   if ($stateParams.id == 0){  
     dataService.call('createNewTrip').then(function(data){  
       $scope.trip = data.data;  
       $scope.editTrip = true;  
       console.log($scope.trip)  
     })  
   }  
   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.setEditTrip = function(_b){  
     $scope.editTrip = _b;  
   }  
   $scope.saveTrip = function(_trip){  
     dataService.process('saveTrip',_trip).then(function(data){  
       $scope.trip = data.data;  
       $scope.setEditTrip(false);  
       $state.reload('trip',{id: $scope.trip.ID})  
     });  
   }  
   $scope.cancelSave = function(){  
     $state.reload('trip',{id: $scope.trip.ID})  
   }  
 }]);  
From a development perspective, there is nothing really tricky happening here. We've added the editTrip boolean value along with a $scope.setEditTrip() function to change the value. Also added is the cancelSave() function which will just reload the route.

The rest of this page will likely be academic now that the styling and navigation paradigm are figured out. Part 2 of this post will deal filling in the rest of the template with sponsors, date and times, waivers, and participants. Stay tuned!

Cheers!

Updated Line Counts

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

Dynamically Loading an AngularJS UI.Router

When starting out on my AngularJS journey, I couldn't get a good handle on the router native to the framework. So I adopted the use of the wonderful angular-ui / ui-router.  During the past few years of development, I've honed in (for better or worse) my paradigm for setting new applications and nearly every AngularJS app has a routes.js file. Without going into background, I wanted a way to load the ui-router dynamically. Typically, the routes gets defined in a .js file and typically looks something like this: angular.module('core').config(function($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/landing/201820"); $stateProvider .state('landing', {url: "/landing/:term", templateUrl: "apps/noted/templates/landing.html"}) .state('uploadCalendar', {url: "/uploadCalendar", templateUrl: "apps/noted/templates/uploadCalendar.html"}) .state('noteTakers...