Chapter 5. View composition and layout

This chapter covers

  • An introduction to layout design
  • Steps for composing views
  • A guide to designing complex routes
  • Insight on how to deal with nested and sibling views

So far you’ve learned some of the basic nuts and bolts of creating a single-page application. During this journey, you learned how to modularize your code and how to use the power of an MV* framework to create the views that your users see and interact with. You also discovered the vital role routers play in your application: they not only allow the natural navigation features of the browser to work with your single page but also provide a way to configure the functionality and views associated with your application’s URLs.

To this point, however, you’ve grasped particular concepts rather than the bigger picture. Now that you have some tools in your SPA toolbox, let’s zoom out and tackle the overall design process for creating a single-page application. Designing a successful SPA is a little like learning to speak a new language. You’ve learned the basics—the vocabulary and grammar—but you still haven’t mastered the dynamics of holding a conversation.

In this chapter, you’ll walk through the design of an SPA, from the visualization of your layout to the transformation of requirements into a real, working application. You’ll learn to design the layout with views instead of pages, to design routes to connect the dots, and then to pull it all together with working code. Even though you understand the underlying mechanics of an SPA, you’ll galvanize what you know by focusing more on the overall process. Along the way, you’ll also learn how to deal with lifelike scenarios, such as more-complicated layouts and routes that result in multiple views.

5.1. Introducing our project

The project for this chapter is the creation of an online tool for a fictitious medical supply company. We’ll pretend that this SPA will be used by sales reps to track the status of their clients’ orders. Because this chapter’s focus is on the overall design process, I’ll make the project’s layout a little more complex than in previous projects. In the application, you’ll need to display several categories of information for a given route, such as customer data, order history, and the client’s addresses for billing and shipping.

We’ll talk more specifically about the details of the project later, when we get into the design process, but first take a glimpse at the final product (see figure 5.1). This gives you a picture of what you’re building as you progress through each phase of the project.

Figure 5.1. Our sample project tracks the status of orders for a fictitious medical supply company and includes a layout that’s more complex and diverse than you’ve previously dealt with.

Before we begin, though, let’s talk about some basic layout design concepts. I want to make sure we’re on the same page with familiar concepts and present any new concepts before continuing.

5.2. Reviewing layout design concepts

Our discussion of SPA concepts thus far has been narrowly focused. Let’s pull back now and review the SPA landscape from a holistic viewpoint. This review will also bring to light some new details that you’ll need to grasp when you start this project.

5.2.1. Views

When you design an SPA’s views, you’re creating the individual pieces of the overall SPA puzzle. Each piece provides a particular experience for the user, whether it’s merely displaying data or providing controls for user input. Generally speaking, you design views on two levels. At a basic level, you design the view itself. At a broader level, you’re more concerned with how the view fits into the overall architecture.

From within the view, your design efforts are concentrated on tasks such as displaying data and possibly adding interactivity via JavaScript. As you’ve learned, it’s the MV* framework that helps keep the various parts of your application’s code separate but working together as a team. HTML elements and bindings combine to form the basis of the view’s design. You can further contribute to the design of the view by applying styles to it via CSS (see figure 5.2).

Figure 5.2. The template’s HTML forms an initial structure, but CSS refines its look and feel.

When it comes to the bigger picture (the layout), you have to think about how each view will be positioned with respect to the other views in a feature. To position a rendered view in a particular area of the screen, you turn to a construct called a region.

5.2.2. Regions

I briefly discussed the concept of a region in chapter 1. Although certain view engines have their own notions of this term, in this chapter I use region to mean an area of the screen that’s been designated to contain one or more views. A region can be defined using semantic elements if you’re using HTML5 (see figure 5.3) or an element such as a DIV if you’re not. These types of elements are ideal, because they can remain invisible to the user but can be used to define physical space within the UI.

Figure 5.3. Regions give you a physical area in the UI where your views can be displayed. Within a region, views can be fixed or dynamically swapped.

To define a region’s dimensions, as well as its aesthetic relationship with other regions, you can use Cascading Style Sheets (CSS). In figure 5.4, you can see an example of applying styles to regions to achieve a certain layout.

Figure 5.4. CSS is used to define the physical attributes of the regions in your layout, as well as to define their relationship with other regions in the UI.

This example positions two regions side by side by assigning a simple float property to the regions. For a simple 2×2 layout, you can float one region to the left and the other to the right. After placing the regions, you can make decisions about the views they’ll contain.

You can also assign other CSS properties to your region to further enhance the effect on your layout’s design, such as width, height, padding, borders, margins, and background-color, to name a few. Any property applicable to the type of element you choose is fair game.

The process of deciding on the size and shape of regions and the arrangement of views within those regions to arrive at a particular layout is called view composition.

5.2.3. View composition

As part of the design process, you arrange, or compose, views in a certain way to form the UI’s layout. In this regard, you can think of view composition as both art and science. On one hand, you have the technology that helps create the views and display them in the UI when a particular route is carried out. On the other hand, there’s the creative aspect in which you make a subjective decision about where regions will be placed and how a view, or a set of views, will be arranged within them.

Although this process is called view composition, regions play an equally important role in the creation of a layout, as you’ve seen. Regions house the views that are rendered by the MV* framework. Given this, you can say that for all practical purposes, regions are the bounding boxes that frame views within your layout. That’s why view composition encompasses both views and regions, hand in hand.

To illustrate how regions can affect view composition, take a look at figure 5.5. Here you see the same views that you saw earlier in figure 5.3. By reconfiguring your regions, you can display exactly the same information with an entirely different layout. In turn, this has a direct impact on your SPA’s look and feel.

Figure 5.5. How regions and views are configured impacts view composition and ultimately the layout.

How your regions and views are arranged is completely subjective, tailored to the goals of your project and your design preferences.

5.2.4. Nested views

An important point to bear in mind is that the use of regions doesn’t have to be confined to the SPA’s shell. Regions can also be employed within a view to nest other views (see figure 5.6).

Figure 5.6. Regions can also be used in views, if you need to nest a view (or views) inside another view.

Nesting views can dramatically increase the complexity of your design, but that’s sometimes necessary given the nature of the feature you’re building. You can also configure the application’s routes so that your design is properly reflected in the UI when the application’s state changes.

5.2.5. Routes

As you saw in chapter 4, the way you configure routes affects the application’s state. This includes the state of the UI. This is why route configuration can also be an important aspect of your layout’s design.

While you were learning about routing and how routers work, you worked with only simple examples, as in figure 5.7. In this type of route, each resulting view occupies the entire destination region. Designing a route like this is pretty cut and dry.

Figure 5.7. Example of a simple route

But as you just learned, the regions you’re targeting can be placed in any number of places on the screen. This can lead to some interesting designs. You could, for example, have multiple regions positioned next to each other and display multiple views for a given route (see figure 5.8).

Figure 5.8. Example of a route with multiple views

When you design routes that result in complex region/view configurations, things can become difficult to manage. For complicated layouts, if you’re not using a one-stop-shopping framework with robust routing and view management built in, you might want to consider adding a library to your arsenal that manages the application’s state specifically for the MV* framework you’re using. The next section covers some of the pros and cons of bringing a view management library onboard and points out some of the options available.

5.3. Considering alternatives for advanced composition and layout

There are a few reasons why you might need a framework with a more elaborate set of features built in. The most obvious one I’ve already mentioned is the ability to deal with complex layouts, specifically those with multiple or nested views.

If a given MV* framework doesn’t have a particular feature built in, you end up writing code to bridge the gap. Frameworks exist that expand upon the original MV* framework, though. They have the common goal of making certain complex tasks, such as advanced view composition and complex routing, as easy as possible while trying to reduce the amount of boilerplate code you have to write. This alone is a fairly compelling reason for considering outside help.

Another reason to consider this type of framework is the extra set of features it may bring to the table, such as events, messaging, and built-in objects for easy layout creation.

Finally, with complex designs, you have to deal with the view’s state. Remember that when you display a view (or views), you’re also interacting with the MV* framework. You rely on the MV* framework not only for the sake of handling the data and interactivity for the view but also to manage the view’s lifecycle.

If you think back to our evaluation of several styles of MV* framework, each one approached the binding and rendering process differently. With Backbone.js, for example, the normal process destroys the previous view and re-creates it from the template with fresh data. In an MVVM-style framework, such as Knockout, binding is done once and the view stays active. You merely interact with the ViewModel as needed.

Each framework has specific parts of the lifecycle that need to be addressed, such as when a view is rendered, shown, hidden, and (if applicable) destroyed. Certain libraries/frameworks either take care of this for you or provide hooks for you to use.

As I mentioned, most of the large, end-to-end frameworks have most or all of these concerns addressed. If you’re using one that doesn’t have these capabilities built in, you might try one of these libraries/frameworks instead. But first you should consider various factors. No dependency should be added without due consideration. So before you jump in with both feet, here are a few of the pros and cons.

5.3.1. Pros

Here are some of the good points:

  • If a library/framework extends or enhances a particular MV* framework, it’ll be designed around the nuances of that framework right out of the box.
  • These libraries/frameworks are written by people who have expertise in a certain MV* framework and understand the difficulties and pitfalls of managing complex tasks in that environment.
  • You don’t have to worry as much about trying to cobble together various other libraries and frameworks yourself.

5.3.2. Cons

Here are a few downsides to consider:

  • You’re at the mercy of the developer(s) of the software for bug fixes and upgrades.
  • Because an outside library/framework isn’t your code and isn’t part of the core MV* framework, you may find debugging application errors more difficult.
  • The author(s) might, for whatever reason, abandon the library/framework. This leaves you dependent on obsolete software.

If you need a more robust set of features from your SPA solution, table 5.1 presents a few options you might consider. Several of these are powerful SPA frameworks in their own right and include many more features than merely view/layout management. This isn’t an exhaustive list, but it does represent some of the choices available at the time of this writing.

Table 5.1. Frameworks with built-in features for advanced composition and layout

Framework

Options for more-complex composition and layout tasks

Knockout Durandal (http://durandaljs.com)
Backbone.js Marionette.js (http://marionettejs.com) Geppetto (https://github.com/ModelN/backbone.geppetto) Chaplin (http://chaplinjs.org) Vertebrae (https://github.com/hautelook/vertebrae) LayoutManager (https://github.com/tbranyen/backbone.layoutmanager) Thorax (https://github.com/walmartlabs/thorax)
AngularJS AngularUI (http://angular-ui.github.io), part of AngularJS but offered as a separate download
Kendo UI Built in
Ember.js Built in

You can apply the same acceptance criteria to these frameworks as you did when selecting the MV* framework itself, in terms of factors such as learning curve, bugs and fix rate, documentation, maturity, and community support.

Now that you’ve reviewed basic design concepts and considered possible software solutions to creating a complex layout, let’s move on to our project. Because I’ll be using AngularJS to illustrate, I’ll be leveraging the AngularUI component. As mentioned in table 5.1, it’s part of the overall Angular software, but it must be separately downloaded. I’ve also included it in this chapter’s code available for download online.

5.4. Designing the application

Let’s begin with a description of what you’re going to design: an online reporting tool for a medical supply company. This tool will help the company’s sales reps track the status of orders, view order history, and view information about clients. The following is a list of features you’ll pretend you’ve been asked to include:

  • Provide a selectable list of the sales rep’s customers.
  • Display all open orders by default.
  • When a customer is selected, display company info, contact info, and order history.
  • Initially hide the customer’s billing and shipping information but make it viewable on demand. In addition, assign customers their own URLs so the user can traverse quickly to where they were previously.
  • Include links in the header to the company’s current campaigns, promotions, and the application’s help file.

5.4.1. Designing the base layout

One of the first things you have to do is to decide on a basic structure for the application, just as you would with a regular web application. This is often called the base layout. Will it be a master-detail kind of application? Or would having a side column for navigation be better? Do you need a footer? Many choices would work.

You’ll use a traditional three-part base layout for this project. You can put your links at the top, the list of clients in the navigation bar at the bottom left, and display the results of your routes in the main content area at the bottom right. You’ll create this structure in two steps.

First, you’ll divide the screen into a header and a main body. Figure 5.9 illustrates this step.

Figure 5.9. The base layout begins with a top region and a bottom region.

Next, you’ll divide the main region into two parts: a region on the left for navigation and a region on the right to display your content (see figure 5.10).

Figure 5.10. You’ll finish up the base layout with a region for navigation and a region for content.

At this point, your base layout source code looks like the following listing.

Listing 5.1. Base layout

You’ll use CSS to mold these regions into the general shape you want for the base layout. To start, you’ll give your header some height and a white background (see the following listing).

Listing 5.2. Header region CSS
header {
   height: 15%;
   background-color: #FFFFFF;
}

You’ll also apply styles to your side navigation region and content region, floating your navigation region to the left so the two regions sit side by side. And you’ll define the proportions of the regions by allocating a portion of the width to the navigation region (see the next listing).

Listing 5.3. Navigation region and content region CSS

After all your styles have been applied to the base layout, it looks like figure 5.11.

Figure 5.11. The base layout with styles applied

Having finished the basic layout for the application, you can move on to adding its content.

5.4.2. Designing the default content

Now that you have some regions that you can add content to, you can design views and a default route. Because your header and navigation views will be fixed (only their content will change), you’ll design those two first.

The header

The header view is fairly simple. It has a logo, some links, and the name of the user who’s logged in. Because it’s a fixed view, you don’t need a route for it. This view will never be swapped for another view. So all you need to do to get it onscreen is include it. In AngularJS, you do this via an attribute added to the header region’s DIV. Note, however, that the MV* framework you choose may require a different method to include a view.

<div
id="header"
ng-include src="'App/components/customerorders/partials/header.html'">
</div>

The following listing provides a glimpse of the source code for your header view.

Listing 5.4. The header view

The Home link provides a way back to your default content, and the other links satisfy the rest of the requirements for your header. With the view rendered and some styles applied, your header now looks like figure 5.12.

Figure 5.12. The layout’s header after styles have been applied

With a header in place, you can design the main navigation view. For this application, the links in the navigation view will consist of a the company’s current customer list.

Navigation

The list of customers in the customer list view will be just as easy. It’s also fixed, so no route is needed. With an include statement, it’ll load with the application.

<div
id="navigation"
ng-include src="'App/components/customerorders/partials/customerList.html'" >
</div>

To create a link for each customer in your client list, you’ll use a binding made for iterating over lists. This is the same kind of operation covered in chapter 2, when you learned about templates and binding. You’ll also use the special link attribute from your view management component I just mentioned (ui-sref) to execute the customerInfo route when a link is clicked (see the following listing).

Listing 5.5. Navigation view

Note that your companion view manager allows you to pass route parameters as you did with the core AngularJS router. In this example, customerID is the name of the variable, and customer.custNum represents the data assigned to the parameter when the ng-repeat attribute stamps out the information from the template.

With the view rendered to the screen, you can use CSS to turn your unordered list into what looks like a group of clickable panels. The following listing is a portion of the style attributes applied (the rest can be viewed in the code available for download).

Listing 5.6. Navigation styles applied

After your navigation view is rendered, your default content will look like figure 5.13.

Figure 5.13. Your layout after both the header and navigation view are rendered

With the fixed portion of your default content completed, you can work on the dynamic area.

The default route

According to our requirements, the user should be greeted with a list of open orders by default. For this, you’ll create a default route. The view resulting from this route will be what a user sees after first entering the application or after clicking the Home link.

Listing 5.7 shows the configuration for this route. As I mentioned earlier, you’re using the AngularUI router. The syntax is slightly different from the router that comes with core AngularJS, but it shouldn’t feel totally alien. This router is much more powerful and was deliberately modeled around states rather than URLs. It also supports multiple views, both nested and parallel.

Keep in mind that this syntax is specific to the AngularUI router. Don’t worry, though; the basic concepts carry over to most frameworks that support advanced state management.

Listing 5.7. Configuration for the default route

As you can see from the template that’s being used, the requirement to show all open orders by default will be met when this route completes. The keyword otherwise denotes the default route.

To specify that the content region should be the recipient of the view rendered by this route, the attribute ui-view is applied to the region’s DIV:

<div id="content" ui-view></div>

No matter what view management solution you’re using, you’ll need to identify the region the view should be inserted into. Consult the documentation to see how to do this for the view manager you’ve chosen.

In your open orders view, you’ll once again use a repeat binding directive to tell AngularJS to repeatedly stamp out rows in a table for each order that’s still open (see the following listing).

Listing 5.8. The row template from our view for open orders

With the meat of your default content filled in, let’s take a peek at what the screen looks like at this point. You saw the opening content of your application at the beginning of the chapter, but let’s look at it again now that you have all the pieces in place (see figure 5.14). It’ll give you a picture of what you’ve accomplished so far.

Figure 5.14. Your layout after the default route displays the open orders in the content region

With your default content complete, let’s move to the last leg of your application: displaying the appropriate customer information for a selected customer. This time, the view composition will be more complicated, so you’ll see the usefulness of your view manager component.

5.4.3. Using a view manager for complex designs

Until this point, the application hasn’t been anything your regular router and MV* framework couldn’t handle. Now, however, multiple views will need to be simultaneously displayed in your content region when a customer from your navigation view is selected.

The design for this feature includes a main customer view that you’ll configure in your customer information route. The complexity, though, is that this new view will contain regions of its own for the placement of the customer’s contact information and order history. These regions, in turn, have views of their own. You’ll configure them within the same route as the main view. You can pretend that each of the three views in this route is complicated enough that it needs to be developed and maintained separately.

As you may recall from earlier in this chapter, the placement of regions isn’t confined to the shell itself. The use of regions within a view is another view composition device you have at your disposal to design the most appropriate layout for a given set of requirements. Figure 5.15 illustrates this arrangement of the regions and views.

Figure 5.15. To compose the customer information feature, you’re placing additional regions within the main view itself to include the related but separate contact and order history views.

With a mental picture of what you’re going to do, let’s see how to use the extended routing and view composition abilities of the view management component you’ve chosen.

Let’s begin with the route. After you wrap your head around how to configure a route like this, understanding the rest of the code will be easier. The following listing shows the configuration of your route with multiple states.

Listing 5.9. Customer information route with multiple states

Again, if you’re using a different kind of view manager, the syntax will be different, but the concepts will be the same or relatively similar. Because this route needs a little more explanation due to its complexity, we’ll analyze the syntax of the AngularUI route you just saw (at a high level) to make sure you understand it.

The route explained

This kind of route has the same things a normal route has (path, view, functionality)—but times three. Here’s what’s happening with this type of route configuration:

  • The default view for this state doesn’t need to be named.
  • The absolute name of each view contains the ID of each region and the name of the state, concatenated using @. (You’ll see the source for the main view in a moment.)

You’ll also notice that each view in the view list can have its own functionality. This is optional but handy because the main reason for this kind of route is to have a feature with views that are related but developed separately. Moreover, thanks to the expanded capabilities of your view manager, you’re able to share the route’s parameter with each inner view and its functionality.

The route’s main/outer view

Now that you’ve seen the route, let’s look at the source for its main view (see the following listing). This view is important because it contains bindings for its own information, as well as the regions where the other two views will be rendered.

Listing 5.10. Main view from the customerInfo route

The source of the two inner views is typical of what you’ve already seen. They display data about the customer through the use of bindings. The route parameter, as I mentioned, is shared, and the inner views can also use it to locate the correct data entries. Their source can be found in the complete online source code.

Let’s look at a screenshot of the application to check your progress (see figure 5.16).

Figure 5.16. What your three views look like when displayed in the content region

The screenshot shows the three rendered views with their styles applied. It’s impossible to tell where each rendered view is from the screenshot, but that’s part of the design. Although these views are developed and maintained separately, the content appears like one seamless page to the user. Look at the screenshot again, but this time I call out the location of each view in the customer information route (see figure 5.17).

Figure 5.17. The result of the customer information route with each view highlighted

One more part of the project, which I haven’t discussed yet, requires you to use nested views.

5.4.4. Creating nested views with their own states

To satisfy your requirement for on-demand billing and shipping information, each having its own state, you’ll once again call upon your view management software. The following listing shows the routes for these views.

Listing 5.11. Routes for billing and shipping info

Notice that child routes are defined with dot notation with respect to another route in the configuration file. In this case, you’re also defining the customer information route as the parent route.

Now you need to know how to construct links to get to these child routes. Here’s where a bit of magic happens. To target these child routes, you also use dot notation in the link. So let’s now add on to the main customer view that you saw earlier and put in these additional elements. The following listing shows the updated source for the main customer view.

Listing 5.12. The main customer view with billing and shipping added

Once again, the syntax you’ll use may be different, but let’s talk about this addition at a high level so you can get the main idea. You’ve added a NAV (navigation) element to the customer information view to display either billing or shipping information. The other element is an empty SECTION where the resulting views from the route will be displayed.

One other thing to note, just for the sake of understanding the code, is that with AngularUI’s router, unnamed regions become a catchall for the route. Contact and History are named, so those views will be assigned directly to the regions of the same name. When no name matches a region, a route’s views are targeted to the first unnamed region. This is specific to AngularUI, but it should be noted to avoid any confusion about this example.

Now, for a final touch, you’ll add a few CSS attributes to the links so they appear more like buttons and you’ll have a way to see the on-demand content. Figure 5.18 shows both button-styled links and what one of the child views looks like when the route finishes.

Figure 5.18. The nested shipping view with its own URL

Now, users can click the appropriate button to show the billing or shipping information on an as-needed basis and then click the browser’s Back button to return to the state the SPA was in previously.

5.5. Chapter challenge

Here’s a challenge to see what you’ve learned in this chapter. Pretend you’ve been hired to create a website for a local landscaping company. The company wants a view for each of its services: lawn maintenance, landscaping, shrub pruning, and exterior home staging. The company also needs a products view to display the custom decks, fountains, and swimming pools they build. You can keep the details for each view as simple as a description, such as “landscaping view,” or make it more lifelike with simulated content.

Divide the screen into three regions: one for the header, one for navigation, and one to display content. Each service will have its own view and corresponding navigation link. The product view also has a navigation link, but its view is subdivided into its own product header and product detail regions. The product header should have links to a view for each product, which will display in the product detail region.

5.6. Summary

Whew, that was intense! You made it, though. Let’s do a quick recap:

  • In an SPA, you use the view composition process to create a layout.
  • The semantic elements that you’ve used as regions occupy physical space on the screen. CSS is used to style and position them. Their arrangement affects how views are displayed, which directly impacts the design of the layout.
  • The first, underlying set of regions that give the application its basic shape is called the base layout.
  • Regions can also be added to views when the layout’s design calls for multiple and/or nested views.
  • Routes that result in complex view and region combinations can be difficult to manage. Frameworks/libraries with robust routing and view management capabilities abstract the complexity by getting you to think in terms of the application’s state.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset