Check out our new training course on AngularJS for Flex Developers

Creating a ViewStack with AngularJS

I've spent the past two months diving into as many JavaScript frameworks as I could find time for. I am trying to determine what I can use in a book I'm writing entitled "Life After Flex". The book will be about converting a Flex application to JavaScript. I decided that I like a lot of things about AngularJS.

This is a tutorial on how to get ViewStack like functionality within an AngularJS HTML project.  My one caveat is that I am not yet an expert in AngularJS; so my understanding of what goes on under the hood may be wrong.

What is a ViewStack?

One of the common navigator components in a Flex Application is known as a ViewStack. It allows multiple views to occupy the same the same space in an application at the same time.  Only one view is displayed at the same time.  The Flex ViewStack does not inherently include any way to navigate between views because that is usually done through user interaction, either through something in the view or through a TabBar.  In the MX Component set; the TabNavigator combines a ViewStack with a ButtonBar for us.

One use case for a ViewStack might be to show a login screen in one view; and the main application screen as another view.  When the user successfully authenticates the application will tell the ViewStack to change from the login view to main application screen.  There are a multitude of uses for this component.  

I wanted to see if I could use a similar approach in an HTML5 based apps.  I was successful and you can view the simple app.

The Index.htm page

The index.htm or index.html is often the main page of an HTML site.  That is where this tutorial starts.  The page must import the angular.js framework and our custom JavaScript Code, in viewstack.js.  It also needs to set up some special AngularJS directives so that Angular will know where to display the custom views.

First, the HTML page imports the JavaScript files:

<html>
<head>
<script src="angular.js"></script>
<script src="viewstack.js"></script>
</head>

The body tag will define the AngularJS application:

<body ng-app="viewStackSample">

The ng-app is a special directive for AngularJS that ties code in our custom JavaScript file to the elements of the HTML document.  Next, a div will contain the view:

<div ng-view></div>

The div includes a special AngularJS directive named ng-view.  Finally the HTML is closed; finishing off the page:

</body>
</html>

The viewstack.js File

The index.htm page referred to a viewstack.js file’ and this file will include our custom JavaScript code.  This code will define two custom controllers; one for each view. It will also set up a config element called a routeProvider.  A route is, basically, a URL.  Based on the URL, the ng-view in the index.htm will decide which HTML page to display.  The approach reminds me of the early years of ColdFusion’s Fusebox framework.  Based on the URL; it determines which page will be displayed.

This is the routeProvider setup:

angular.module('viewStackSample', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/view1', {templateUrl: 'view1.html', controller: View1Ctrl}).
when('/view2', {templateUrl: 'view2.html', controller: View2Ctrl}).
otherwise({redirectTo: '/view1'});
}]);

The first line is creating an AngularJS module.  The module is named; viewStackSample which refers back to the value of the ng-app directive defined on the body tag in the index.htm. 

On the newly created module, the config method is called, and the ‘$routeProvider’ value is configured.  The $routeProvider is defined with a function.  The function has two “when” clauses and one “otherwise” clause.  This code, basically says:

  • If the URL has “/view1” in it then display the view1.html in the ng-view of the index.htm file.  Send it an instance of the View1Ctrl.
  • If the URL has “/view2” in it; then display the view2.html in the ng-view of the index.htm file.  Send it an instance of View2Ctrl.
  • If the URL has neither of those in it; then redirect the URL to ‘/view1’ and try again.

The two values View1Ctrl and View2Ctrl are defined the same viewstack.js file:

function View1Ctrl($scope) {
$scope.title = 'View 1 From Controller';
}

function View2Ctrl($scope) {
$scope.title = 'View 2 From Controller';
}

These two functions are, in essence, data stores, like a model class.  They each define a variable instance named title and put that on the $scope variable.  $scope is just a fancy way to send data into the view template.  These functions can do a lot more than just define data; they can also have functions which can call remote services or communicate with other aspects of the application. 

The view1.html and view2.html files

The view1.html and view2.html files are simple files.  This is view1.html:

<h1>{{title}}</h1>
<a href="#/view2">Go to View 2</a>

The h1 tag refers to the title variable defined in the viewstack.js file.  It uses double curly brackets, with the variable name inside it.  This is framework magic which tells the template to display the value of the title variable instead of the actual text.  It also includes a link which will change the URL to “#/view2”. 

The view2.html is almost identical to view1.html:

<h1>{{title}}</h1>
<a href="#/view1">Go to View 1</a>

The view2.html file has the same H1 tag; although a different value will be displayed.  The link goes back to “#/view1”.    

Final Thoughts

Loading this page will allow you to switch between the two views easily by clicking the link.  The code is not quite as encapsulated as the Flex ViewStack; however it is not too difficult and an approach like this can provide a lot of flexibility to an application.

Sign up for DotComIt's Monthly Technical Newsletter

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Salvatore Fusto's Gravatar Hey Jeff, very good post, thank you.
Now suppose i've 2 views: a page for a list of pictures and a filter (say a select) for picture type (say a category) , and a page detail: of course i can define a single controller for the list view, for both select and li ng- repeat's, but imho it should be more correct to have a controller for category select and another controller in list view, so having 2 controllers in the list page: can i assign 2 controllers in the routing, for the list view, and how?
regards
# Posted By Salvatore Fusto | 11/10/13 5:00 AM
Jeffry Houser's Gravatar Salvatore,

I'm sorry; I'm not able to envision your architecture based on your explanation; so I don't understand your question. You may try posting something more detailed to StackOverflow.
# Posted By Jeffry Houser | 11/10/13 5:26 AM
Salvatore Fusto's Gravatar suppose we have a page to fetch a list of something with a filter input; you can have a single ctrl for the page
<div ng-app="myApp" ng-controller="myCtrl">
<input...ng-model='myFilter'... >
<ul >
<li ng- repeat="... !filter : myFilter>{{..}}</li
</ul>
</div>
or you can have 2 ctrl's:

<div ng-app="myApp" ng-controller="myCtrl">>
<input...ng-model='myFilter'... >
<ul>
<li ng- repeat....>{{..}}</li
</ul>
</div>
one for the filter, and one for the repeated, and filtered, items, 2 ctrl for the page: in this case how can you implement the view route? how do you assign 2 ctrl's to one page in $routeProvider?
regards
# Posted By Salvatore Fusto | 11/11/13 12:49 AM
Eric Cancil's Gravatar The main issue with this, is that unlike a viewstack ng-view actually recreates the view / scope everytime its traveled to.
# Posted By Eric Cancil | 3/7/14 9:38 PM
Pat B's Gravatar Exactly what Eric said. And because of this, Angular, in its pure form, can't do a true viewstack because everything is reset as you move between pages/views. The "solution" is to persist data about the view in a service and then rebuild the view each time.

Since this is unacceptable to me, I've chosen to not use Angular's ng-view and instead just roll my own container stack.

However, I would really like to see if anyone knows of an angular plugin that allows you to persist the views.

The answer here from Igor: https://groups.google.com/forum/#!topic/angular/nc...

suggests that normally you don't want to have them persist because of the slowdown in performance and such you could see because all those invisible views are still listening. He is correct, but in my case that's exactly what I want. I want to be able to allow the user to start editing a form, then leave and come back right where they left off. And if an event is broadcast, I want all the open views to hear it and respond. If they are garbage collected, they don't respond. :)

So - has anyone seen anything out there that is more ng'ish? A true ngViewStack component?

@Eric - good to see you around again. You off the Flex-candy and into JS-land full time now?
# Posted By Pat B | 4/16/14 5:13 PM
All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.