• Search form is empty!

  • Ease the Transition to Angular 2.0

    http://robertdunaway.github.io

    Ease the Transition to Angular 2.0

    Significant changes are coming to Angular 2.0. Since the http://ngeurope.org/, I’ve considered how to position my angular applications for Angular 2.0.

    The changes coming are being driven in large part by ES6. Eventually, all frameworks will need to make this transition. Angular is stepping up and getting it done now. So let’s just suck it up and get-er-done.

    Go ahead and bookmark this blog post. Whenever I learn of a change that positions our applications for Angular 2.0, I’ll update this post. This will be a single go-to place for all changes, making the transition as easy as possible.

    Let’s jump right in…

    Learn ES6

    I’ll soon post links here to many tutorials, utilities, and advice on making this transition.

    Use the “controller as” syntax

    With the release of Angular 1.3 the “controller as” syntax was introduced.

    This helps reduce dependence on $scope and positions our controllers to more easily become ES6 classes when we transition to Angular 2.0.

    Use the new ng-router

    Update: Rob Eisenberg left the AngularJS team and is back to working on Durandal.
    Rob Eisenberg, the author of Durandal, is championing a new more advanced router for Angular. His experience with Durandal, and a general survey of the features available in all of today’s popular routers, has driven the requirements for the new router.

    The ng-router is an Angular 2.0 feature but is being back ported to 1.3. Early adoption of this new router means you’ll have one less thing to deal with when Angular 2.0 comes out.

    Besides, it’s got a few freaking cool features in it. Check out the video below. It’s two months old which, in our new world, means it’s ancient and outdated but still a great starting point. I’ll post more tutorials as I learn how to use it.

    https://www.youtube.com/watch?v=h1P_Vh4gSQY
    https://www.npmjs.org/package/angular-new-router

    Use services wherever possible

    The more code we can move out of controllers and into services, the easier the transition to Angular 2.0 will be.

    Full disclosure – I came across this advice on “Adventures in Angular” podcast and while I get “separation of concerns,” I don’t know how it helps with Angular 2.0. I’ll figure this out and update my notes here.

    Lazy loading

    Lazy (eager) loading of modules is planned for Angular 2.0.

    For now, I’ve prepared for this by implementing lazy loading with ocLazyLoad.

    I mention it in this router post but I’ll provide a specific post about lazy loading later.

    http://robertdunawaypro.blogspot.com/2014/10/routeconfig.html

    Drop-in application design

    I have no idea what to call this, so I completely made up that name. The idea is this. With the new angular router you’ll have the ability to modularize your application such that you can drop modules into an application from another application and this is supposed to work.

    http://Mashupjs.githut.io has an implementation like this. I’ll upgrade it to use the features of the new Angular router as soon as I can.

    In closing

    I’m just like everyone else. I’m figuring this stuff out as I go. If you come across something you think might be helpful then post a comment and I’ll add it to this list.

    Thanks…

    jQuery 2.x or jQuery 1.x ??

    jQuery 2.x or jQuery 1.x ?

    If you employ “Continuous Improvement” in an effort to battle technical debt, you’ll need to consider the direction of jQuery in your decision to upgrade. Even your incremental upgrade strategy may have consequences you weren’t prepared for.

    Upgrading to 2.0

    If you simply upgrade to 2.0 from 1.x and run your tests, everything will work just fine. That is until you receive calls from angry users about features that no longer work.

    Why?!

    This is because you are a developer and work with Greenfield browsers. jQuery 2.x has dropped support for IE 8 and lower. jQuery 1.x will still be around for these older browsers. jQuery staff recommend you use jQuery 1.9 if you think there is a chance some of your user base is still using IE 8.
    http://jquery.com/upgrade-guide/1.9/

    Either will work

    The jQuery API is consistent between 1.9 and 2.0, so when you feel you no longer need to support IE 8, you can swap in the 2.0 release and benefit from the performance increase without changing code.

    In theory…

    So, of course we test our code when we update libraries… right? Right. In the case of jQuery 2.0, don’t just test but make a conscious decision on which path to take. 1.9 or 2.0. IE 8 or not IE 8. Enjoy…
    http://jquery.com/download/

    Software Architect or Handyman

    Software Architect or Handyman

    Businesses need both but they don’t often realize this. First, let’s define a software architect. Later, I’ll share the qualities that I think make for a good software architect.

    The software architect

    Wikipedia

    http://en.wikipedia.org/wiki/Software_architect
    “Software architect is a computer programmer or computer manager or expert who makes high-level design choices and dictates technical standards, including software coding standards, tools, and platforms.”

    Not so fast, Wikipedia. Agreed, a software architect “is” a programmer but he may not be a manager and need not dictate standards and tools.

    In fact, an architect should have vast understanding of an array of technologies and approaches but might work with the existing technologies. There are many right ways to do things.

    Microsoft

    http://msdn.microsoft.com/en-us/library/ee658098.aspx
    Philippe Kruchten, Grady Booch, Kurt Bittner, and Rich Reitman definition is:
    “Software architecture encompasses the set of significant decisions about the organization of a software system including the selection of the structural elements and their interfaces by which the system is composed; behavior as specified in collaboration among those elements; composition of these structural and behavioral elements into larger subsystems; and an architectural style that guides this organization. Software architecture also involves functionality, usability, resilience, performance, reuse, comprehensibility, economic and technology constraints, tradeoffs and aesthetic concerns.”

    Honestly I got lost by the end of this and have little to no idea what the heck they are saying.

    Martin Fowler

    “The highest-level breakdown of a system into its parts; the decisions that are hard to change; there are multiple architectures in a system; what is architecturally significant can change over a system’s lifetime; and, in the end, architecture boils down to whatever the important stuff is.”

    Clear as mud…

    Now what?

    So I guess all we’ve proved is there can be many opinions about the definition of a software architect.
    And that’s okay.

    So now that we’ve agreed not to agree about the definition of a software architect, let’s just consider my opinion on what it takes to be a software architect and leave the discussion of “what” a software architect is, to people much smarter than me.

    The software handyman

    The reason for this blog is all to often companies hire a handyman to build their house and skip the architect all together.

    This is a problem. Far to many projects fail to be delivered or when delivered fail when under load. All this could be avoided if an architect were involved.

    The same reason you wouldn’t use a handyman to build your house without the blue prints of an architect is the same reason you need a Software Architect for your enterprise applications.

    Any Software Handyman can become an architect with work. It’s something they must desire and dedicate years toward. Not every Software Handyman wants to. Maybe are happy to support existing applications and write code for new applications according to a specification. We need these guys. They are the unsung heroes of IT.

    What it takes…

    So, maybe I can’t give you one definition of an architect. Generalized terms and descriptions offer little to the concrete world we programmers live in.

    So, dispensing with the BS titles and ego boosting descriptions I’ll simply say what I think an architect must be.

    Be experienced

    Sounds obvious right. Well it’s not. A programmer supporting an application for 15 years might be experienced but is not Software Architect material based on that experience alone.

    A Software Architect must be experienced in a variety of environments with many technologies accompanied with both successes and failures. A diversity of solutions might include client-server, n-tier, distributed computing, and enterprise application integration (EAI) solutions. Failure experiences are as important as successful experiences.

    Anyone who does anything significant is going to have failure. I wouldn’t trust anyone who didn’t.

    Know surrounding practices

    Surrounding practices are all those “things” that bump up against software architecture but are not specifically architectural tasks.

    Examples include project management, process methodologies, networking, and testing. If we had a brainstorming session we could easily come up with a dozen more.

    Be a communicator

    The only thing worse than having no architect is having an Architect who cannot communicate or is a curmudgeon. Instead of not having an architect which leaves room for improvement you have someone in place nobody wants to follow and who could be difficult to remove.

    Like the Project Manager an Architect must be able to communicate ideas and listen to his programmers. As much can be learned from other programmers as personal research.

    I’ve seen grouchy old men who can’t keep up with the young wiper snappers and it’s imply uninspiring. Don’t be a curmudgeon! Programming is supposed to be fun. Don’t beat your programmers up with “Standards”, encourage them with “Guidelines”.

    Study, study, study

    An architect shouldn’t have to study anymore. Right? I wish!

    At the rate of change technology is taking the architect job is no longer as easy as it once might have been. Today an architect must be a perpetual student, always learning, and always testing out new approaches and techniques.

    Whatever technology you are using today is outdated in two years. When reading technology specific blogs and listening podcasts I check the dates. I skip anything over 6 months old and anything over 3 months old I view with skepticism.

    The Architect job is a hard hard job and if you don’t want to do the work then that’s OK. Don’t take the job.

    I imagine this is the one topic that will piss other architects off the most. We like to feel like we have arrived and all we need to do now is attend meetings, give our blessing and deliver edicts. How nice that would be.

    Nope, we have to work our tukisses off more than anyone else because not only must we understand the technology but we must be able to place it into an enterprise context and teach it. When teaching we must know the technology many times more than if we simply wanted to use it.

    Be a coder

    Yep, not only should an architect have written several man years of code he’d better still be writing code.

    As stated earlier, technology is changing at a rapid pace. An architect who is not writing code is quickly outdated and will have his lunch eaten by the next young programmer who “is” excited about technology. He won’t have the experience an architect should have but he will be able to code circles around a non-coding architect.

    So pick a project, select a set of project requirements, humble yourself if needed, and get coding. It’s the only way to stay in this game.

    Read/write articles, blogs, podcasts… oh my!

    There is so much information published every day the even focusing on a narrow subject you can not hope to read everything available or even keep up.

    It doesn’t matter. Today’s good architect reads every article and blog he can, has a list of podcasts. When enough time isn’t available cherry picking the most important podcasts is necessary.

    Subscribing to news letters is an excellent way to stay on top of the best articles without having to search for them. When you discover a programmer author you respect then find his home page, contribute comments to his articles because this will make you a more critical thinker and you’ll, in time become a thought leader.

    You should probably blog once a week because that forces you to continue learning and share what you have learned with the rest of us.

    Be a teacher, mentor, thought leader, and be opinionated

    This is the fruit of your labor which will multiple when it comes into contact with other programmers and applications.

    Be a teacher to all and a mentor to those you see potential in. Chances are someone was a mentor to you. You could change a life. In this industry we do change lives.

    Be an opinionated thought leader. We can never know it all and if we try to know something we risk being wrong. There is nothing more uninspiring than an architect who never has a solid opinion and even less inspiring is an architect who never changes or updates his opinion.

    So be a though leader and come to some conclusions. When more information is available change those conclusions. Don’t keep it a secret out of fear someone might think you are wrong. Share it and have a bit of confidence.

    If you aren’t making mistakes then you aren’t doing anything.

    Feedback welcome

    Being opinionated means I will sometimes be wrong but I’ve given myself the opportunity to get a few things right. I’m bothered by my “handyman” metaphor because it doesn’t give the deserved credit to the everyday coder. I might completely change that.

    If you have feedback, I’m open to learning. You can leave a comment or just email me. I’m not looking to build some large readership base and boost my ego. I simply want to be better tomorrow than I am today.

    Thank you. Bob

    Use Angular's Controller As syntax

    Use Angular’s Controller As syntax

    http://robertdunaway.github.io

    Sure, when we first learn to use Controllers, we start with the standard constructor with $scope.
    There is a better way.

    101 Using Controllers

    The controller can be paired with the html in the html itself or in the route configuration. For the purpose of this demonstration, we’ll use the route configuration.

    Here we have a route pairing up a controller with its view.

    This is a common approach and perfectly acceptable but not a best practice.
    When dealing with scope, you’ll find yourself referencing the $parent in Views with nested controllers.
    $scope is deprecated or gone in Angular 2.0 so it wouldn’t hurt to learn to live without it.

    Route

    .when('/exApp1/angularExamplesMain/controllers', {
           templateUrl: "apps/exApp1/angularExamplesMain/controllers/controllers.html",
           controller: 'exApp1.controllersController',
           ...

    Controller

    /*global mashupApp:false */
    mashupApp.controller('exApp1.controllersController', function ($scope) {
        $scope.a = 1;
        $scope.b = 2;
        $scope.myValue = 0;
        $scope.add = function (a,b) {
            $scope.myValue = a + b;
        }
    });

    Using Controller As syntax

    The Controller As approach puts the controller on the scope, so it’s not necessary to pass in $scope anymore.
    Simply referencing variables and functions with “this” is enough.
    The challenge with “this” is “this” can have varying meanings in different contexts.
    So while we no longer have to deal with $parent we now have to deal with what “this” really means at any given point.
    Notice the controllerAs: ‘vm’ syntax in the route config.
    Route
    .when('/exApp1/angularExamplesMain/controllers', {
           templateUrl: "apps/exApp1/angularExamplesMain/controllers/controllers.html",
           controller: 'exApp1.controllersController',
           controllerAs: 'vm',
           ...
    Controller
    /*global mashupApp:false */
    mashupApp.controller('exApp1.controllersController', function () {
        this.a = 1;
        this.b = 2;
        this.myValue = 0;
        this.add = function (a,b) {
            this.myValue = a + b;
        }
    }); 

    Best Practice

    Setting “this” equal to a local variable

    Assigning a local value a reference to “this” solves the problem of “this” potentially meaning something different in a different context.
    In this case “vm” will always mean what it meant at the moment it was set equal to “this”.
    Additional References:
    https://github.com/johnpapa/angularjs-styleguide#controllers
    http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/

    Route

    .when('/exApp1/angularExamplesMain/controllers', {
           templateUrl: "apps/exApp1/angularExamplesMain/controllers/controllers.html",
           controller: 'exApp1.controllersController',
           controllerAs: 'vm',
           ...

    Controller

    /*global mashupApp:false */
    mashupApp.controller('exApp1.controllersController', function () {
        // vm = View Model
        var vm = this;
        vm.a = 1;
        vm.b = 2;
        vm.myValue = 0;
        vm.add = function (a,b) {
            vm.myValue = a + b;
        }
    });    

    Local variable “vm” and Controller As “vm”

    You might see where using “vm” in two different contexts in the view and controller might be bad coding practice.
    In an effort at maintaining good coding practice, I could name the local value something different but now I’d refer to the add method in code as vp.add() and in the markup as vw.add().
    The approach of naming the local variable and controller the same doesn’t seem to cause any practical conflict.

    Route

    .when('/exApp1/angularExamplesMain/controllers', {
           templateUrl: "apps/exApp1/angularExamplesMain/controllers/controllers.html",
           controller: 'exApp1.controllersController',
           controllerAs: 'vm',
           ...

    Controller

    /*global mashupApp:false */
    mashupApp.controller('exApp1.controllersController', function () {
        // vm = ViewModel
        var vp = this;
        vp.a = 1;
        vp.b = 2;
        vp.myValue = 0;
        vp.add = function (a, b) {
            vp.myValue = a + b;
        }
    });

    WebApi-HowToCreateForMashup

    Create WebApi for the Mashup & CORS

    http://robertdunaway.github.io

    The Mashup is a learning tool that also serves as a bootstrap project for line-of-business applications.

    http://mashupjs.github.io

    The WebApi or any restFul service provides back-end support for SPA applications.

    Intro

    This is a step-by-step tutorial for creating a WebApi for the Mashup.

    The WebApi provides the back-end support for your Angular application. A typical SPA application with Angular will consist of a WebApi and index.html page on the same web site. This is an easy configuration to spin up and start coding against.

    The challenge arises when your application grows and your enterprise expects to share programming resources. The client may no longer be coming from the same domain and you enter into cross domain sharing of resources. This is where CORS comes into play.

    Here is a step-by-step tutorial for creating a WebApi for the Mashup that addresses the CORS issue.

    Creating the WebApi

    Using Visual Studio, add a new project to your existing Angular solution.

    Right click the solution and select Add -> New Project.

    Select ASP.NET Web Application and give your project a name, then press OK.

    enter image description here

    The template page loads.

    Select Web API in the “Select a template” section.

    MVC and Web API will be automatically selected.

    Select “Change Authentication” and select No Authentication and press OK.

    Verify Host in the cloud is unchecked.

    Press OK.

    enter image description here

    Using the selected template and settings, Visual Studio creates the new Web API.

    Getting CORS to work

    CORS support is already built into the Web Api. What we need to do is add headers to requests so Chrome and Firefox can access our resources.
    NOTE: I’ve had inconsistent experiences with WebApi and CORS. Make sure you’ve installed the NuGet package giving you Microsoft.AspNet.WebApi.Cors.
    enter image description here
    Add the code, below, to your Global.asax in the public class Global.
    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        if (Context.Request.Path.Contains("api/") && Context.Request.HttpMethod == "OPTIONS")
        {
    
        Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
        Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST PUT, DELETE, OPTIONS");
        Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        Context.Response.End();
        }
    } 
     
    Enable CORS by adding the following to the Register function of the WebApiConfig.cs

    // Enabling CORS Globally
    var cors = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
    config.EnableCors(cors);
     
    Add the following to the include session of the WebApiConfig.cs.

    using System.Web.Http.Cors;

    Attribute Routing

    To enable Attribute Routing, add the following code to your App_Start/WebApiConfig.cs file.
    Oaky, never mind. Since the last time I did this, it seems the new Web API template does this for you. Here is the code that makes Attribute Routing possible.
    // Web API routes
    config.MapHttpAttributeRoutes();
    With the rapid changes in technology, it can almost be detrimental to be an early adopter.
    As you see above, I was expecting to write extra configuration code but since the time I created the first Mashup Web APIs, the Visual Studio templates have been improved.
    You might have noticed the old template only created WebApiConfig.cs and the new template creates both the WebApiConfig.cs and RouteConfig.cs templates.
    This leads me to believe I should make it a practice to periodically recreate my projects using the latest Visual Studio templates. This might be a best practice for all third party libraries.
    This would be an effort to keep Technical Debt to a minimum. As the projects we create age, they become liabilities. It seems to me the effort to keep projects current is less if done more frequently with fewer changes between versions.

    Create an endpoint to test.

    In the NewWebAPI’s Controller’s directory create two files.
    • Item.cs - as a class with the following content.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace **Leave your namespace here**
    {
        public class Item
        {
            public int id { get; set; }
            public string action { get; set; }
            public bool done { get; set; }
            public DateTime completed { get; set; }
            public decimal myDecimal { get; set; }
            public double myDouble { get; set; }
            public long myLong { get; set; }
            public string contact { get; set; }
            public string doneWithIndeterminate { get; set; }
        }
    }
    • ItemController.cs
    Add the following code to it. Remember to swap out your namespace.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    
    namespace **Leave your namespace here**
    {
        [Authorize] // Even though this says "Authorize" it should require the user to be at least Authenticated.
        public class ItemController : ApiController
        {
            List<Item> _items = new List<Item> {
                new Item { id = 1, action = "Buy Gloves", done = false, completed = Convert.ToDateTime("5/28/2014"), myDecimal = 1.1m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 2, action = "Get Hair cut", done = false, completed = Convert.ToDateTime("5/28/2014"), myDecimal = 1.2m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 3, action = "Collect Tickets", done = true, completed = Convert.ToDateTime("5/28/2014"), myDecimal = 1.3m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 4, action = "Call Joe", done = false, completed = Convert.ToDateTime("5/28/2014"), myDecimal = 1.4m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 5, action = "Check TODO List", done = false, completed = Convert.ToDateTime("6/28/2014"), myDecimal = 2.1m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 6, action = "Call the tax dude", done = false, completed = Convert.ToDateTime("6/28/2014"), myDecimal = 2.1m, myDouble = 1.00, myLong = 64, contact = "name1@domain.com"},
                new Item { id = 7, action = "Feed the dogs", done = true, completed = Convert.ToDateTime("6/28/2014"), myDecimal = 2.1m, myDouble = 1.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 8, action = "Start spring cleaning", done = false, completed = Convert.ToDateTime("6/28/2014"), myDecimal = 3.1m, myDouble = 1.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 9, action = "Schedule checkup", done = false, completed = Convert.ToDateTime("7/28/2014"), myDecimal = 3.1m, myDouble = 1.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 10, action = "Sweep porch", done = false, completed = Convert.ToDateTime("7/28/2014"), myDecimal = 1.1m, myDouble = 1.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 11, action = "Do dishes", done = true, completed = Convert.ToDateTime("7/28/2014"), myDecimal = 1.1m, myDouble = 2.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 12, action = "Take out trash", done = false, completed = Convert.ToDateTime("7/28/2014"), myDecimal = 1.1m, myDouble = 2.00, myLong = 64, contact = "name2@domain.com"},
                new Item { id = 13, action = "Do your homework!", done = true, completed = Convert.ToDateTime("1/28/2014"), myDecimal = 1.1m, myDouble = 3.00, myLong = 64, contact = "name2@domain.com"}
            };
    
    
            [Route("api/Items")]
            [HttpGet]
            public List<Item> GetAllItems()
            {
                return _items;
            }
    
            [Route("api/Items/{id:int:min(2)}")]
            [HttpGet]
            public List<Item> GetItem(int id)
            {
                var items = new List<Item>();
    
                var item = _items.FirstOrDefault((p) => p.id == id);
    
                items.Add(item);
                return items;
            }
    
    
        }
    }

    JSON Formatter

    Before we call this new Web Api let’s tell it to return all data as JSON.

    Open your App_Start/WebApiConfig.cs file and copy this line at the end.

    // By default Web Api wants to return XML.  (This is confusing because RestFul is based on JSON)
    // This line changes the default return type to JSON.
    config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

    This will require a new using statement.

    using System.Net.Http.Headers;

    Help page

    You’ll notice many more files, created by the template we used, than are necessary to create a WebApi. This is because the WebApi template also creates a web UI. You can build off this UI but our intention is only to use the UI as a helper for building WebApi(s).
    I’m choosing to keep the UI pieces because I like the Help feature. It would not be unreasonable to remove all files except those needed to create the WebApi. In fact, if this is your intent upfront then rather than select WebApi as a template, select Empty as your template, then MVC and WebApi below when adding the new project. This tutorial was originally based on this approach, so we know it works.
    We are not interested in seeing the start page when we start up this project, so we’ll make a slight modification to this project’s properties.
    Right click on the project we just created in the Solution Explorer and select Properties.
    Select the Web tab then change the value of Specific Page to “Help”.


    enter image description here




    enter image description here

    Call the WebApi

    Set the new WebApi as the startup project and press the Run button in Visual Studio.

    enter image description here

    Enter localhost:49261/api/Items

    enter image description here

    You might have a jumbled mess of JSON on your screen. To clean that up you’ll want to use some kind of browser plug-in. What I’m using here is JSONView.

    enter image description here

    One more thing

    This will likely go away as templates improve and WebApi matures but for now, as of Nov 2014, you need up update your WebApi 2.2 NuGet package.

    The problem is Attribute Routing and CORS don’t get along well until you install the Microsoft ASP.NET Web API 2.2 and the Version 5.2.2 package version. The Visual Studio template uses the 5.2.0 version of the package and it don’t work so well.

    Once this is installed you should be able to access WebApi from your AngularJS client even when it’s not part of the Origin domain. You can build WebApi(s) for your enterprise and not specifically for one client. Also, you’ll find this useful when building hybrid mobile applications whose origin will never be the same as we WebApi.

    enter image description here

    routeConfig.js

    routeConfig.js


    http://robertdunaway.github.io

    The Mashup is a learning tool that also serves as a bootstrap project for line-of-business applications.

    https://github.com/MashupJS/MashupJS

    The routeConfig.js file holds routing information for the Mashup.

    Multiple files implementation

    There is one routeConfig.js file per application. This offers a little independence to the developer and application if it is deployed as a mobile application.

    Additionally, for large applications, separating route configurations makes it easy to exclude routes the user doesn’t need, reducing the number of routes the system must interrogate between state changes.
    Applications are defined by each directory in the core/apps directory. 
    Examples:
    App1 - root/core/apps/app1
    App2 - root/core/apps/app2
    Mashup - root/core/apps/mashup

    root/config/rootConfig.js

    This is the initial route configuration file that is always loaded. It contains the default route “/” and more importantly the code for starting up the session.

    The loadCompleted() function is run by every route requiring an authenticated user. It gets user information and puts it into the sessionService, then logs the route for instrumentation and analysis.
    The “getUserInfo()” function may vary from application to application but the pattern is in place and works for anyone who wishes to verify authentication before executing a route.

    The “60” in getUserInfo(60) means the user’s information is cached and that cache doesn’t become stale for 60 minutes. If the user information cache is not stale then the cache is used rather than making a call to the authentication server, AuthApiADSP.
    NOTE: AuthApiADSP is the authentication server used by the Mashup but any custom implementation will work.
    The ADSP means the AuthApi uses Active Directory for authentication and Stored Procedures for data access as opposed to oData or EF.

    Lazy Loading

    Typically a SPA application written with Angular will have many files referenced in the Index.html that load when the application starts. To improve this, many files can be concatenated into one and then minified.

    Lazy Loading takes this one step further. Files are still minified and an application may choose to concatenate its own files but the files are never loaded until the first time they are needed and then they are cached for subsequent loads.

    This release of Angular (1.3) does not support lazy loading but I expect it will soon be available with the work Angular 2.0 is doing and much of that will be done in the router and ported back to Angular 1.3.

    How we are implementing lazy loading, until Angular supports it, is via ocLazyLoad.

    mashupApp.config(function ($routeProvider) {
    
        $routeProvider
    
            .when('/mashupExamples/angularExamplesMain', {
                templateUrl: 'apps/mashupExamples/angularExamplesMain/angularExamplesMain.html',
                controller: 'angularExamplesMainController'
                , resolve: {
                    loadMyCtrl: ['$ocLazyLoad', function ($ocLazyLoad) {
                        return $ocLazyLoad.load({
                            name: 'mashupApp',
                            files: ['apps/mashupExamples/angularExamplesMain/angularExamplesMainController.js']
                        });
                    }]
                 , sessionLoad: function ($route, sessionLoad) { return sessionLoad.loadCompleted(); }
                }
            })
    NOTE: Notice the file property is an array. Here you can link to any and all js files your module needs and if it has already been requested, that will be used and the load never occurs.
    Here is the Network tab in Chrome during the first load of a module and the next image is during the second call for the same module.

    First load


    Second load


    NOTE: There is a bug in ocLazyLoad where you must tell it what modules you’ve already loaded. ocLazyLoad 0.3.9 fixes this for everything except Angular Bootstrap which the mashup uses.
    https://github.com/ocombe/ocLazyLoad/issues/71#issuecomment-61446335
    Added to address the ocLazyLoad issue.
    // ----------------------------------------------------------------------------------------------
    // This configures ocLazyLoadProvider and let's it know that some modules have already been
    // loaded.  Without this the Menu dialog would not work because some directive was loaded twice.
    // https://github.com/ocombe/ocLazyLoad/issues/71
    // ----------------------------------------------------------------------------------------------
    angular.module('mashupApp').config(['$ocLazyLoadProvider', function ($ocLazyLoadProvider) {
    
        $ocLazyLoadProvider.config({
            loadedModules: ['ngRoute', 'ui.bootstrap', 'ngSanitize', 'oc.lazyLoad']
        });
    
    }]);
    

    Resolve

    The resolve function is very powerful and allows you to perform functions required before a route is executed.

    The Mashup makes use of Resolve to verify the user has been authenticated, after which the user session is created.

    Here is the configuration for the /about route.

        $routeProvider
            .when('/about', {
                templateUrl: 'apps/mashup/about/about.html',
                controller: 'aboutController',
                resolve: {
                    loadMyCtrl: ['$ocLazyLoad', function ($ocLazyLoad) {
                        // you can lazy load files for an existing module
                        return $ocLazyLoad.load({
                            name: 'mashupApp',
                            files: ['apps/mashup/about/aboutController.js', 'apps/mashup/~appServices/dataService.js']
                        });
                    }]
                    , sessionLoad: function ($route, sessionLoad) { return sessionLoad.loadCompleted(); }
                }
    
            })
     
    When the /about route is triggered, the templateUrl is set as well as the controller. Then you’ll have a resolve. Everything in the resolve must be complete before the route can execute. In this case we have two functions. First the loadMyCtrl function must complete and then the sessionLoad: function. The session load calls sessionLoad.loadCompleted() function.

    Here is the factory implementation. You’ll see that every attempt is made to short-circuit the load event if it can be determined the user has been detected recently. Slowness in this function will be perceived by the user.
    
    mashupApp.factory('sessionLoad', function ($log, $q, $timeout, $location, $interval, sessionService, mashupDataService, utility) {
    
        var userInfoCacheDuration = 1;
        var userInfoLastChecked = 0;
    
        var logRouteInstrumentation = function () {
            // -------------------------------------------------------------------
            // Instrumenting the application so we can track what pages get used.
            // -------------------------------------------------------------------
            var logObject = utility.getLogObject("Instr", "MashupCoreUI", "sessionLoad", "loadComplete", "UI-Routing", sessionService);
            // Additional or custom properties for logging.
            logObject.absUrl = $location.absUrl();
            logObject.url = $location.url();
            $log.log("UI-Routing to [ " + $location.url() + " ]", logObject);
            // -------------------------------------------------------------------
            // -------------------------------------------------------------------
        };
    
        var loadCompleted = function () {
    
            var defer = $q.defer();
    
            (function () {
                // This controller only loads once when this site with any route is reloaded.
                // For now this is where I'll put code that needs to load once.
    
                // Attempt to shortcircuit call to getUserInfo if it has been called within the cache duration.
                var duration = (new Date().getTime() - userInfoLastChecked) / (1000 * 60); // 1000 is one second and 60 is one minute.
                if (duration > userInfoCacheDuration) {
    
                    mashupDataService.getUserInfo(60).then(function (data) {
    
                        if (data === null || data === undefined || data === '' || data.length === 0) {
    
                            // TODO: add this to a log that is transmitted immediately  will need to implement the log transmission module first.
                            $log.info('The getUserInfo returned an empty array.  Just thought you should know.');
    
                        } else {
                            // saving session information for the rest of the mashup to use.
                            userInfoLastChecked = new Date().getTime();
                            sessionService.setUserSession(data[0]);
                        }
    
                        logRouteInstrumentation();
    
                        // TODO: Might be a good place to put some AuthR code.  Anything we do here needs to also be done at the server.
                        // The client side is to easily manipulated.
                        defer.resolve(true);
                    }),
                        function () {
                            // if userInfoLastChecked is not 0 then the user has been authenticated at some point.
                            // if an error occurs then we should not prevent the user from navigating to the next page.
                            if (userInfoLastChecked) {
                                defer.resolve(true);
                            }
                        };
                } else {
                    // short circuit because we are within the duration threshold
                    logRouteInstrumentation();
                    defer.resolve(true);
                }
            })();
    
            return defer.promise;
        };
    
        return {
    
            // Good jsfiddle on how this works. http://jsfiddle.net/JYvsZ/
    
            loadCompleted: loadCompleted
        };
    });

    WebApi CORS problem

    WebApi CORS problem


    http://robertdunaway.github.io

    The Mashup is a learning tool that also serves as a bootstrap project for line-of-business applications.

    http://mashupjs.github.io

    Following the basic WebApi CORS setup, you will get good results. When the client server and WebApi server know about each other, i.e., the client server is configured in the WebApi server as Access-Control-Allow-Origin, everything works.

    The problem comes into play when the client is not hosted by a server at all as is true with hybrid mobile apps. In this case there isn’t an origin.

    Chrome rejects a wild card on Access-Control-Allow-Origin, making the solution nearly impossible unless you hijack the “Preflight” process and respond to the client with the client added to the Access-Control-Allow-Origin.

    Solution

    Here is the code to make this work.

    using System;
    using System.Web.Http;
    
    namespace AuthApiADSP
    {
        public class WebApiApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                GlobalConfiguration.Configure(WebApiConfig.Register);
            }
    
            protected void Application_BeginRequest(object sender, EventArgs e)
            {
                if (Context.Request.Path.Contains("api/") && Context.Request.HttpMethod == "OPTIONS")
                {
    
                    Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
                    Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
                    Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST PUT, DELETE, OPTIONS");
                    Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
                    Context.Response.End();
                }
            } 
        }
    }
     
    For completeness, my WebApiConfig.cs looks like this.

    using System.Net.Http.Headers;
    using System.Web.Http;
    using System.Web.Http.Cors;
    
    namespace AuthApiADSP
    {
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
                // Web API configuration and services
                // Enabling CORS Globally
                var cors = new EnableCorsAttribute("*", "accept,content-type,origin,x-my-header", "*") { SupportsCredentials = true };
                config.EnableCors(cors);
                //config.EnableCors();
    
                // Web API routes
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
    
                // By default Web Api wants to return XML.  (This is confusing because RestFul is based on JSON)
                // This line changes the default return type to JSON.
                config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
    
            }
        }
    }
    

    Background Info

    The challenge is all around CORS and its requirements for ‘Access-Control-Allow-Origin’. The server must maintain a list of allowed server urls and provide that list in the origin header attribute. Chrome looks at this header and if it doesn’t see itself in the list then it self imposes a restriction. This all happens in the preflight before the actual request for data occurs.

    Here is a better description of what is going on:
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS


    Because hybrid applications don’t have a server based origin, your first attempt to address this problem might be a wildcard setting Access-Control-Allow-Origin = “*”.

    This works fine in IE because IE ignores Access-Control-Allow-Origin completely but Chrome and Firefox don’t have it. Also, in Chrome and Firefox GET requests and POST without data all work. It isn’t until a POST has data that a preflight is initiated.

    NOTE: Another good reason to develop in Chrome. Had I developed solely in IE, I would never have discovered this problem until we went into production.

    Another good resource for learning about CORS:
    http://www.html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request

    Ultimately the only solution that addressed all my requirements was hijacking the preflight, “old school,” and returning the client as “origin,” satisfying Chrome and Firefox.

    I plan to update this later. Something about the solution I’ve come up with just doesn’t feel right. It feels too much like a hack. I’m always open to suggestions and new ideas so feel free to email me. robertdunaway@usa.net

    detectService

    detectService

    http://robertdunaway.github.io

    The Mashup is a learning tool that also serves as a bootstrap project for line-of-business applications.

    https://github.com/MashupJS/MashupJS

    The Mashup is home to a service called detectService.

    Introduction

    The detectService tracks the connectivity state of the application preventing unnecessary attempts to call for data and instead encouraging the client to retrieve cached data if available.

    A failed connection attempt to a WebApi takes 3 or 4 seconds to fail. With 3 or 4 WebApi calls required to load a page, this can be punishing to the user.

    The detectService short circuits failed attempts.

    detect()

    detect() is a function the cachedService calls before every attempt to retrieve data from a Restful service.

    The detect() never attempts to detect a remote service but uses any information collected to determine if a connection is available. If no attempt to connect to a particular service has been attempted then the default response is “true,” i.e., the detectService makes a positive assumption.

    When the detect() function is called, the remote service is added to a Heartbeat Monitor where it is periodically checked.

    failed()

    failed() is a function the cache calls when a remote service attempt fails. This tells the detectService that a service has failed and updates its status in case another call to the remote service is attempted. The remote service is added to a Code Blue List and a process begins checking the remote service for a heart beat.

    Internals

    Here are some of the internals and how they work. You shouldn’t have to deal with them but it might be useful to understand how they are intended to work.

    Heartbeat Monitor

    The Heartbeat Monitor tracks connectivity once a resource is accessed. If a connection attempt fails, then the connection is added to the Code Blue Monitor list and if not already started, the Code Blue Monitor starts.

    Successful heartbeats are logged to the console but not to IndexedDB.

    The log is checked every two hours and any logs more than a week old are removed.

    enter image description here

    Every 2 hours the log is checked and any logs over 1 week old are removed.

    Code Blue Monitor

    The Code Blue Monitor checks resources for connectivity much like the Heartbeat Monitor does but more frequently. The results, pass or fail, are recorded to the console window and IndexedDB.


    Battery level

    If available, the battery level is checked. If the battery level is less than or equal to 30%, all monitors slow down.

    logService

    logService

    Credit

    I first learned about the $log service and reasons to use it in Adam Freeman’s, “Pro AngularJS”.

    I stumbled across an easy to understand example and explanation in Vinny Linck’s “AngularJS: How to override $log implementation”: http://vinnylinck.tumblr.com/post/58833687265/angularjs-how-to-override-log-implementation

    The logService is significantly different than the work that kick-started it’s creation but it’s good go give credit whenever possible and these guys did a great job helping me along my way.

    Introduction

    The Mashup is a learning tool that also serves as a bootstrap project for line-of-business applications.
    https://github.com/MashupJS/MashupJS

    The Mashup is home to a service called logService.

    The logService overrides the $log service to extend it’s functionality.
    Overriding $log gives us
    • Console.[log, info, warn, error]
    • Instrumentation
    • Error logging
    • Debugging clients
    • Custom logging actions
    • Mobile friendly resource economy

    Console.[log, info, warn, error]

    Vinny Linck’s article, referenced above, probably explains this better. When overriding the logserviceadelegatetothelog service is provided via delegate.ThisisusedtorestoretheConsoleimplementationoflog.
    $delegate.log(argument);

    Instrumentation

    This implementation of logService decorates a logObject with several user session and environmental variables. Within the logObject itself are subject, app, mod (module), func, and status. Using these properties and calling $log the task of instrumenting applications is easy.

    The routeConfig uses the resolve function of each route to call the logRouteInstrumentation. Each route the user takes is logged to the indexedDB database and can be retrieved to see what parts of the application is used.

        var logRouteInstrumentation = function () {
            // -------------------------------------------------------------------
            // Instrumenting the application so we can track what pages get used.
            // -------------------------------------------------------------------
            var logObject = utility.getLogObject("Instr", "MashupCoreUI", "sessionLoad", "loadComplete", "UI-Routing", sessionService);
            // Additional or custom properties for logging.
            logObject.absUrl = $location.absUrl();
            logObject.url = $location.url();
            $log.log("UI-Routing to [ " + $location.url() + " ]", logObject);
            // -------------------------------------------------------------------
            // -------------------------------------------------------------------
        };
    

    Custom logging actions

    Error and Debug are custom implementations. The CodeBlueMonitor, shown in the code below, is an implementation of custom logging.

    Error logging
    There is, currently, no special implementation for Error logging but this is easily remedied once you’ve decided how you want your errors logged. You can “roll your own” solution or use a Log4net type implementation.

    Using $log to log errors is as simple as getting a logObject as shown above and setting the subject to “Error”.

    Below is the portion of the logService where you determine the action taken by subject.

    var subject = logServiceObj.subject;
    
    switch (subject) {
    
        case "Perf":
            {
    
                break;
            }
        case "HeartBeatFail":
        case "CodeBlueMonitor":
            {
                logDb.put({ name: 'heartbeat' }, logServiceObj);
                break;
            }
        case "Debug":
            {
                // This is a space for doing anything you need to do with debug data.
                // This can be saved to a separate IndexedDB database or table.
                // This can be sent to a WebApi or file.
                // Using the "subject" property you can add any custom behavior you need.
                break;
            }
        case "Error":
            {
                // Do something.
                break;
            }
    }
    
    
    Debugging clients
    Using the same approach as above with the subject set to “Debug”.

    Mobile friendly

    Environment
    The sessionService collects environmental information including the machine type (mobile/desktop), what kind of browser, version of browser, operating system, and where possible battery level.

    Battery level
    The battery level is not supported by all browsers but where supported it is collected, displayed, and used for throttling.

    Throttling
    Using the information collected about the environment certain tasks are throttled.

    When the environment is a mobile device the size the log is reduced in size.

    When the client machine is using a battery the battery’s level of charge determines some functionality. When the battery is 30% or lower then log management significantly decreases and the tolerance for stale data is increased. Reducing the amount of work the client must perform will help preserve battery power until the device can be recharged.

    How to use

    To get the same behavior of logdonothingdifferent.log will operate as before with the addition of the log entry being saved to the indexedDB database logServiceDB in the log table.

    To leverage the logService call the utility.getLogObject function passing in information you would like to see in a log. The object you get back will have the information you passed along with environmental and session information.

    Additionally, any property you add to the returned logObject will be included in logging.
    Example, the logRouteInstrumentation function uses all the standard logObject properties and adds two of it’s own.

        var logRouteInstrumentation = function () {
            // -------------------------------------------------------------------
            // Instrumenting the application so we can track what pages get used.
            // -------------------------------------------------------------------
            var logObject = utility.getLogObject("Instr", "MashupCoreUI", "sessionLoad", "loadComplete", "UI-Routing", sessionService);
            // Additional or custom properties for logging.
            logObject.absUrl = $location.absUrl();
            logObject.url = $location.url();
            $log.log("UI-Routing to [ " + $location.url() + " ]", logObject);
            // -------------------------------------------------------------------
            // -------------------------------------------------------------------
        };
    
    
    Looking at the code above you can see a simple pattern.

    1 . Call the utility.getLogObject function passing in the following:
    - subject
    - app (application name, you might have many applications)
    - mod (module name)
    - func (function name)
    - status (this can be any status value that makes sense. Often set to true/false but not limited.)

    var logObject = utility.getLogObject("Instr", "MashupCoreUI", "sessionLoad", "loadComplete", "UI-Routing", sessionService);
    Here is the interface for getLogObject
    mashupApp.service('utility_LogHelper', function () {
    
        var getLogObject = function (subject, app, mod, func, status, sessionService) {
    
    
    You might have noticed that this function is implemented in utility_LogHelper. To reduce dependency injection into your module a number of utilities, like this, will be exposed by the utility module.

    This implementation is straight forward and described here: https://github.com/MashupJS/mashupjs.docs/blob/master/docs/mashupCore/services/utilityService/utilityService.md

    2 . If you have any properties you’d like to add, do this next.
    logObject.absUrl = $location.absUrl();
    logObject.url = $location.url();
    
    
    3 . Call the $log([message], [log object]);
    $log.log("UI-Routing to [ " + $location.url() + " ]", logObject);

    Loosley coupled

    The goal of the logService is to be loosely coupled. Errors that occur in the logService should not negatively impact the user. Also, adding the logService to an application should be as simple as adding the script to the application. The problem is dependencies so when adding logService be sure to include the dependency modules.

    Removing the logService should have zero impact on an application so in that regard it is loosely coupled.