Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Monday, May 22, 2017

Updating Your Angular Yeoman Repo to ES6 NPM and Webpack

I inherited a project that was quickly put together as a proof of concept/demo that was created using the Yeoman Angular-generator.  While this was the preferred way of setting up projects a few years ago,  front end technologies and patterns have moved on.  Having recently come from a project that abandoned bower, gulp, grunt, and moved to ES6, JSPM, npm, and node scripts I knew I wanted to develop using ES6 and more importantly use a module loader.



Since I was starting the project fresh, I decided that removing grunt and bower would be a good way of understanding the codebase,  a good chance to learn Webpack, and finally to make sure that we were using the best practices in our front end code.  In the end the transition was fairly painless and made the code more in line with current practices.  

I've decided to do this with a website that I developed using the yeomen generator to show you the basic steps of converting such a project.  The website is live here: http://andy-meyers.appspot.com/#/, the yeoman generated repo is here https://github.com/thatguyandy27/Portfolio/tree/angular-yeoman, and the ES6ified repo is here: https://github.com/thatguyandy27/Portfolio/tree/es2016.  

So let's get started.  

1. Install Webpack and Other Loaders


The first thing we are going to do is add what we need to get webpack up and running using npm scripts.  Each one of these can be added using `npm install --save-dev <packagename>`.  Here are a list of the packages and their uses.  You may need more or less depending on your repo, like unit for unit testing, but this is the core stuff to get started.  


2. Create New Config Files


Next, we want to add a webpack.config.js file at our root directory.  Yours may need to be modified for testing, path directories, and other project specific requirements.   

We will also need to add a .babelrc file in our root that we will just populate with `{  "presets":["es2015"] }`.


3. Update Your Bower Dependencies to NPM Dependencies.  


This should be pretty straight forward.  For each bower package you should be able to add an npm package using `npm install --save <packagename>`.    After you are done we can delete the bower.json file and the bower dependencies folder. 


4. Create A Top Module File For the App 


Next we need an entry point for our application that will import your top level dependencies.  Ideally each component would import ONLY the dependencies that they require, but to get up and running quickly we can create a bootstrap file to import everything.  We will also want to create module files for each module in your application.  My application only has one module portfolioApp, but if yours has more you will do this for each module and then import those modules as dependencies to your top level module.

So my app-module.js file looks like this:

 
import angular from 'angular';
import 'angular-animate';
import 'angular-cookies';
import 'angular-resource';
import 'angular-route';
import 'angular-sanitize';
import 'angular-touch';
import appTemplateConfig from './app-templates.js';

const module = angular
  .module('portfolioApp', [
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch'
  ]).run(appTemplateConfig);

export default module;


So a quick review of what is going on here.  First, I am importing the angular npm packages dependencies using the es6 syntax.  We then create our module and export it to be used later to register our controllers, services, components, etc.  I am also importing an appTemplateConfig function that will be run on load of the application.  We will get into this more later.


5.  Change JS Files to Import & Export


We now need to update our components to remove the dependencies on angular.  Ideally we would break the app into components, but I will leave that for a later exercise.  So for now I will have a controller-module.js,  service-module.js, and view-module.js.  All of our components will be imported here and registered as angular controllers, views, etc.  We also then need to update the component files to export the component to be registered.  Here is an example of one:

import THREE from 'three/three.js';
const ThreeJSPlanetCtrl = function($scope, planetService){
    ...
};
ThreeJSPlanetCtrl.$inject = ['$scope', 'planetService'];
export default ThreeJSPlanetCtrl;

The important thing here is that I didn't have to change most of the code. I now import any non-angular dependencies using the import syntax and use it as normal.  Finally at the end of the controller I use `$inject` syntax instead using something like ng-annotate.  We finally then export the function to be registered later on our module that we previously created.   Here is a controller-module.js that I created that will be used to register all of my controllers.

import '../libraries/OrbitControls.js';
import AboutCtrl from './about.js';
import BubblePopperCtrl from './bubblePopper.js';
import CanvasChessCtrl from './canvasChess.js';
import CssSolarSystemCtrl from './csssolarsystem.js';
import RelationshipEditorGOTCtrl from './relationshipEditorGOT.js';
import RelationshipMapGOTCtrl from './relationshipMapGOT.js';
import ThreeJSBubbleCtrl from './threejsBubbles.js';
import ThreeJSDemoCtrl from './threejsDemo.js';
import ThreeJSPlanetCtrl from './threejsPlanet.js';
import ThreeJSSolarSystemCtrl from './threejsSolarSystem.js';
import TransparentImageDemoCtrl from './transparentImageDemo.js';
import appModule from '../../app-module.js';

appModule.controller('threejsPlanetCtrl', ThreeJSPlanetCtrl)
    .controller('threeJsSolarSystemCtrl', ThreeJSSolarSystemCtrl)
    .controller('transparentImageDemoCtrl', TransparentImageDemoCtrl)
    .controller('AboutCtrl', AboutCtrl)
    .controller('bubblePopperCtrl', BubblePopperCtrl)
    .controller('CanvasChessCtrl', CanvasChessCtrl)
    .controller('CssSolarSystemCtrl', CssSolarSystemCtrl)
    .controller('relationshipEditorGOTCtrl', RelationshipEditorGOTCtrl)
    .controller('relationshipMapGOTCtrl', RelationshipMapGOTCtrl)
    .controller('threeJsBubbleCtrl', ThreeJSBubbleCtrl)
    .controller('threeJsDemoCtrl', ThreeJSDemoCtrl); 

This file imports the appModule that we defined in step #4 and then register every controller we have as an angular controller.  We will perform this step as many times as necessary depending on how your app is structured.

6. Create A Template Cache File


This is more of an optional step, so you could still use the native angular templating, but I have grown accustomed to importing templates as strings and attaching them to their components.  This is an intermediate step to that.  We are going to create the `app-templates.js` that was mentioned in step #4.  

import mainTemplate from './views/main.html';
import aboutTemplate from './views/about.html';
import bubblePopperTemplate from './views/bubblePopper.html';
import canvasChessTemplate from './views/canvasChess.html';
import cssSolarSystemTemplate from './views/cssSolarSystem.html';
import relationshipEditorGOTTemplate from './views/relationshipEditorGOT.html';
import relationshipMapGOTTemplate from './views/relationshipMapGOT.html';
import threejsBubblesTemplate from './views/threejsBubbles.html';
import threejsDemoTemplate from './views/threejsDemo.html';
import threejsPlanetTemplate from './views/threejsPlanet.html';
import threeJsSolarSystemTemplate from './views/threeJsSolarSystem.html';
import transparentImageDemoTemplate from './views/transparentImageDemo.html';


function templateConfig($templateCache){
  $templateCache.put('views/about.html', aboutTemplate);
  $templateCache.put('views/main.html', mainTemplate);
  
  $templateCache.put('views/bubblePopper.html', bubblePopperTemplate);
  $templateCache.put('views/canvasChess.html', canvasChessTemplate);
  $templateCache.put('views/cssSolarSystem.html', cssSolarSystemTemplate);
  $templateCache.put('views/relationshipEditorGOT.html', relationshipEditorGOTTemplate);
  $templateCache.put('views/relationshipMapGOT.html', relationshipMapGOTTemplate);
  $templateCache.put('views/threejsBubbles.html', threejsBubblesTemplate);
  $templateCache.put('views/threejsDemo.html', threejsDemoTemplate);
  $templateCache.put('views/threejsPlanet.html', threejsPlanetTemplate);
  $templateCache.put('views/threeJsSolarSystem.html', threeJsSolarSystemTemplate);
  $templateCache.put('views/transparentImageDemo.html', transparentImageDemoTemplate);
}

templateConfig.$inject = ['$templateCache'];

export default templateConfig;

All we are doing in this file is creating a function that has $templateCache injected into it and importing those templates to those paths that we are already using in the app.  We then export the function to be run in when the app loads.  This moves the importing of the templates from runtime to build time as the strings will be in the file already.


7. Create The App Entry File.  


In my webpack.config I have defined an entry point to the application that will bootstrap the entire thing.  Here is the snippet from the config. 

  config.entry = isTest ? {} : {
    ['app']: './app/app-main.js'
  };

This tells the app to use this file to bootstrap everything.  It will pull in the top level dependencies which will then pull in their dependencies all the way down the tree.

This file is pretty simple.  Here is what it looks like:

import './styles/about.scss';
import './styles/cssSolarSystem.scss';
import './styles/main.scss';
import './styles/relationshipEditorGOT.scss';
import './styles/relationshipMapGOT.scss';
import './styles/threejsPlanet.scss';
import './styles/transparentImageDemo.scss';


import './scripts/app.js';
import './scripts/services/service-module.js';
import './scripts/controllers/controller-module.js';
import './scripts/root.js';
import './scripts/libraries/OrbitControls.js';


It is just an import of the css styles and the top level application files.  If you have things that need to be run in order for some reason you can change that order here as well.  


8. Create Some NPM Scripts


We are going to add some basic npm scripts that can be run in place of grunt, gulp, etc.  These will all live in your package.js and can be run using npm run <command>.  Here are the scripts that I've added:

"scripts": {
    "build": "rimraf dist && webpack --bail --progress --profile",
    "server": "webpack-dev-server  -d  --history-api-fallback --inline --progress --port 9000",
    "test": "karma start test/karma.conf.js",
    "start": "npm run server"
}

We now have the following commands:

  • build: removes the dist directory and then tells webpack to build
  • server: runs webpack locally in dev mode
  • test: runs the karma tests
  • start: shorthand for server
You may need to add other commands as your setup may be different.  The important thing to note is you can use these commands to execute others.  So if your process has 4 steps you can have 4 indvidual commands and then one to call them in order.


9. Enjoy


Finally enjoy using your es6 syntax and start componentizing.


Friday, September 23, 2016

React Item Templates

With the future of Angular 1.x development coming to a head, our team started looking at other front end technologies that could potentially live side by side with angular 1.x and eventually (a long time down the road) replace our angular 1.x app completely.  We considered a few frameworks and ended up settling on React.js.

Shamelessly stolen picture from work HipChat room. 

One of the problems I recently worked on required a multi selectable list of items.  The catch was that I wanted to allow the template of the item to customizable while allowing for most of the functionality to be the same.  I realized that you could pass in a function as a property, and wondered why not a react component class.  It is essentially a function.  I tried it out and it ended up working.  Here is a JSFiddle with using that idea:




So in the container class, CustomList, I accept an optional parameter called itemTemplate.  It should be a function.  I then set a variable called ItemTemplate to be either the itemTemplate passed in or the DefaultRow class.  The key thing here to note is that you must make your ItemTemplate variable name capitalized.  React has a convention that you custom elements need to be capitalized.  I can then just pass whatever properties to each item instantiation and each one can format however they like.  Super Easy!

Monday, September 21, 2015

Learning ECMA Script 6: Arrow Functions

If you are familiar with a language other than JavaScript, there is a good chance that language already supports something similar to => functions.  In ES6 they are called arrow functions (or FAT arrow functions) and 9 times out of 10 they replace anonymous functions.  There is one major case where they differ, and I will get to that in my last point.




The first way to use an arrow function is the same any way you would use function (param1, param2, ...) { ... }.  Instead of that you would write the following:

   
(param1, param2, ...) => { ... }; 

It is pretty straight forward and it is shown in the example below:

var closureVariable = 'foo';
var test1 = () => {
    var innerVariable = 'bar';
    // look ma!  Closure still works.
    return closureVariable + innerVariable;
};

document.getElementById('test1').innerHTML = 'Test 1: ' + test1();


As you can see, no magic there.  It is simply replacing function (param...) with (param...) =>.  Most places you have anonymous functions are in callbacks and promises.  While it was super weird at first using them I found my code to be easier to read now (especially jasmine unit tests) and rarely write the word function anymore.

Now if you have a function that is one line and returns something, you can make the arrow function one line.  Imagine you were using the lodash function map that will "map" your current array into another one.  You just need to supply the function to transform each object.  Imagine you just wanted a property out of that array like name.  Before you would need something like this:

    
_.map(list, function(item){ return item.name;});


Now you can write this:

    
_.map(list, item => item.name);


item.name is implicitly returned since it is not surrounded with curly braces.



So you might ask, that's fine, but what if I want to return an object?  I need curly braces for that!  The answer is, Yes you do!  So to do this you would wrap your object in parenthesis and the above will still work.

So you would write the following:

    
_.map(list, (item) => ({ id: item.id, name: item.name}));





There one major difference between anonymous functions and arrow functions is how this is defined in the function.  Arrow functions now have Lexical this. 

What does this mean?  The easiest way would be to show you the difference in these two examples:

Suppose I have the following object.
  
var solarSystem = {
        planets: ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto'],
        star: 'Sun'
    };


Now I want to add some function to that object that does something to the array, and references some other property on the object.

    solarSystem.performOperation = function(){
        this.message = '';
        this.planets.forEach(function(p, i) {
            this.message += p + ' is planet number ' + (i + 1) + ' from the ' + this.star + '.  ';
        });
    };


What happens if you run this code? You will get the following:  

solarSystem.message === ''; //true

Why? This is because in the forEach function 'this' is defined as the window object. So the window object will have a message property on it that contains the following:  

window.message = 'undefinedMercury is planet number 1 from the undefined. Venus is planet number 2 from the undefined. Earth is planet number 3 from the undefined. Mars is planet number 4 from the undefined. Jupiter is planet number 5 from the undefined. Saturn is planet number 6 from the undefined. Uranus is planet number 7 from the undefined. Neptune is planet number 8 from the undefined. Pluto is planet number 9 from the undefined. ' 

 
However if you write the same code using arrow functions like in the fiddle below you will get the desired result.


Cheers!

Part 1 - Math & Numbers
Part 2 - New Parameter Features 

Tuesday, July 28, 2015

Learning ECMA Script 6: New Parameter Features

ES6 has a few new features when it comes to working with parameters.  To get these examples working, you most likely will have to use Firefox.

Pluto Hearts ES 6 Features

Default Parameters

Allowing for default parameters is a new feature being added into ES6, and are really simple to use.  When defining a function & specifying the parameters just add an equal sign and the value and you want to default it to and BAM! DONE!  So you would write your function like this: 
function defaultParam(foo = "bar"){};   
Now if the first parameter passed for foo is undefined then it will default to "bar".  However if it is null then it will be passed in as null!  Note that you can actually explicitly send in undefined it WILL use the default value.  You can also call functions to get the default value for a parameter, but be aware that the function will NOT be called unless undefined is passed in for that parameter.  

Rest Operator


The rest operator “…”is used when you define a function and works similarly to something like ‘varargs’ in Java or the ‘params’ in c#.  If placed before the last parameter in a function definition it turns it into an array and places all arguments that aren’t accounted for into the array.  You would write your function like this:
function restParam(foo, ...bar ){};  // bar is now an array



Spread Operator

The spread operator "..." is the same as the rest operator syntax wise, but used differently.  While the rest operator brings parameters together, the spread operator blasts arrays apart.  So the spread operator can be used to “spread” and array into individual arguments for a function call, or it can be used to “spread” an array out for use in another array definition.  It would be used like this:
function spreadParam(foo, bar){}; spreadParam(...["foo","bar"]);
One thing to note is that also works on strings as well.




Cheers!

Friday, July 10, 2015

Learning ECMA Script 6: Math and Number Features

At work we are currently preparing setting up our environment using tools such as babel to allow us to work with the newest JavaScript features without worrying about browser support.  So I am going to start taking a look at some of these new features and hopefully start incorporating them into my daily repertoire.

In this post I am going to take a look at the Number and Math features which are pretty easy, and mostly already implemented by modern browsers.


While Math can be scary, using these new features don't have to be.  We will start with the Number features and then move into the Math ones.

Numbers:

1. Support for octal and binary notation.  You can now specify numbers as octal (0o###), or binary (0b###);   So 8 (0o10) + 2 (0b10) + 10 + 16 (0x10) = 36



2. Support for octal and binary strings.  Since octal and binary formats are now supported you can pass those formats into the Number() function and it will convert it into an integer.  In the fiddle below the variables are converted to numbers using the Number function and we obtain the same result.


3. Number.EPSILON is a new constant that represents the difference between the smallest possible number greater than 1 and 1.  So essentially the smallest possible number that can exist greater than zero.  It represents 2.2204460492503130808472633361816E-16.  From what I can tell it is most useful in testing equality of decimals.  So instead of doing .3 - .2 - .1 === 0 (false) you can do Math.abs(.3 - .2 - .1) <= Number.EPSILON


4. Number.isInteger is a new function that lets you know if a number being passed in is an integer.  If it is not an integer or not a number then it will return false.


5. Number.isSafeInteger is similar to the function above, but also checks to see if the number is "safe".  Due to the way that numbers are represented in JS, there are max and minimum numbers that are "safe", and any numbers beyond that are potentially off.  What do I mean by "off"?  Essentially two or more integers beyond the max or minimum are represented by the same JavaScript integer.  Check out the third line in the fiddle below for wtf I am talking about.


6. Math.sign will return -1, 1, 0 (or -0....), or NaN based on the value passed in. 

7. Math.trunc will turn the number into an integer which will essentially round down if greater than 0 or round up if less than 0.



8. There are new trig methods such as Math.sinh, Math.cosh, Math.tanh, Math.asinh, Math.acosh, Math.atanh.  There is also a new Math.hypot function that calculates the square root of the sum of the objects. 

9.  There are also some new exponential and root functions available.  Math.cbrt returns the cube root of the number passed in.  Math.expm1 and Math.logp1 are similar to the Math.exp and Math.log, but from what I can tell by reading online they are more accurate when it comes to numbers close to 1. 

I believe that covers most if not all of the major changes in ECMA Script 6.  If I forgot something let me know and I will make sure to update the post.

Cheers!

Thursday, June 25, 2015

Creating ES6 JSfiddles with Babel

One of the things I’ve wanted to start to start learning is what is new in ES6.  There are a lot of new features in ES6 that are pretty awesome.  There are some that can even be implemented today using other libraries or polyfills such as promises.  However, for other features it can be harder to implement.  

The good news is there are libraries out there that will compile your ES6 script into backwards compatible JS.  The one I am looking into is Babel  https://babeljs.io.  Babel would typically be set up in your development/build process like any other compiler, but this one would theoretically fade away and leave you with ES6 scripts when browsers catch up to it.  

So to get started playing around with ES6 scripts, Babel has provided a live editor here: https://babeljs.io/repl/ which is nice.  However that doesn’t let you save anything, or show it off in a blog, etc.  So I went to see if JS fiddle has any out of the box support for Babel, and found it doesn’t.  I knew that Babel had a client side compiler (which shouldn’t obviously be used for production), so I tried to find that listed somewhere in a CDN.  I couldn’t find it, so I decided to see if I can reference it via google drive.  After some time investigating and finding this site I found how I was able to do it http://www.komku.org/2013/08/how-to-host-javascript-or-css-files-on-google-drive.html.  You are only allowed to reference files with a .js extension so I had to use method 1, and I generated the following url https://www.googledrive.com/host/0B4QQSpfhQZN5bXdTVTI0VFpjZW8/browser.js. 

    
Once you have it listed as an external resource on the JS Fiddle you can then create <script> blocks with the type of text/babel in your HTML area.  Once babel loads it will automatically compile those blocks and you are good to go!

Cheers!

Friday, May 8, 2015

Using HTML5 Canvas to Create a Transparent Image.

I recently have been working on learning some threejs stuff and trying to understand 3d concepts in general.  One of my favorite things is looking at 3d renderings of planets and other cosmic things.  So I found this post that talks about how he created the planets.  He uses the textures from planetpixelemporium for his planets.  Beyond the simple textures there are textures that define the bump map for rocky planets, textures for specular which defines the reflectiveness for each area, textures for clouds, and even textures for rings.

While I was going through his blogpost and his code, trying to make sure I understood everything he was saying, I came across this:
We build canvasCloud and use it as texture. It is based on the jpg images you see above: one for the color and the other for the transparency. We do that because jpg doesn’t handle an alpha channel. So you need to make the code to build the texture based on those images. 

I was super confused, and I apparently wasn't the only one based on his comments.  So armed with just his code, and google I set out to see what the deal is with multiple jpegs and transparency.  I found all kinds of interesting things out there and why people would write code doing this instead of using a png or another format that supports transparency like was done for this android app, was done for a game in JS, and this interesting trick that XORs two images to get a transparent one.

So armed with reasons why I wanted to visualize what this actually looks like when we go through the data and merge the two jpegs into an image with alpha.   I originally wanted to do this all in jsfiddle like I normally put my examples, but unfortunately jsfiddle doesn't support hosted images, I didn't want to pay one of the other online sandboxes to host them, and you can't look at the image data in a canvas that is from a different domain.  So I put this on a website I have been playing around with for a bit, so you too can see it in action.  The source is on github here.

To get this to work we will use 3 canvases (these don't have to be in the DOM to work).  One for the source image, one for the alpha (transparency) image which is all greyscale, and one for the destination.  We then load the two images we want by creating Image elements for each of the images, setting the src to the appropriate image, and then handling the onload event of each image.  The code looks like this:

    function loadImages(sourceImageUrl, alphaImageUrl){
        sourceImg = new Image();
        sourceImg.addEventListener("load", function(){
            sourceCanvas.height = sourceImg.height;
            sourceCanvas.width = sourceImg.width;
            sourceContext.drawImage(sourceImg, 0,0);            
            alphaImg = new Image();
            alphaImg.addEventListener('load', function(){
                alphaCanvas.height = alphaImg.height;
                alphaCanvas.width = alphaImg.width;
                alphaContext.drawImage(alphaImg, 0, 0);                
                startOverlay();
            });
            alphaImg.src = alphaImageUrl;
        });
        sourceImg.src = sourceImageUrl;
    }


Once that is done and we have the images loaded on the two canvases we will need to loop through the image data and copy over the RGB components from the source canvas and compute the alpha based on the brightness of the greyscale with white (255) being completely transparent, and black (0) being completely opaque (you can make this up however you want realistically).  Here is the code for that:

function startOverlay(){
        destinationContext.clearRect(0,0, destinationCanvas.height, destinationCanvas.width);
        
        var sourceData = sourceContext.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height),
            alphaData = alphaContext.getImageData(0,0, alphaCanvas.width, alphaCanvas.height),
            destinationData = destinationContext.getImageData(0,0, destinationCanvas.width, destinationCanvas.height);

        var mergeProperties = $scope.mergeProperties;
        mergeProperties.x = 0;
        mergeProperties.y = 0;
        mergeProperties.offset = 0;
        
        function mergeImages(){

            for(var i = 0; i < mergeProperties.speed; i++){

                destinationData.data[mergeProperties.offset+0]  = sourceData.data[mergeProperties.offset+0];
                destinationData.data[mergeProperties.offset+1]  = sourceData.data[mergeProperties.offset+1];
                destinationData.data[mergeProperties.offset+2]  = sourceData.data[mergeProperties.offset+2];
                destinationData.data[mergeProperties.offset+3]  = 255 -  alphaData.data[mergeProperties.offset+0];

                mergeProperties.x++;
                mergeProperties.offset+=4;
                if (mergeProperties.x >= sourceImg.width){
                    mergeProperties.x = 0;
                    mergeProperties.y++;
                }
                if (mergeProperties.y >= sourceImg.height){
                    destinationContext.putImageData(destinationData,0,0);    
                    return;
                }
            }

            destinationContext.putImageData(destinationData,0,0);    
            $timeout(mergeImages, 10);

        }
        
        mergeImages();
    }

The Timeout code is there just to slow it down for my demo. For actual production you would not have the timeout and the speed. You would just loop until you hit the imageHeight.

So take a look at it, it is pretty cool visualization.
Cheers!

Tuesday, April 7, 2015

Angular JS with MVC templates

I was recently working on an ASP.MVC project and I was thinking about using angular on the project.  There wasn't going to be a ton of dynamic things that required angular, but I still thought it would be a good fit for the project.  I knew that the typical way to use angular + ASP.MVC is to pretty much use ASP.MVC as a rest service (WebAPI), but I wanted a hybrid approach where I could use the Razor templating in places that had static data and angular templating in places I wanted it to be more dynamic.

With your powers combined...

The solution that we used allowed us to do both which is what I wanted.  We would use MVC templates instead of just html ones.  Angular would reference those templates in the routing and it could contain the server side information when it came back with the template.


So to get started I created a basic MVC project and opened up NuGet (.net package manager) to remove the jQuery packages and add AngularJS Route package.

Next I modified App_Start/BundleConfig.cs to bundle the new files.  I removeed the jQuery ones and added the angular.js ones.

 
  public static void RegisterBundles(BundleCollection bundles){

      // angular.js libraries
      bundles.Add(new ScriptBundle("~/bundles/angular").Include(
            "~/Scripts/angular.js",
            "~/Scripts/angular-route.js"
            ));
      //Application code
      bundles.Add(new ScriptBundle("~/bundles/app").Include(
            "~/Scripts/app.js",
            "~/Scripts/controllers/*.js",
            "~/Scripts/services/*.js"
             ));

      // Use the development version of Modernizr to develop with and learn from. Then, when you're
      // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
      bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                    "~/Scripts/modernizr-*"));

      bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
}

Next let's update the Views/Shared/_Layout.cshtml to use the new bundles.  We will also add the ng-app  and ng-view directives here as well.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body ng-app="mvcApp">
    @RenderBody()

    <div id="content" ng-view></div>
    
    @Scripts.Render("~/bundles/angular")
    @Scripts.Render("~/bundles/app")
</body>
</html>

For this app I am going to have a home page with 3 links to a view and passing in the id in the following format '<controller>/<action>/<id>'. 

Notice in my page I have the ng-view directive, but I also have @RenderBody() which MVC uses to render the pages. What I am going to do is create a default page that just renders as an empty page.  This is going to be hit on the initial load, but that is it.  After that the MVC views will be used as angular templates.

So let's first add the mvc pages & services.

For my first view and control I will name it Home just to keep things simple.  I am going to implement an Index function and just return the view.

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

And here is the corresponding view:

@{
    ViewBag.Title = "View1";
    Layout = null;
}

<h2>Home View</h2>

<p>{{hello}}</p>

<nav>
    <ul>
        <li><a href="#dynamic/1">Dynamic Page with Id 1</a></li>
        <li><a href="#dynamic/2">Dynamic Page with Id 2</a></li>
        <li><a href="#dynamic/3">Dynamic Page with Id 3</a></li>
    </ul>
</nav>

For all my links I will be going through the angular routing.  Likewise I can use angular in my views. Here is the dynamic view which is a bit more interesting.

The dynamic view accepts an id as a parameter and returns data based on it.  First we create the controller which will return  an object with the controller.  Then we will set up the view to use both data retrieved from an angular service call and the MVC template.  Finally we will create a service for our angular code to call.

The controller:
public class DynamicObject
{
   public string Name { get; set; }
}

public class DynamicController : Controller
{
    public ActionResult Index(string id)
    {
        return View(new DynamicObject { Name = "Dynamic Object Name " + id });
    }
}

The view:
@model MVCApp.Controllers.DynamicObject

@{
    Layout = null;
}

<h2>Dynamic View</h2>

<h3>
    @Model.Name
</h3>

<p>{{hello}}</p>

<ul>
    <li ng-repeat="item in dynamicList">{{item}}</li>
</ul>

The Service:
public class DynamicController : ApiController
{
   private Dictionary<string, string[]> Data;
   public DynamicController()
   {

       Data = new Dictionary<string, string[]>();
       Data.Add("1", new string[] { "Mercury", "Venus", "Earth" });
       Data.Add("2", new string[] { "Mars", "Jupiter", "Saturn" });
       Data.Add("3", new string[] { "Uranus", "Neptune", "Pluto" });
    }

    [AcceptVerbs("GET")]
    public string[] Index(string id)
    {
        return Data[id];
    }

}

Notice in the view we are using the value from the model Name and using ng-repeat to iterate through all of the items in the dynamicList on our angular $Scope object.

Now that we have our server code all set up (you should be able to actually hit each of these endpoints/views).  We can get started on the angular side.

First we will create our app.js file in the Scripts folder to define our app and routing.

'use strict';
angular
  .module('mvcApp', ['ngRoute']).config(['$routeProvider',  function ($routeProvider) {

      $routeProvider.when('/', {
          templateUrl: 'home',
          controller:'mainCtrl'
      })
      .when('/dynamic/:id', {
          templateUrl: function(params){
              return 'dynamic/index/' + params.id;
          },
          controller: 'dynamicCtrl'
      });
}]);


One interesting thing to note here is that instead of returning a url for the dynamic page, I return a function which is then used to get the URL for the view.  I do this so I can pass the Id from the angular.js routing to the MVC routing.  An alternative could be query string passing, but I wanted to stick with the nicer looking urls.

Next we will define our two controllers.  One for the dynamic view and one for the home view,  The home view one is pretty simple, so let's just skip to the dynamic view one.

angular.module('mvcApp').controller('dynamicCtrl', ['$scope',
'$routeParams', 'dynamicService', 

function ($scope, $routeParams, dynamicService) {
    $scope.hello = "Hello World from dynamic.js";
    dynamicService.getDynamicList($routeParams.id).then(function (data) {
        $scope.dynamicList = data.data;
    });
}]);

So in this controller we are setting data directly on the scope property hello and putting data from a service call onto the dynamicList property of the scope.  We use the id from the $routeParams to pass onto the service.

So let's define the dynamicService.js file that will call out to the MVC.WebApi Conroller that we created earlier.

angular.module('mvcApp').factory('dynamicService',['$http',  function ($http) {
    function getDynamicList(id) {
        return $http.get('api/dynamic/' + id);
    }

    return {
        getDynamicList: getDynamicList
    };

}]);


Now we have a service that calls the rest endpoint that we created earlier.  So now if we run the app we should get what we were looking for.  MVC views with an angular.js front-end!  The source code is on github here if you want to check it out and run it yourself or use it as a reference.

Cheers!

Dynamic Page 1

Dynamic Page 2

Dynamic Page 3



Friday, March 13, 2015

Developing without the backend: Working with 3rd Party Scripts

In my last post Developing without the backend. Simple Web Socket development., I talked about how to work offline with a simple Web Socket server so you can write your front-end in peace.  That is cool, but what if I have to deal with a 3rd party JS library that requires access to the Internet, login info to their site, actually running on their site, etc.  Essentially anything that requires you to run outside of your development environment.

So how do you deal with these 3rd party JavaScript API's using angular and work without their backend?  What if you need the cloud and you are offline?  What do you do?
Not that kind of cloud.  
Angular has the built in capability to use this pattern when necessary to add, remove, or completely replace the functionality of a provider by using the $provide.decorator function.

Angular has 2 major phases.  A run phase where it is actually executing the code, and a config phase where it wires up your application.  The decorator function comes in handy during the config phase that runs before the run phase.  In this phase you can access the $provider service, as well as built-in providers for your services, factories, etc.  So first you want to create a service wrapper for the 3rd party library that you are using.  Whether or not you just return the 3rd party object or create wrappers for all the functions, I'll leave up to you, but this will work for either method.  Next, you can do is create a config for your module (don't worry you can have multiple config calls) that uses the $provide.decorator function to overwrite or completely replace the service we just created.  For more information on these take a look at these posts on angular dependency injection and extending the $q method.  They are excellent reads.

I created a sample on jsFiddle to show you how it will work.  In this example I will use the underscore library in my application and then use the function above to overwrite one of the functions.  Outside of this tutorial, the next step would be to update your grunt (or whatever build) tasks to remove the mock config file from your project during the build process so you would actually use the real library.

To start I will create a simple service to wrap the underscore library, so I can write better unit tests and develop disconnected.

factory('underscoreService', function(){
    return _;
})

No magic there.  Next I will refer to that service in a controller object and call a couple of methods on it.

controller('ctrl' ,['$scope', 'underscoreService', function ($scope, underscoreService){
    $scope.list = ['Mercury', 'Venus', 'Earth', 'Mars', 
                   'Jupiter', 'Saturn', 'Uranus', 
                   'Neptune', 'Pluto'];
    
    $scope.sampleList = underscoreService.sample($scope.list, 3);   
    $scope.shuffleList = underscoreService.shuffle($scope.list);

}])

No magic here.  I have a list that creates 2 additional lists with method calls from underscore.  Now I want to swap out the logic from the service before it ever gets used.  This is where the $provider.decorator comes in. I will overwrite the logic for shuffle with a function that just returns the array passed in.

config(['$provide', function($provide){
    // override the function. you can uncomment it if you just want it to shuffle
    $provide.decorator('underscoreService', function($delegate){
        $delegate.shuffle = function(list){return list};
        return $delegate;
    });
}]

In the decorator function you receive a parameter called "$delegate" which is the return value from the service, factory, etc, before it gets injected anywhere.  So you can either overwrite the methods there, or you could return your own object instead!!!1

Here is the end result:


Just remember to update your build process to remove the config file!
Cheers!!

Monday, January 19, 2015

The Power of Apply and Call

A lot of people that use JavaScript don't understand (or even want to understand) the apply and call functions that are built into every function.

Or apply me maybe?
In combination with being a bit confusing, a lot of times you can get around them by just adding an additional parameter or some extra code here and there, and so you just ignore them.

I just ran across a situation that shows a great use case for apply and hopefully will help someone understand how to use it.

The scenario is I have two arrays.  One array of N points starting from 0 and one array of M points starting at an some positive integer X.  When my JS code handles array M, I want to insert and overwrite positions X...M.length + X of array N.  The brute force way would just be to loop through them all copying them as they go.  I thought about it for a bit and gave myself a quick reminder of what functions exist for a JS array.

I thought about concat, but that only appends to the end.  I thought about splice, but that only handles a list of parameters.  Then I remembered that JS already has that covered with apply.  Using apply will solve my issue of having an array instead of a list of parameters.  The code for this is pretty simple.  Given 2 arrays, and a position it will be this:
Array.prototype.splice.apply(array1, 
         [position, array2.length].concat(array2));

You might notice the [position, array2.length].concat code in there and wonder what that is.  Well the first 2 parameters to splice are the position and the # of elements to move.  The remaining parameters are the values to insert.  So using concat to add the two together will make the apply call work.

Here is a live example:



Monday, December 15, 2014

Custom Angular Flyout

While working recently on a project I came across an issue where I had a finite amount of space and a bunch of actions that would be visible or hidden based on its current state.  I put some time into thinking how I could fit everything I needed and I thought that +90% of the time the user won't be performing any actions beyond just viewing the data so I started thinking about how to minify the amount of space my buttons took up.  I first thought about using icons, but unless they are so universal, it is typically better to just go with text.  So I thought about a button that has a popup menu.  I'm not sure if flyout is the technical term for it, but that sounded right to me so that is what I went with.  One of the benefits of this approach was if I needed to add more buttons down the road I know it won't cause any issues.  Likewise I will always know exactly how much real estate it will take up.
You could have infinite benders take up a finite space!

I knew that I wanted my angular directive to have a couple of things.  First I wanted the flyout to be driven by the developer so it can be reused over and over.  This can be easily solved with an ng-transclude directive.  Secondly, I wanted to make sure that it was smart enough to figure out where it was positioned in the window and dynamically display based on its position in the window.

The HTML for the flyout should be pretty straight forward.  An outer container, .flyoutContainer,  for the control, a container for the flyout, .flyout, and a div for the text/button to expand the flyout, .expanderContainer.  The .flyoutContainer has a couple events on it, blur and click, to figure out when to display the .flyout, and a ng-class directive to help with any styles you want to apply as well.  Here is the HTML result:
 
<div class='flyoutContainer' tabindex='0'  ng-click='click();' 
   ng-blur='lostFocus();' ng-class='{"expanded": options.isExpanded}'>
     <div class='expanderContainer' >
       {{options.display || 'Expand'}}
     </div>
     <div class='flyout'  ng-transclude ></div>
</div>

Next is the bare bones CSS to handle what we need.  We want the .flyout to be absolutely positioned, to start off hidden, and to go on top of other elements.  When the .expanded class is applied we will then want to change the visibility so the .flyout and its contents will be displayed.  The .flyoutContainer styles are pretty simple.  Change the positioning to relative so the absolutely positioned .flyout will be relative to it, and remove any outlining (unless you are into that kind of thing).  For the .expanderContainer I just added css to change the pointer to a cursor to make it explicit that it is clickable.  Here is the css:
    
.flyoutContainer{
    outline:none;
    position: relative;
}

.expanderContainer{
    cursor:pointer;
}

.flyout{
    position:absolute;
    top:auto;
    left:auto;
    z-index:1;
    visibility:hidden;
}

.expanded .flyout{
    visibility:visible;
}

Finally we come to the JS code to put it all together.  The easy part of the code is to just set the options.isExpanded appropriately based on the current state and event.  This will drive the CSS above.  The more difficult part of the code is making sure that the positioning of the .flyout is set appropriately based on where it is located in the window.  For this we pass in the $window parameter to the directive to get the window height and width.  We use the $window param instead of the window global element to make life easier for unit testing.  We can then compare those values with the getBoudingClientRect values of the .flyout to determine how much you need to update the top and left properties of it.  Here is the resulting JS:

function link(scope, element){
    scope.lostFocus = function lostFocus(){
        scope.options.isExpanded = false;
        clearFlyoutPosition();
    };

    scope.click = function click(){
        updateFlyoutPosition();
        scope.options.isExpanded = !scope.options.isExpanded;

        if (!scope.options.isExpanded){
            clearFlyoutPosition();
        }
    };
       
    function clearFlyoutPosition(){
        var $flyoutElement = angular.element
            element[0].querySelector('.flyout'));
        $flyoutElement.css('top', 'auto');
        $flyoutElement.css('left', 'auto');
    }

    function updateFlyoutPosition(){
        var flyoutElement = element[0].querySelector('.flyout'),
            $flyoutElement = angular.element(flyoutElement);

        if ($window.innerHeight < 
            flyoutElement.getBoundingClientRect().bottom){
            $flyoutElement.css('top', '-' +
                (flyoutElement.getBoundingClientRect().height ) + "px");
        }
        else{
            // set the top to the flyout container so it starts right at the bottom.
            $flyoutElement.css('top','auto');
        }

        if ($window.innerWidth < 
            flyoutElement.getBoundingClientRect().right){
            $flyoutElement.css('left', "-" +
                (flyoutElement.getBoundingClientRect().right -
              $window.innerWidth + 10) + "px");

        }
        else{
            $flyoutElement.css('left', 'auto');
        }

    }

}


Here is the end result with one flyout in the top left and one in the bottom right!




Monday, December 1, 2014

Create Your Own Angular Confirm Dialog


Modal dialogs have always been kind of iffy to use in regards to UX.  This goes 1000000x for web dialogs.  While most browsers somewhat support showModalDialog, it definitely isn't recommended to use for a lot of reasons.    I do believe that dialogs like other maligned things still have their place if used properly.

Loud Noises!
The type of dialogs I am referring to on the web are typically not traditional dialogs, but a transparent div of height/width of 100%.  You can find these types of dialogs all over the internet for all kinds of various things.  Even as I create this blog post blogger had a custom dialog to insert an image, and a hyperlink.
Blogception
So I wanted to create something that I could reuse and style easily as well as incorporate in my work.  An angular directive seemed like the right approach for this.  I ended up creating a simple confirm dialog that you could set the text for the title, detail, and buttons, as well as the confirm and cancel button callbacks.  

The first thing I started out with was the HTML for the dialog.  It should be pretty straightforward,  one container to take the entire page, one div that is centered in the middle of the page and is the height and width of whatever we desire it to be, the title, the detail, and the buttons.  Here is what that looks like: 
      
    <div class='confirm' ng-hide='!showPrompt'> 
        <div class='confirmContainer'>
            <div class='confirm-title'>{{confirmTitle}}</div> 
            <div class='confirm-detail'>{{detailText}}</div> 
            <div class='confirm-actions'>
                <button class='ok' btn' ng-click='ok()'>{{okText || 'OK'}}</button>
                <button class='cancel btn' ng-click='cancel()'>{{cancelText || 'Cancel'}}</button> 
            </div> 
      </div>
</div>

Next lets add some basic CSS for this dialog to center everything, and set the height/width appropriately.  We want the dialog to take the entire height & width of the page with a transparent background.  To center the container vertically we put the top at 50% and then set a negative margin top to move it back up.  We then put the buttons container to the bottom right of the confirmContainer.   
      
.confirm{
    height:100%;
    width:100%;
    position: fixed;
    top:0;
    opacity: 1;
    left:0;
    background-color: rgba(3, 3, 3, .25);
    z-index: 1000;
}

.confirm .confirmContainer{
    position: relative;
    top:50%;
    width:50%;
    margin: 0 auto;
    height:200px;
    margin-top: -50px;
    background-color: rgba(255, 255, 255, 1);
}

.confirm .confirm-actions{
    position: absolute;
    bottom:10px;
    right:10px;
}

Finally lets get to the directive code.  We are going to use an isolated scope to bind whatever properties we want as well as the 2 functions we want to call.  We will also bind a showDialog property on the scope that will show the dialog when it is set to true.  When we want it to hide we will then set it back to false in our button code.  Pretty easy.  Here is the whole directive:

 
.directive('confirm', function(){
    var template ="<div class="confirm" ng-hide="!showPrompt">
" + 
        "<br />
<div class="confirmContainer">
" + 
            "<br />
<div class="confirm-title">
{{confirmTitle}}</div>
" + 
            "<br />
<div class="confirm-detail">
{{detailText}}</div>
" + 
            "<br />
<div class="confirm-actions">
"+
                "<button btn="" class="ok" ng-click="ok()">{{okText || 'OK'}}</button>"+
                "<button class="cancel btn" ng-click="cancel()">{{cancelText || 'Cancel'}}</button>" + 
            "</div>
" + 
        "</div>
</div>
";
    
    function link(scope){
        scope.cancel = function cancelFunc(){
            scope.showPrompt = false;
            scope.cancelFunction();
        };
        
        scope.ok = function okFunc(){
            scope.showPrompt = false;
            scope.okFunction();
        };
    }
    
    
    return {
        template: template,
        link:link,
        scope:{
            confirmTitle: '=',
            detailText: '=',
            cancelText: '=',
            okText: '=',
            okFunction: '&',
            cancelFunction: '&',
            showPrompt: '='
        },
        restrict : "E"
    };
});

Now the dialogs in action:




Some next steps for this would be to create an even more generic dialog using ng-transclude so you can have your own picture upload dialog.  Likewise, another way I thought of using this confirm dialog was to use this in conjunction with a dialog service that had access to the rootScope.  That way you could have 1 dialog in your root page and have your service open/close that one as necessary.  

Tuesday, November 18, 2014

Custom Dropdown With Angular.

The more I get into front-end development the more I end up disliking standard HTML inputs.  I dislike them for different reasons, but my biggest issue has been the inability to style them how I need them to.  This goes especially double for new HTML 5 elements that you pretty much have to jump through hoops to get any kind of custom styling.  I already took on restyling a checkbox since it doesn't really work well for my fat fingers.  Now I am going to take on the select tag.

What styling a select feels like.
I don't really dislike the select tag.  Overall it is pretty simple and you can style most of it.  However. if you end up deciding to style it with colors of any type, then you will hate it.  You will hate it because you can't change the hover color when it is expanded.  You will hate it because you can't change the arrow/box at the end that indicates it is a drop down.  You will hate it even more because not only can these styles not be modified, but they are different per browser/OS combo.

So after googling a bit, I didn't find anything I completely liked, so I decided I would handle it myself.  I decided that since I would be using it for angular development that I would make it a directive and have it function as a select element, that can be customized.

Here is the end result.






Wednesday, October 15, 2014

Creating A Resortable Drag & Drop List With Angular JS.

I recently ran into a requirement that required a user to be able to resort a given list elements via drag and drop.  As soon as I heard about the requirement I shuddered a bit.  I had the vague memory of trying drag and drop once and it not being as intuitive as it should have been.

I started with a quick Google search to see what was out there and while there were some good starting points, there wasn't anything that was exactly what I wanted.  Pretty much all the examples I found were dragging from area A to area B like this.  Nothing showed resorting in the same list like I had hoped.  What was even a bit more dismaying was when I searched specifically for angular.js implementations of drag and drop I found that it didn't even come with directives for those events.  There were some directives that others implemented for drag and drop, but I decided I should figure it out for myself instead of trying to fiddle with other people's code.

So to get started I decided to break my angular code into 3 directives.  One for making an element, draggable, one for making an element droppable, and one for making the entire list sortable.

Three sir.

Draggable directive.

To make something draggable we want to create a directive that can be applied to any element that we want to make draggable.  So I restricted the directive to "A" for attribute, and added a link function that attached event handlers to the dragstart and dragend events.  In the dragstart event I needed to do a couple of things.  First set the data that is going to be transferred (this could be anything but I set mine to an attribute on the element ), tell the browser what type of transfer is happening, and finally I want to add a class to the element I'm dragging so I can style the element more appropriately.  In the dragend event handler I just want to remove said class.

The end result ends up looking like this:

directive('draggable', function(){
    return {
        restrict: 'A',
        link: function (scope, element, attributes) {
            var el = element[0];
            el.draggable = true;
            
            el.addEventListener('dragstart', handleDragStart, false);
            el.addEventListener('dragend', handleDragEnd, false);
            function handleDragStart(e){
              this.classList.add('dragging');
              
              e.dataTransfer.effectAllowed = 'move';
              e.dataTransfer.setData('data', attributes.dragData);                
                
            }
            
            function handleDragEnd(e) {
              // this/e.target is the source node.
              this.classList.remove('dragging');
            
                           
            }
        }
    }
})

What is important to note here is whatever effectAllowed property you set, will dictate certain styles like the mouse cursor, and it limits where it can be dragged to other elements that have that same effect allowed.

Likewise, for the setData attribute, you can name that whatever you like.  It is a message for you to handle later.  So if you actually have multiple sets of data to drag and drop, you can limit what accepts it by having different "types".

Droppable directive.

The droppable directive is going to be similar.  Once again I am going to limit it to be an attribute on the element and to mostly adding events to the element.  The first events that I am going to handle are dragenter and dragleave.  All I am going to end up doing with these events is adding and removing a class to the element we are dragging over so we can add an additional style to it.  Next I am going to handle the dragover event where I am going to set the dropEffect of the element to match what is in the effectAllowed above.  Finally I am going to handle the drop event in which I will grab the data specified in the draggable directive above, and call a function on the scope.

Here is what it looks like:

.directive('droppable', function(){
    return {
        restrict:"A",
        link: function(scope, element, attributes) {
            var el = element[0];
            el.addEventListener('dragenter', handleDragEnter, false);
            el.addEventListener('dragover', handleDragOver, false);
            el.addEventListener('dragleave', handleDragLeave, false);
            el.addEventListener('drop', handleDrop, false);
            
            function handleDragOver(e) {
              if (e.preventDefault) {
                e.preventDefault(); // Necessary. Allows us to drop.
              }
              e.dataTransfer.dropEffect = 'move';  
            
              return false;
            }
            
            function handleDragEnter(e) {
                // this / e.target is the current hover target.
                this.classList.add('dragover');
            }
            function handleDrop(e){
                // Stops some browsers from redirecting.
                if (e.stopPropagation) e.stopPropagation();
                var data =e.dataTransfer.getData('data');
                
                if ('function' == typeof scope.ondrop) {      
                    scope.ondrop(data, attributes.dropData , e);
                }
                this.classList.remove('dragover');  
            }
            function handleDragLeave(e) {
                if (e.preventDefault) e.preventDefault();
                this.classList.remove('dragover');  
               
            }
        }
    };
})


Reorder Directive

The reorder directive is tied to an Array object and attaches the draggable and droppable directives onto each element in the array.  I decided to make the reorder directive based on an element ("E") and enabled transclude on it allow you to style each list element yourself.  The rest of the directive creates 1 <li> element for each object in the array and implements the drop handler needed in the draggable directive.  In the drop handler I had to add an $apply call to notify the UI that changes had been made to the array and that it should be updated.

Here is the directive:
directive('reorder', function () {
    return {
        restrict: 'E',
        transclude: true, 
        scope: {
            list: '='
        },
        template: '
  • ', link: function (scope, element) { scope.ondrop = function resort(dragIndex, dropIndex, event){ if (dragIndex == dropIndex){ return; } scope.$apply(function(){ dragIndex = Number(dragIndex); // grab element to move var elementToMove = scope.list[dragIndex]; // remove the element scope.list.splice(dragIndex, 1); // insert the element scope.list.splice(Number(dropIndex), 0, elementToMove); }); }; } }; });


    Here is the end result:
    Cheers!