Purpose.

  • The following information on this page is intended to meet the learning standards and outcomes for IUPUI NEWM-N Classes 220 & 320.

 
 

NEWM-N 220.

1. An HTML page, and style it via CSS (Lines 9 - 11 in KryptoApp/www/index.html).

 
 <!-- compiled css output -->
        <link href="css/ionic.app.css" rel="stylesheet">
        <link href="css/style.css" rel="stylesheet">
  • Links Ionic CSS (Line 10 in KryptoApp/www/index.html)

  • Links Customs App CSS (Line 11 in KryptoApp/www/index.html)

  • CSS used to style following pages:

    • KryptoApp/www/templates/details.html

    • KryptoApp/www/templates/settings.html

    • KryptoApp/www/templates/tab-docs.html

    • KryptoApp/www/templates/tab-home.html

    • KryptoApp/www/templates/tab-leads.html

    • KryptoApp/www/templates/tab-products.html

    • KryptoApp/www/templates/tab-videos.html


 

2. Link in an external style sheet to the page (Lines 9 - 11 in KryptoApp/www/index.html). See question one for more details.

 
 <!-- compiled css output -->
        <link href="css/ionic.app.css" rel="stylesheet">
        <link href="css/style.css" rel="stylesheet">

 

3. Create an external Javascript file and add it to an HTML page (Lines 19 - 31 in KryptoApp/www/index.html).

 
<script src="js/firebase.js"></script>
<script src="js/angularfire.min.js"></script>
<script src="js/product.service.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/settings.controller.js"></script>
  • The above JavaScript files are the ones containing the primary logic for the app. Various other JavaScript files are lined to add additional features and Cordova functions.


 

4. Deconstruct a written problem statement into a series of computer instructions.

 

Problem Statement:

  • We want to create an app that dynamically scales to content. Meaning every time we want to add a new image, product, or video we do not have to modify the source code.

Computer Instructions:

  • For each image, product, or video, in a database create a image card and display the item.


5. Write the Javascript that aligns with those instructions.

  • Loops - The videos, docs, and products tab us ng-repeat to loop through all content (Line 36 - 61 in KryptoApp/www/templates/tab-videos.html)
<!-- For each video in storage, column size is set to 50% (Loop !START!)-->
<div ng-repeat="v in videos" class="col col-50 light-content">
  
  <!-- Video card !START! -->
  <div class="card">
    
    <!-- Title of the card !START! -->
    <div class="item center item-background-color">
      <!-- Remove .mp4 file extension from name then display it -->
      <h2>{{v.name.replace('.mp4',"")}}</h2>
    </div>
    <!-- Title of the card !END! -->
    
    <!-- Video item !START! -->
    <div class="item item-image">
      <video poster="img/NewYorkFAHMini-thumb.jpg" class="krypt-video" controls>
        <source ng-src="{{v.nativeURL}}" type="video/mp4">
      </video>
    </div>
    <!-- Video item !END! -->
    
  </div>
  <!-- Video card !END! -->
            
</div>
<!-- Loop !END! -->
  • Conditionals - Checking the condition of image to see if it is in an array of images or not (Lines 22 - 36 in KryptoApp/www/js/controllers.js).
$scope.image = function(images) {
  
  if(images.constructor===Array) {
    $scope.SaveImage = images[0];
  } else {
    $scope.SaveImage = images;
  }
  $scope.imagePath = $scope.SaveImage.split('/');
  $scope.targetPath = cordova.file.externalRootDirectory + '/KryptoApp/images/' + $scope.imagePath[$scope.imagePath.length - 1];
  
  return $scope.targetPath;
}
  • Variable Manipulation - Taking data from a JSON file then replacing extra characters and spacing to create clean usable data (Lines 24 - 37 in KryptoApp/www/js/product.services.js)
//Read products text file
$cordovaFile.readAsText(GetJSONFile, 'products2.txt')
  .then(function (data) {
  var myJSONString = JSON.stringify(data);
  var myEscapedJSONString = myJSONString.replace(/\\n/g"\\n")
                                      .replace(/\\'/g"\\'")
                                      .replace(/\\"/g'\\"')
                                      .replace(/\\&/g"\\&")
                                      .replace(/\\r/g"\\r")
                                      .replace(/\\t/g"\\t")
                                      .replace(/\\b/g"\\b")
                                      .replace(/\\f/g"\\f");

  var jsondata = JSON.parse(myEscapedJSONString);
  • Object Construction & Modification - Using Firebase I create a new connection for both the leads database and Google Authentication. Based on the authentication I can create a new session for the app showing all of the apps content or hiding it if the incorrect credentials are supplied (Lines 151 - 212 in KryptoApp/www/js/controllers.js).
//FireBase Factory for Auth !START!
.factory("Auth", function ($firebaseAuth) {
  
  //Set variable to new FireBase URL
  var usersRef = new Firebase("https://kryptoniteapppoc.firebaseio.com/leads");
  
  //Returns FireBase Auth data with auth url
  return $firebaseAuth(usersRef);
})
//FireBase Factory for Auth !END!

//FireBase Factory for Leads !START!
  .factory("Leads", function ($firebaseArray) {
  
  //Set variable to new FireBase URL
  var leadsRef = new Firebase("https://kryptoniteapppoc.firebaseio.com/leads");
  
  //Returns FireBase Leads data with url
  return $firebaseArray(leadsRef);
})
//FireBase Factory for Leads !END!

//Controller used for Google Kryptonite FireBase Account, also shows and hides tabes based on correct login !START!
  .controller("FireBaseAuth", function($scope, Auth, $ionicTabsDelegate) {
  
  //Hides tabs by defult until successful auth has taken place
  $ionicTabsDelegate.showBar(false);
  
  //Google Auth login function
  $scope.login = function () {
    
    //In App Browser Google Auth popup -> send data to authData
    Auth.$authWithOAuthPopup("google").then(function(authData){
      
      //Setting AuthData scope
      $scope.authData=authData;
      //console.log("User " + authData.uid + " is logged in with " + authData.provider); //Used to debug issues with FireBase Rules
      
      //If Google User ID is equal to Kryptonite FireBase account then procced as normal
      if(authData.uid == '9br1pBkktlXAmG62Qt2sMfvwmLS2') {
        
        //Show navigation tabs due to successful and correct Google auth.
        $ionicTabsDelegate.showBar(true);
      }
      //Else alert user of wrong login, remove FireBase session and reload the page for re-attempt.
      else {
        
        //Alerting user of wrong login.
        alert('Please sign in with the correct Google Account');
        
        //Remove Google User FireBase session
        window.localStorage.removeItem("firebase:session::<host-name>");
        
        //Reload page to start process over
        window.location.reload(true);
        
      }
    }
                                            
    )};
})
//Controller used for Google Kryptonite FireBase Account, also shows and hides tabes based on correct login !END!
  • Mathematical Instructions - I was not able to find a great solution for this in the Ionc App I created a quick page with an example in Jquery (Lines43 - 73 in index.html Found on my GitHub)
function multi() {
   var myBox1 = document.getElementById('box1').value;
   var myBox2 = document.getElementById('box2').value;
   var result = document.getElementById('result');
   var myResult = parseInt(myBox1) * parseInt(myBox2);
   result.value = myResult;
 }
function divide() {
  var myBox1 = document.getElementById('box1').value;
  var myBox2 = document.getElementById('box2').value;
  var result = document.getElementById('result');
  var myResult = parseInt(myBox1) / parseInt(myBox2);
  result.value = myResult;
}
function add() {
  var myBox1 = document.getElementById('box1').value;
  var myBox2 = document.getElementById('box2').value;
  var result = document.getElementById('result');
  var myResult = parseInt(myBox1) + parseInt(myBox2);
  result.value = myResult;
}
function subtract() {
  var myBox1 = document.getElementById('box1').value;
  var myBox2 = document.getElementById('box2').value;
  var result = document.getElementById('result');
  var myResult = parseInt(myBox1) - parseInt(myBox2);
  result.value = myResult;
}

6. Modify the DOM to provide feedback to the user and create new elements on the page at runtime - Besides a few alerts in the Ionic app there was not a good example. I created a quick example using Jquery. Pressing the button alerts the user that they can click on the child button to change the color (Full example here).

function addButtons() {
  $("ul").append("<li><button id='babyButton'> Baby Button </button></li><br />");
  alert("Press a baby button to change the baby button(s)")
}

$(document).on('click', '#babyButton',function() {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  $(this).css("background-color", color);
});

7.  Use Javascript to adjust the css properties of one or more objects on the page in reaction to user input (mouse clicks) - Demonstrated this above using an OnClick function to change the color of child buttons.


8. Demonstrate the use of Javascript input events, along with using an agnostic event handler that uses event.target in its response - Demonstrated in the main button to create child buttons, then allowing the child buttons to be recolored, example above.


NEWM-N 320.

1. Creating a front end application that loads in data (via AJAX) that controls how it functions. - The Ionic App access data from a great deal of sources. The main concern was that the sales representative would not have access to Wi-Fi. We made the Kryptonite App have offline capabilities by downloading (Syncing) all of the content to the device and then serving this content in the various tabs such as products, videos, and documents (Lines 5 -- 40 in KryptoApp/www/js/controllers.js).

.controller('ProductCtrl', function ($scope, ProductFactory, $state) {

  //Used for testing
  /*        $scope.products = ProductFactory.ReadMock();*/
  
  //Production script
  ProductFactory.Read().then(function (data) {
    
    $scope.products = data;
    /*alert($scope.products[0].ImageURL[0]);*/
    
  });
  
  $scope.changePage = function(id){
    $state.go('details',{id: id});
  }
  
  $scope.image = function(images) {
    
    if(images.constructor===Array) {
      
      $scope.SaveImage = images[0];
    } else {
      
      $scope.SaveImage = images;
    }
    
    $scope.imagePath = $scope.SaveImage.split('/');
    $scope.targetPath = cordova.file.externalRootDirectory + '/KryptoApp/images/' + $scope.imagePath[$scope.imagePath.length - 1];
    
    return $scope.targetPath;
  }
  
  $scope.productsLib = { account: 'kryptoappdev', container: 'kryptoappdev-products', library: 'products' };
  
})

2. Given an array of objects, show that you can sort and filter that data to find objects that match a specific need for the user (for example, all red cars or a car with a singular ID) - The Ionic app stores the JSON data into an array, allow us to call individual indexes or all of the data. Being since the Ionic app did not have any sort of filter of content I created a quick demonstration using JQuery as well. (Full example here)

var imagesOfCars = ['http://i.imgur.com/O1Ae4mV.jpg', 'http://i.imgur.com/72a10bS.png', 'http://i.imgur.com/xxkfNay.png', 'http://i.imgur.com/72a10bS.png', 'http://i.imgur.com/72a10bS.png', 'http://i.imgur.com/O1Ae4mV.jpg', 'http://i.imgur.com/xxkfNay.png', 'http://i.imgur.com/O1Ae4mV.jpg']
        
$(document).ready(function() {
  var $div = $("#carImgs");
  
  $.each(imagesOfCars, function(i, val) {
    $("<img />").attr("height", "100px").attr("src", val).appendTo($div);
    
  });
});

function filterRed() {
  
  var $div = $("#carImgs");
  
  $("#carImgs").empty();
  $.each(imagesOfCars, function(i, val) {
    if(val == 'http://i.imgur.com/72a10bS.png'){
      $("<img />").attr("height", "100px").attr("src", val).appendTo($div);
    }
    else {
      
    }
    
  });
}

function filterBlue() {
  
  var $div = $("#carImgs");
  
  $("#carImgs").empty();
  $.each(imagesOfCars, function(i, val) {
    if(val == 'http://i.imgur.com/O1Ae4mV.jpg'){
      $("<img />").attr("height", "100px").attr("src", val).appendTo($div);
    }
    else {
      
    }
    
  });
}

function filterYellow() {
            
  var $div = $("#carImgs");
  
  $("#carImgs").empty();
  $.each(imagesOfCars, function(i, val) {
    if(val == 'http://i.imgur.com/xxkfNay.png'){
      $("<img />").attr("height", "100px").attr("src", val).appendTo($div);
    }
    else {
      
    }
    
  });
}

function showCars() {
  var $div = $("#carImgs");
  
  $("#carImgs").empty();
  $.each(imagesOfCars, function(i, val) {
    $("<img />").attr("height", "100px").attr("src", val).appendTo($div);
    
  });
        }

3. Break a large scale application down into smaller, self-contained components that accomplish one task of the application, and communicate with the other components at runtime - Due to this Ionic app needing to work with a large amount of systems we broke the development into chunks. After doing so I was able to focus on a specific piece of the puzzle and then join these pieces together. See the flow chart below for more details.


4. Design and diagram connections between components, given a problem statement - To show how the Ionic app works with various other systems I created this data flow chart to make it easier for our clients to understand where all of this data was coming from and being generated.


5. Use templates to design placeholder elements that will be filled in with data, and repeated on a screen when data has been loaded - Being since we have a great deal of products that are being displayed within the app I created a template page that allows the user to look at each individual product with much more detailed information (Found in KryptoApp/www/templates/details.html). 

<!--
                            Product details page
    #############################################################
    The purpose of this page is to show all details of the selected
    product. The product selected originates from the products tab.
    #############################################################

-->

<!-- Product details view !START! -->
<ion-view ng-controller="DetailsCtrl">

    <!-- Create back button !START! -->
    <ion-nav-buttons side="left">
        <a href="#/tab/home">
            <div class="custom-spacing-top">
                <a href="" ng-click="goBack()" class="button button-icon ion-ios-arrow-back">
                    Back
                </a>
            </div>
        </a>
    </ion-nav-buttons>
    <!-- Creat back button !END! -->

    <!-- Content container !START! & sets background color -->
    <ion-content class="padding kryptonite-background">
        
        <!-- Content container for product details !START! -->
        <!-- The inline style allows the cards to warp down to what looks like a new row, without this style the columns go on forever in one row -->
        <div class="row" style="flex-wrap:wrap">
            
            <!-- Product card !START! -->
            <div class="card" style="width:100%">
                
                <!-- Set product card title !START! -->
                <div class="center item item-background-color">
                    <!-- Select product name from Salsify json -->
                    <h1> {{selectedProduct['Product Name_Title']}}</h1>
                </div>
                <!-- Set product cart title !END! -->
                
                <!-- Display product image !START! -->
                <div class="item item-image" style="border:none">
                    <!-- Select image name from Salsify json file (this file is in local storage) -->
                    <img src="{{image(selectedProduct.ImageURL)}}" style="max-width: 400px">
                </div>
                <!-- Display product image !END! -->

                <!-- Container for features !START! -->
                <div>
                    <h2 class="center">Main Features </h2>
                        <ul class="list" style="width:85%; margin-left: 7%"> 
                            <li class="item">{{selectedProduct.Feature1}}</li>
                            <li class="item">{{selectedProduct.Feature2}}</li>
                            <li class="item">{{selectedProduct.Feature3}}</li>
                            <li class="item">{{selectedProduct.Feature4}}</li>
                            <li class="item">{{selectedProduct.Feature5}}</li>
                            <li class="item">{{selectedProduct.Feature6}}</li>
                            <li class="item">{{selectedProduct.Feature7}}</li>
                        </ul>
                    <h2 class="center">Additonal Features</h2> 
                        <ul class="list" style="width:85%; margin-left: 7%">
                            <li class="item">{{selectedProduct.MiscSpec}}</li>
                        </ul>
                </div>
                <!-- Container for features !END! -->

                <!-- Container for back button !START! -->
                <div class="row center" style="width:50%; margin-left: 25%">
                    <a href="#/tab/products" class="col button button-block button-energized icon ion-android-arrow-back"> <strong> Back</strong></a>
                </div>
                <!-- Container for back button !END! -->

            </div>
            <!-- Product card !END! -->

        </div>
        <!-- Container for product details !END! -->

    </ion-content>
    <!-- Content container !END! -->

</ion-view>
<!-- Product details page view !END! -->