Thursday, 3 December 2015

Welcome

Welcome to the first installment of Angular Ingots!

I've been consulting for almost 20 years and in that time I've had to learn a lot of languages and frameworks. Some good, some...not so much. Some frameworks and languages stand the test of time, some disappear without a trace.

I've been very impressed with AngularJS and think it will be with us for a long time to come...if my crystal ball is not mistaken.

My goal with this set of posts is not only to capture nuggets and tidbits of angular goodness as I find them but also explain them in a clear fashion so you're able to digest them easily.

Ingots is a nice name for these nuggets, especially for Angular, if you catch my drift. ;)

While the angular documentation isn't bad I sometimes feel I am getting information and concept overload.

I plan to keep each post's focus small so that I can give each concept its due and explain it fully.

Let's start with Angular Routing. Something I found yesterday in the course of having to replumb an angular application that is evolving rapidly in my current agile development project to incorporate the business' changing and expanding universe of functional requirements (apologies to Eric Idle).

Approach 1 - Marrying client & server-side routing

By client-side routing we mean Angular routing. By server-side routing we mean ASP.NET MVC routing.

If you're not careful you can get in a knot and trip over yourself. A few things need to be lined up to make it work as advertised.

At a high level, server-side routing is the url path components BEFORE the # and client-side routing is the url path components AFTER the #. Let's dig a bit deeper.

Start at the beginning

Let's look at the structure of the URL first to get the 50,000' view. I'll use single squiggly brackets just to denote the components, its not angular notation.

FQDN/{controller}/{action}/#/{angular-route}

The FQDN is the Fully Qualified Domain Name.

The {controller}/{action} is the server-side routing picked up by RouteConfig.cs.

The {angular-route} is the client-side routing picked up by the $stateProvider.state clauses in app.js.

Server-side routing

RouteConfig.cs

...
routes.MapRoute(name: "Home",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "MyApp", action = "Home", id = UrlParameter.Optional });
...

MyAppController.cs

public class MyAppController{
    public ActionResult Cmp() {
        return this.View("~/App/Main/views/layout/cmp/layout.cshtml");  
    }

    public ActionResult Generic() {
        return this.View("~/App/Main/views/layout/generic/layout.cshtml"); 
    }

    public ActionResult Home() {
        return this.View("~/App/Main/views/landing/landing.cshtml");
    }
}

In the above example we have a landing page that has no layout of its own. Any URL containing /cmp/ in the {controller} component gets one layout, and any URL containing /generic/ in the controller component gets another.

N.B. It is important to ensure the returned ActionResults start with "~/". This ensures it works with sites deployed to virtual directories

Client-side routing (app.js)

The {angular-route} matches against a state entry in your app.js. The application navigates internally to the specified templateURL.

You can then navigate from one route to another with an <a> tag like so:

<a href="myapp/generic/#/somewhere">Navigate</a>

After a couple of hours of being rather pleased with myself I realised that the only function MVC server-side routing was now giving me was the ability to control the layout, and that I'd been a bit silly really.

While Approach 1 will work and might be useful to know if you have technical limitations imposed on you by a manager, it is far more preferable to embrace the caow and adopt total client-side angular routing.

Approach 2 - Angular Routing

With Angular routing you can do "nested views" which is the angular word for ASP.NET Master Pages. Again, like most things there a couple of things you need to do to make it work as you expect. If you have been reading my other blog Jenkins Heaven you've probably heard me say 'everything is easy if you know how'.

Dot notation in app.js states

To specify a layout you just create a layout.html (in a folder structure that makes sense for you) holding your header and footer fragments. Between the header and footer in your layout file you must also have a <div ui-view></div> fragment. This is where the child page will appear.

The first state, of (your first master page) in your app.js file will just be the name of your master. Let's call it myfirstlayout

The first of probably many child states has to follow the dot notation convention like so: myfirstlayout.myfirstchildstate

Thus, when you use either a $state.go() or an <a> tag to navigate to your new child state you have you specify "myfirstlayout.myfirstchildstate".

Placement of master and child states is important. It's good practice to place all the child states under the master to which they belong before the next master state.

As you navigate from one master page to another (which may represent different functional branches of your application) you will probably at some point want to go to the "home page" of that functional branch which means specifying the "root" url which in our example is /myfirstlayout.

$state.transitionTo()

Unfortunately, the templateUrl of this state specifies only the layout itself. You will probably have an initial child state that has the actual content you want to show the user that sits inside the layout (or Master Page). You can do this by adding a $state.transitionTo("myfirstchildpage") in the controller of myfirstlayout. If you forget to add this transitionTo(), you will probably wonder why your seeing only the layout.

Following this convention and these rules you should now be able to think about the structure of the your application and build up a taxonomy of master and child pages.

I hope you found this helpful. As always, I welcome your comments and feedback.

Till next time...