SQL Reporting Services, ASP.NET MVC, AngularJS, F#, Life after Parse
MAY JUN codemag.com - THE LEADING INDEPENDENT DEVELOPER MAGAZINE - US $ 5.95 Can $ 8.95
2016
Create Mobile UIs with Xamarin.Forms
Explore and Embrace AngularJS 2 Implement Lean Controllers in iOS Apps Tame the HTML5 Audio Control
TABLE OF CONTENTS
Features 8 Parse is Set to Shut Down. Now What? It seems like only yesterday that Facebook acquired Parse so that developers could focus more on the frontend than the backend of their apps. In January 2017, they’re shutting Parse down. What’s a developer to do? Jason tells us what our options are.
Jason Bender
12 The Journey to Angular: Part 1 When you wrap your JavaScript code into a closure, you won’t have bugs caused by unnecessary variables. Paul shows you how to do this using a templating tool called Mustache, which will help you get closer to coding in Angular.
60 Implementing Lean Controllers in iOS Applications Have you been struggling with sluggish apps? Perhaps all that’s needed is a little tightening of the code. Mohammad makes your apps lean and mean again with a simple ToDo example.
Mohammad Azam
64 SQL Server Reporting Services: Eight Power Tips SQL Server’s Reporting Services is still the best workhorse for getting data to your users. Kevin tells you how to take advantage of some of its more subtle points.
Kevin S. Goff
Paul D. Sheriff
18 AngularJS 2 Sahil shows us a few nifty aspects of AngularJS 2 that help experienced and novice developers alike. AngularJS 2 is still in beta, but he says that you’ll love it even so.
Sahil Malik
Columns 74 Managed Coder: On Responsibility Ted Neward
22 Taming the HTML5 Audio Control If you want your Web page to hold audio recordings that play when users want them to, you’ll want to read John’s article. He shows you that it’s not only quick, but it’s easy!
John V. Petersen
28 Why F# Code Functional programming is all the rage and Microsoft’s foray into the functional world is called F#. Rachel introduces you to this first-class functional language with the ability to harness the rich .NET ecosystem.
Rachel Reese
Departments 6 Editorial 10 Advertisers Index 73 Code Compilers
34 Arranging Views with Xamarin.Forms Layout There’s no longer a simple answer to what sort of device your page will be viewed upon. Walt examines the options and shows you how to make sure that yours will look great on anything, old or new.
Walt Ritscher
44 Integrating ASP.NET MVC and AngularJS Miguel looks at organizing styles and integrating technologies to make one spiffy new kind of app with multi-SPA layers, and you don’t have to throw away your Microsoft experience to do it.
Miguel Castro
US subscriptions are US $29.99 for one year. Subscriptions outside the US pay US $44.99. Payments should be made in US dollars drawn on a US bank. American Express, MasterCard, Visa, and Discover credit cards are accepted. Bill me option is available only for US subscriptions. Back issues are available. For subscription information, send e-mail to subscriptions@codemag.com or contact customer service at 832-717-4445 ext 028. Subscribe online at codemag.com CODE Component Developer Magazine (ISSN # 1547-5166) is published bimonthly by EPS Software Corporation, 6605 Cypresswood Drive, Suite 300, Spring, TX 77379 U.S.A. POSTMASTER: Send address changes to CODE Component Developer Magazine, 6605 Cypresswood Drive, Suite 300, Spring, TX 77379 U.S.A. Canadian Subscriptions: Canada Post Agreement Number 7178957. Send change address information and blocks of undeliverable copies to IBC, 7485 Bath Road, Mississauga, ON L4T 4C1, Canada.
4
Table of Contents
codemag.com
ONLINE QUICK ID 00 EDITORIAL
Creative Fear The SXSW Interactive conference has a section of the conference dedicated to the concept of mentorships. It works like this: As a mentor, you meet with attendees who sign up for seven-minute meetings. These meetings are free form and the attendees can ask whatever they want of their mentor.
My mentorship sessions tend to revolve around three tent-pole areas of my career: writing, being a software developer, and being an independent consultant/business owner. It’s an honor and privilege to be a mentor. In my 20+ years as an independent software developer I‘ve been a mentor to many of my colleagues and friends and I take the job seriously. I’ve been a mentor at SXSW for a few years now and for the first few years, no-one showed up for their scheduled session. Admittedly, this was a bit of a disappointment. Some of my friends asked why I continued to participate in the program. My answer was simple: If someone shows up, the seven minutes I give him or her could be life-changing. Not to be egotistical, but I firmly believe that seven minutes of good advice, or encouragement, can make all the difference in a person’s life. This year, I might have had that affect. My last appointment of the day was a software developer from Boston, who, with some other software developers, was about to quit the world of W2 employment and “hang a shingle.” This software team is putting together a set of tools for building customized chat bots. It was an interesting concept and looked to be well-architected. I gave him the same advice I’ve given other colleagues over the years: get a CPA, set up an LLC or corporation with the help of a lawyer, make sure the ownership of the intellectual property is properly documented, etc. After having a rich conversation about his business and architecture, we chit chatted for a while and I told him something that set him back a bit. I told him that he’s way ahead of so many other people. The simple act of showing up for our mentor session set him apart from 75% of the other people who failed to show up. That commitment to making himself better, accompanied by the leap it takes to start a new endeavor said much more about the person than the business idea. It takes guts to set out on your own and it takes smarts to ask for advice about starting out. He had both of these elements, guts and smarts. I hope he succeeds in his endeavors. This leads me to the subject I want to really talk about: Getting over fear in the creative process. Being creative can be exhilarating and scary at the same time.
6
Editorial
I had a great conversation with Melanie Spiller (the person who makes our authors, including me, sound so good) about the aspect of fear in creative endeavors. Melanie and I both have interests outside of the software development/technology realm. Melanie’s an accomplished singer as well as a great writer, and I have a passion for film, especially the creative process surrounding film’s creation. There’s another trait that we both share as well: The fear of turning artistic fantasy into reality. In my case, I’m kind of scared witless to create my own film. Numerous cautionary tales and random trepidations creep into my mind: What if I don’t like creating my own movie? What if I can’t find people to help me? What if I make a movie and it sucks? And Melanie tells me that she’s revised her book 11 times (that’s right, the whole shebang, rewritten from start to finish 11 times!) over numerous years and with the help/ advice of various writing groups and agents, and despite more than one agent saying “yes,” she’s expressed similar fears to me and describes her current status as “blocked.”
The point is to find people who’ll give you honest and constructive criticism. You don’t necessarily have to be told how to fix it, but knowing that something doesn’t work puts you closer to the right path.
Just Do It Sometimes you just need to put one foot in front of the other and go for it. Write that script, get that camera and shoot that photo, break out the journal and start that book—just go for it. What do you have to lose? Rod Paddock
How do you get over these creative fears? Here are a few ideas.
Ask for Help This is exactly like the software developer in my mentorship story. Seek out the advice of other people who’ve done it before. Consider their advice and leverage their knowledge as you pursue your endeavors. It’s not a sign of weakness—it’s a sign of strength to know what you need and how to get it.
Form a Circle of Trust In any creative endeavor, you need to have people that you can send your work to who will set you straight when something works and, better yet, when something doesn’t work. One of my friends in Austin is a professional screenwriter/ author and I’m part of his “Circle of Trust.” He relies on me to give honest advice and I’m always happy to do that. For one of his scripts, my advice was something like this: “These sections sound like bad episodes of Law and Order and you should drop or trim them as they do nothing for the story.” If all he wanted was praise for his lovely prose, I bet I’d be out of the circle quickly.
codemag.com
ONLINE QUICK ID 1605021
Parse is Set to Shut Down. Now What? Parse, a Backend-as-a-Service (BaaS) platform, allows developers to focus their efforts on building a robust front-end experience without having to worry about constructing a complex backend infrastructure. Its cost and resource-effective options alleviate many projects’ need to create a database, code API endpoints, handle a push-notification infrastructure, figure out how to scale properly, etc. Facebook acquired Parse in 2013. In 2014, Parse released a report detailing their support of over 500,000 applications. On January 28, 2016 Facebook announced that it would shut Parse down with all services ceasing operation on January 28, 2017.
Jason Bender Jason.bender@rocksaucestudios.com www.controlappdelete.com www.twitter.com/TheCodeBender Jason Bender is the Director of Development for Rocksauce Studios, an Austin, Texas-based Mobile Design and Development Company. He has 11 years coding experience in multiple languages including Objective-C, Java, PHP, HTML, CSS, and JavaScript. With a primary focus on iOS, Jason has developed dozens of applications including a #1 ranking reference app in the US. Jason was also called upon by Rainn Wilson to build an iOS application for his wildly popular book and Youtube channel: SoulPancake.
This announcement left many developers, including me, needing to figure out where to go next. Essentially, anyone using the service has one year to migrate their backend elsewhere or their application will stop working when the plug is pulled. If you’re like most, you haven’t planned or budgeted for migrating your backend to another service. Fortunately, several options exist and migrating isn’t as hard as you might think.
What are My Options? Parse shutting down inconveniences many developers, and many alternative options do exist. From building your own backend, finding a substitute service, or spinning up your own self-hosted instance of Parse, there are a variety of options to choose from.
Building Your Own Backend
Out of the options covered in this article, creating your own backend will likely consume the most time and resources, both short-term and long-term. However, this approach also grants the highest level of flexibility, customization, and control. When building a custom backend, you’re free to choose the technologies and frameworks that make the most sense to support your business. You don’t have to deal with the traditional limiting factors that you may encounter when using a BaaS platform. Furthermore, you remove
the dependency on a third party. As made evident by Parse shutting down, relying on a third party to support your business’s infrastructure can negatively impact you in the long term should the service get discontinued or its product undergo a radical change.
Building your own backend grants the highest level of flexibility, customization, and control.
Although this approach does give you control of your own destiny, it also comes with several negatives, depending on your team’s expertise and the monetary resources at your disposal. Many developers originally went with Parse to save costs and get to market faster. A custom backend not only costs significantly more to develop, it takes longer and comes with a higher probability for error. Because you assemble the infrastructure and architect the approach yourself, not only do you introduce the human error element, but also maintenance needs. It requires an ongoing effort to keep it running at maximum potential. For instance, you have to make sure that it has the latest security updates, scales properly, manages the hardware, and monitors error logs, just to name a few new chores to add to your team’s ToDo list. Building a custom backend, however, doesn’t need to be an all-or-nothing endeavor. There are several proven technologies and services to simplify and expedite that process. Again, you open yourself up to reliance on third parties but, as is the case with much of software development, you’ve got to run a cost-benefit analysis. Services like Heroku (more
Figure 1: Amazon Mobile Hub dashboard
8
Parse is Set to Shut Down. Now What?
codemag.com
details in the sidebar) can at least alleviate some of the pressures of maintaining your own infrastructure. You still build and architect your solutions from scratch but technologies like this help solve many hardware headaches and make it significantly easier to scale up with your business.
Amazon Mobile Hub
Amazon Mobile Hub is Amazon’s BaaS platform that uses existing Amazon Web Services (AWS) technologies, like AWS Cognito, AWS Simple Notification Service, and AWS DynamoDB, to help developers create robust mobile experiences. Under a single dashboard, developers can enable and configure the corresponding services that they need to power their application. Figure 1 demonstrates the dashboard that allows a developer to use an array of features, like: • User management and authorization using AWS Cognito • NoSQL and SQL databases using AWS RDS and AWS DynamoDB • Push Notifications using AWS Simple Notification Service • App Analytics using Amazon Mobile Analytics • Cloud-based logic using AWS Lambda • QA against real devices in the cloud using AWS Device Farm If you’ve used Parse in the past, the feature offerings should appear very familiar. Additionally, many of the Amazon services, like DynamoDB for one, have drop-in client-side iOS and Android SDKs just as Parse did. However, they’re not drop-in replacements. If you want to transition to Amazon, you need to reconfigure your backend architecture through the Amazon Mobile Hub dashboard and then, using the various Amazon SDKs, replace any use of the embedded Parse SDK with the Amazon counterpart. Although the functionality is similar, it differs in syntax, so this requires a sizeable amount of rework on each client-side platform, depending on the overall scope of the project. As a result, Amazon Mobile Hub makes a lot of sense when starting a new project and looking for a Parse alternative. However, when looking at the most cost-effective migration options, other approaches exist that can get you back up and running quicker.
Your Own Parse Server
When Parse announced that it was closing up shop, it also open-sourced the Parse server, which runs on Node.js and MongoDB. If building your own custom backend or switching to a Parse substitute on short notice seems unfeasible, self-hosting your own version of Parse, at the very least, gives you time to figure out your long-term plan. Amazon details tutorials on their website for how to migrate your existing Parse setup to Amazon AWS using AWS Elastic Beanstalk. Not only do you get the dependability of Amazon’s infrastructure, but also the development effort on your existing platforms is minimal. For instance, it allows you to continue to use the Parse client-side SDKs that you already have in place, meaning that (as long as you’re on the latest SDK versions) you only need minimal changes to the client-side application to continue using basic data services. If you use push notifications, analytics, or some of a few of the other offerings that Parse had, you’ll have to find substitutes to fill in the gaps. Check
codemag.com
Figure 2: Client-to-backend relationship after the first phase of Parse’s migration strategy
Figure 3: Client-to-backend pre-deployment relationship after completed migration the sidebar for links to some of the services you can take advantage of. Similarly, on Github (links in sidebar), Parse details tutorials to incrementally configure your own version of the Parse server using Heroku and either mLab or ObjectRocket. You start by migrating only your data to a self-hosted MongoDB. From there, you can tell your existing application to start writing to your hosted DB instead of the Parse-hosted DB. Figure 2 demonstrates what the relationship looks like after phase 1, with blue elements controlled by Parse and orange self-hosted. Eventually, after following the entirety of Parse’s instructions on migrating your dependencies from their platform to your own self-hosted version, the client-to-backend relationship resembles Figure 3. In Figure 3, you have a development build running on a completely Parse-independent version of the Parse API using your own deployed Parse server and a self-hosted MongoDB instance. At the same time, your production application, still using Parse’s API, has transitioned to the new MongoDB instance as well, because changing your production application’s data source can be done without client-side updates. Then, the only thing left to do is push a version of the development build into production and have your users update to this build. If you’ve given yourself
Parse Substitutes https://github.com/relatedcode/ ParseAlternatives contains a very detailed list of Parse alternatives as well as smaller services that cover specific functionality that Parse offers, like push notifications and analytics.
Parse’s Migration Plan https://github.com/ ParsePlatform/parse-server/ wiki/Migrating-an-ExistingParse-App contains official Parse documentation for migrating your existing Parse application to a self-hosted instance.
Parse is Set to Shut Down. Now What?
9
enough time, your user base will migrate to the new build and when Parse goes offline, your users will be free-andclear of the version that previously relied on Parse. Although this option likely gives you the shortest path to migrate off of Parse’s services, you should also consider the downside to this approach. Mainly, the open source server doesn’t come with all the bells and whistles that largely made Parse desirable to begin with; items such as the dashboard (Parse mentions that they’re working on an open-source version), push notifications, analytics, webhooks, general application configuration, cloud code, and scheduled jobs will all need to be substituted if your application is dependent on them.
Additional Parse Substitutes
Amazon Migration Plan https://aws.amazon.com/mobile/ getting-started/ details Amazon’s official strategy for moving an existing Parse application to AWS using Elastic Beanstalk.
Heroku https://www.heroku.com/ Heroku is a cloud platform based on a managed container system, with integrated data services and a powerful ecosystem, for deploying and running modern apps.
ADVERTISERS INDEX SQL Reporting Services, ASP.NET MVC, AngularJS, F#, Life after Parse
MAY JUN codemag.com - THE LEADING INDEPENDENT DEVELOPER MAGAZINE - US $ 5.95 Can $ 8.95
2016
Create Mobile UIs with Xamarin.Forms
Lastly, the BaaS ecosystem has a number of players that you could consider if you’ve determined that you want a substitute and would rather re-engineer your client-side applications instead of taking up backend development. The following list details some of the more popular options. The sidebar contains additional links to a more comprehensive list. • Kinvey: A BaaS platform very similar to Parse and offering cloud storage, push notifications, and custom business logic. Unlike Parse, the free tier is development-only. • Firebase: NoSQL cloud database that specializes in real-time communication. Data is stored in JSON objects and is then synced to all connected clients in real-time. • CloudKit: An iOS- and OSX-specific public and private cloud database that has a point-click dashboard similar to Parse. • Microsoft Azure Mobile App Service: Similar to Parse in that it also does both offline/online data
Implement Lean Controllers in iOS Apps Tame the HTML5 Audio Control
Advertising Sales: Tammy Ferguson 832-717-4445 ext 026 tammy@codemag.com
Sooner Rather than Later Whichever approach you decide to take, it benefits you to move quickly. Parse announced that if you haven’t at least migrated your data source by April 28, 2016, it would de-prioritize the traffic to your application in favor of applications that have already made that transition. They also formally suggest that you try to complete your migration off of their services by July 28, 2016.
If you haven’t at least migrated your data source by April 28, 2016 Parse will de-prioritize the traffic to your application in favor of applications that have already made that transition.
The options listed in this article represent a small sample of the choices that you have to consider. If you want to check out more options, look at the sidebar for several additional resources. Additionally, when considering one of the Parse alternatives, keep an eye out, as most have tutorials specifically created to help developers transition from Parse to their respective platform. Jason Bender
Advertisers Index 1&1 Internet, Inc. www.1and1.com AnDevCon www.andevcon.com
dtSearch 7
www.dtSearch.com
55
43
Hibernating Rhinos Ltd. http://ravendb.net
5
2
LEAD Technologies www.leadtools.com
38
Aspose www.aspose.com
Explore and Embrace AngularJS 2
synching. Additionally, it supports user authentication, cloud code, and push notifications.
CODE Divisions www.codemag.com
76
Real Time Web Solutions Conference www.webrtcexpo.com/east/Default.aspx
67
CODE Framework www.codemag.com/framework
11
SPTechCon www.sptechcon.com
47
DevCon5 www.devconfive.com DevTeach Developers Conference www.devteach.com
29 75
This listing is provided as a courtesy to our readers and advertisers. The publisher assumes no responsibility for errors or omissions.
10
Parse is Set to Shut Down. Now What?
codemag.com
ONLINE QUICK ID 1605031
The Journey to Angular: Part 1 In the eighteen years that I’ve been doing Web development, a lot has changed. We started out creating HTML pages to present static information to our users. We then used classic ASP to get database data and incorporate that into our pages. To use both of these technologies, we had to know a lot about HTML, CSS, and JavaScript. Along came .NET and we started rendering everything on the server-side. We forgot a lot about HTML, CSS, and JavaScript as Web Forms wrapped up a lot of that for us. Web Forms’ architecture closely mimicked the way developers created desktop applications. This was great for helping developers move to the Web, but unfortunately hid a lot of the power of the Web, and also tended to be a little slow.
Paul D. Sheriff www.PDSAServices.com Paul D. Sheriff is the President of PDSA, Inc. PDSA develops custom business applications specializing in Web and mobile technologies. PDSA, founded in 1991, has successfully delivered advanced custom application software to a wide range of customers and diverse industries. With a team of dedicated experts, PDSA delivers cost-effective solutions, on-time and on-budget, using innovative tools and processes to better manage today’s complex and competitive environment. Paul is also a Pluralsight author. Check out his videos at http://www.pluralsight.com/ author/paul-sheriff.
Web developers were demanding that Microsoft get back to the roots of HTTP and HTML, and thus MVC was invented. This technology was very different from Web Forms, but was more closely aligned with how the Web worked. In order to use MVC, you had to get back to your roots of HTML, CSS, JavaScript, and jQuery. Most developers using MVC were still doing a lot of server-side rendering and this meant a lot of post backs, which also still slowed down the user experience. In my last three articles for CODE Magazine, I introduced you to a pure HTML page that allowed you to list, add, edit, and delete data using the Web API, jQuery, and JavaScript. As you have seen from these articles, this page is very fast, much faster than either Web Forms or MVC’s server-side rendering. Unfortunately, the code we’ve written leaves a little bit to be desired. There are a couple of things we can do to make our code better; namely, JavaScript closures and HTML templating. These two technologies are used extensively in AngularJS, which is a clientside framework that is very popular for Web development today. In this article, you’ll learn to wrap your JavaScript functions and variables into a JavaScript closure. In addition, you’ll learn to use mustache.js to create HTML templates and eliminate the coding of HTML in JavaScript. By learning these techniques, you will be that much closer to moving to Angular. When you begin to use Angular you will see a performance gain in your Web pages. This is due to less post-backs and less bytes transferred across the network. There are many other benefits as well, and I will explore these in the next articles.
Introduction to JavaScript Closures The first step in getting to Angular is to learn about JavaScript closures. Think of a JavaScript closure like a class in an OOP language. Within this closure, you can have private variables, private functions and public functions. To illustrate, let’s take the HTML page shown in Figure 1 and the JavaScript to load the HTML table shown in Listing 1 and wrap that up into a closure.
12
The Journey to Angular: Part 1
table-condensed table-striped"> <thead> <tr> <th>Product Name</th> <th>Introduction Date</th> <th>URL</th> </tr> </thead> <tbody></tbody> </table>
In Listing 1, there are three main components. There’s a variable called products that holds the collection of product data and there’s a function called productTableBuild() and a function called productList(). Of these three things, only productList() needs to be public. The other items should be hidden from the global scope of the page. This is where a closure works well. To create a closure, wrap all your variables and functions contained within the <script> tags into another function, as shown in the code snippet below. var controller = (function () { // Add your private functions & variables })();
The wrapper that you’re creating is called an Immediately-Invoked Function Expression, commonly known as an IIFE. An IIFE is a design pattern that creates a scope where you can create private variables and functions and expose public functions. It does all of this while not polluting the global environment. If you take the code from Listing 1 and wrap it into a closure, all three components become private. This means that you can’t reference any of them from the new controller variable. The variable products and the productBuildTable() function are fine, but you need to make productList() a public function. To do this, add a return statement around productList() and move the name of the function in front of the function statement. The code below shows the complete closure without the all of the code in the functions. var controller = (function () { var products = []; function productTableBuild() { }
The portion of this page you’ll be interacting with is the <table> element. This element is shown below.
return { productList: function () { } }; })();
<table id="productTable" class="table table-bordered
When the HTML document is loaded, the $(document). ready function is called, which, in turn, calls the pro-
codemag.com
ductList() function in your closure. Because this function now only has scope within the closure, you need to add the variable name controller in front of the call to productList(), as shown below. $(document).ready(function () { controller.productList(); });
The complete JavaScript code in your page should now look like Listing 2.
JavaScript Closure for Product Add, Edit, Delete Page
If you’ve been reading my last few articles, you’ll remember that you wrote a lot of functions. If you’re not familiar with the last article, that’s not a problem, as you can probably get a good idea from the code in Listing 3 of
Listing 1: A normal JavaScript function to load a table <script> $(document).ready(function () { productList(); }); var products = []; function productTableBuild() { for (var i = 0; i < products.length; i++) { $("#productTable tbody").append( "<tr>" + "<td>" + products[i].ProductName + "</td>" + "<td>" + products[i].IntroductionDate + "</td>" + "<td>" + products[i].Url + "</td>" + "</tr>"); } } function productList() { // Add data to products variable products = [
{ ProductId: 1, ProductName: 'Extending Bootstrap with CSS, JavaScript and jQuery', IntroductionDate: "6/11/2015", Url: 'http://bit.ly/1SNzc0i' }, { ProductId: 2, ProductName: 'Build your own Bootstrap Business Application Template in MVC', IntroductionDate: "1/29/2015", Url: 'http://bit.ly/1I8ZqZg' }, { ProductId: 3, ProductName: 'Building Mobile Web Sites Using Web Forms, Bootstrap, and HTML5', IntroductionDate: "8/24/2014", Url: 'http://bit.ly/1J2dcrj' } ]; productTableBuild(); } </script>
Figure 1: A list of products
codemag.com
The Journey to Angular: Part 1
13
Listing 2: A JavaScript closure eliminates functions and variables from the global namespace $(document).ready(function () { controller.productList(); });
{ ProductId: 1, ProductName: 'Extending Bootstrap with CSS, JavaScript and jQuery', IntroductionDate: "6/11/2015", Url: 'http://bit.ly/1SNzc0i' }, { ProductId: 2, ProductName: 'Build your own Bootstrap Business Application Template in MVC', IntroductionDate: "1/29/2015", Url: 'http://bit.ly/1I8ZqZg' }, { ProductId: 3, ProductName: 'Building Mobile Web Sites Using Web Forms, Bootstrap, and HTML5', IntroductionDate: "8/24/2014", Url: 'http://bit.ly/1J2dcrj' } ];
var controller = (function () { var products = []; function productTableBuild() { for (var i = 0; i < products.length; i++) { $("#productTable tbody").append( "<tr>" + "<td>" + products[i].ProductName + "</td>" + "<td>" + products[i].IntroductionDate + "</td>" + "<td>" + products[i].Url + "</td>" + "</tr>"); } }
productTableBuild(); return { productList: function () { // Add data to products variable products = [
Sample Code You can download the sample code for this article by visiting my website at http://www.pdsa.com/ downloads. Select PDSA Articles, and then select “Code Magazine - The Journey to Angular - Part 1” from the drop-down list.
} }; })();
what it does. You’re not interested in creating the whole project in this article, just how to wrap up the different functions into the closure. The HTML page from the last articles lists, adds, edits, and deletes product data. This page contains 19 functions within the <script> tags. Of these 19 functions, only five need to be made public. There’s also one global variable called Product. This variable becomes a private variable within the closure, as that’s the only place it needs to be referenced. Listing 3 is what your closure looks like for all 19 functions. I removed all of the code from within the functions so you can just see how everything is enclosed within the IIFE. Remember that after wrapping your functions into a closure, you need to reference the closure variable in front of your functions. This includes your event handlers. If you added your event handlers into your closure, you need to make sure they are made public. You also need to change the HTML that calls these event handlers. Add the name of your closure variable in front of the calls to the event handlers, as shown in the next code snippet. Notice the controller. that was added prior to the updateClick() function name. <button type="button" id="updateButton" class="btn btn-primary" onclick="controller.updateClick();"> Add </button>
Creating closures is not that hard and really, just requires a little bit of refactoring of your code. Think about what functions need to be exposed as public functions on your
14
The Journey to Angular: Part 1
page and just change the format of those to match what’s presented in this article.
Templating with Mustache Concatenating strings in JavaScript as I did in the productTableBuild() function is a horrible way to generate HTML. It’s very difficult to read, hard to debug, and leads to runtime errors. Instead, let’s employ a technique called templating by bringing an open-source library called Mustache into the project. You can download this very small library at https://github.com/mustache/mustache.github.com. Once you download it, add the mustache.min.js file to your project and reference the library in your HTML page using the following tag: <script src="/Scripts/mustache.min.js"></script>
In Listing 2, the function productTableBuild() is used to build HTML table rows in JavaScript. You’re now going to eliminate the code in that function and replace it with code to process a template. To do this, you need to do three things. First, add a public function in the closure to expose the collection of product objects contained in the products variable. productCollection: function () { return productList; }
Second, add a <script> tag with an ID attribute and a type of “text/html”. Within this tag, you create template data binding code using curly braces. Anything you see within two open curly braces and two closing curly braces refers to a function or property name of a JSON object.
codemag.com
<script id="productTableTmpl" type="text/html"> {{#productCollection}} <tr> <td>{{ProductName}}</td> <td>{{IntroductionDate}}</td> <td>{{Url}}</td> </tr> {{/productCollection}} </script>
In the template code, you see {{#productCollection}}. This refers to the function name productCollection in the closure that returns the array of JSON product objects. Think of this token as a “for” loop. At the end of this template code is {{/productCollection}}. This signifies the end of the “for” loop. Within these two tokens are the <tr> and <td> elements and within each <td> elements are more data binding tokens with the names of each property in the product object that you wish to display in each column of the table.
The third thing you need to do is to write the code to process this template using the Mustache library. In the productTableBuild() function, replace the old code with the code shown here: function productTableBuild() { var template = $("#productTableTmpl").html(); var html = Mustache.to_html(template, controller); $('#productTable tbody’).html(html); }
The first line of code returns the HTML for the template code in the <script> tag. The second line of code passes that HTML and the variable named controller (the JavaScript closure) to the to_html() function of the Mustache library. The Mustache library now accesses the productCollection() function in the closure, iterates over the collection of product objects, and renders each <tr> element between the {{#productCollection}} and the {{/productCollec-
Listing 3: Wrapping all your functions into a closure $(document).ready(function () { // Get all product data controller.productList(); }); // **************************** // Closure for page // **************************** var controller = (function () { // **************************** // Private variables // **************************** var Product = { ProductId: 0, ProductName: "", IntroductionDate: "", Url: "" } // Constants const BASE_URL = "/api/Product/"; // **************************** // Private Functions // **************************** // Call Web API to add a product function productAdd(product) { } // Add Web API call was successful function productAddSuccess(product) { } // Display all Products function productListSuccess(products) { } // Add Product row to <table> function productAddRow(product) { } // Build a <tr> for a row of table data function productBuildTableRow(product) { } // Clear form fields function formClear() { }
codemag.com
// Delete product from <table> function productDelete(ctl) { } // Call Web API to update a product function productUpdate(product) { } // Update Web API call was successful function productUpdateSuccess(product) { } // Update product in <table> function productUpdateInTable(product) { } // Get a product from Web API function productGet(ctl) { } // Move product data to input fields function productToFields(product) { } // Handle exceptions from AJAX calls function handleException(request, message, error) { } // Get error messages from ModelState function getModelStateErrors(errorText) { } // **************************** // Public Functions // **************************** return { deleteClick: function (ctl) { }, editClick: function (ctl) { }, addClick: function () { }, updateClick: function () { }, // Get all Products to display productList: function () { } }; })();
The Journey to Angular: Part 1
15
tion}} tokens. The to_html() function returns the raw HTML to which you pass that HTML to the html() function of the selector, which returns the DOM object <tbody> in the “productTable” <table>. In this way, the table now has rows and columns and can display the product data. Listing 4 contains the complete JavaScript code to generate the page that looks like Figure 2.
Formatting Data in a Template
Displaying a UTC date in your HTML table is probably not what you want for your users. Mustache has some limited formatting capabilities, but it’s often easier to create a
public function that returns the data formatted the way you want. For example, if you want to display the date in your locale’s format, you can add a public function named toLocalDate() in your closure, as shown here: toLocalDate: function () { return this.IntroductionDate .toLocaleDateString(); },
Modify your template to use this new function instead of the IntroductionDate property.
Figure 2: A list of products from a template Listing 4: Wrapping all your functions into a closure var controller = (function () { // ************************************ // Private Variables // ************************************ var productList = []; // ************************************ // Private Functions // ************************************ function productTableBuild() { // Get template from script var template = $("#productTableTmpl").html(); // Pass Mustache the template and the object // with the collection of product data var html = Mustache.to_html(template, controller); // Insert the rendered HTML into the DOM $('#productTable tbody').html(html); } // ************************************ // Public Functions
16
The Journey to Angular: Part 1
// ************************************ return { productList: function () { // Add data to productList variable productList = [ { ProductId: 1, ProductName: 'Extending Bootstrap with CSS, JavaScript and jQuery', IntroductionDate: new Date(2015, 5, 11), Url: 'http://bit.ly/1SNzc0i' }, ... ]; productTableBuild(); }, productCollection: function () { return productList; } }; })();
codemag.com
<script id="productTableTmpl" type="text/html"> {{#productCollection}} <tr> <td>{{ProductName}}</td> <td>{{toLocalDate}}</td> <td>{{Url}}</td> </tr> {{/productCollection}} </script>
When you run the page now, you should see the date displayed in your local format.
Get Data from Web API
So far in this article, you used hard-coded data for the product list. A more realistic use is to retrieve data from a Web API call. Add a Web API controller to return a collection of product objects with the same property names as the ones used in this article. Add a new private function to your closure called getProducts(). This function makes an Ajax call to your Web API and upon successful return of the data, assigns the collection returned to the productList variable in your closure. function getProducts() { return $.ajax({ url: BASE_URL, type: 'GET', contentType: "application/json; charset=utf-8", dataType: 'json', success: function (products) { productList = products; }, error: function(request,message,error) { handleException(request,message,error); } }); }
type: 'GET', contentType: "application/json; charset=utf-8", dataType: 'json' }).done(function (data) { products = data; productTableBuild(); }).fail(function (error, textStatus) { handleException(error,textStatus,null) }); },
If you have a date property being returned from the Web API like IntroductionDate, you need to remember that this value comes across as a string. You need to convert it into a valid JavaScript date prior to attempting to use the toLocaleDateString() function on it. Modify your toLocalDate() function to do the conversion, as shown in the following code snippet: toLocalDate: function () { return new Date(this.IntroductionDate) .toLocaleDateString(); },
SPONSORED SIDEBAR: Does Your Cloud App Leave You Feeling Under the Weather?
Summary In this article, you learned how to wrap your JavaScript code into a closure. A closure ensures that your JavaScript doesn’t pollute the global environment with unnecessary variables that can cause hard-to-find bugs. You also learned how to use templating with the open source library called Mustache. Templating is used extensively in Angular, so starting to get familiar with this technique will help you to move to Angular. In the next article, I’ll introduce you to Angular and you will see how to apply what you learned in this article. Paul D. Sheriff
In the getProducts() function notice, this function returns the call to the $.ajax() function. A “Promise” is returned from the jQuery $.ajax() call. This allows us to call the getProducts() function from the productList() function and chain to the Promise’s done() function. This allows the $.ajax() call to be processed asynchronously, and when it’s done, call the productTableBuild() function in order to process the Mustache template.
The developers at CODE have worked on everything from cloud applications to mobile projects. If you’re having problems with your and need guidance, the developers at CODE Consulting can help. For more information visit www.codemag.com/ consulting or email us at info@codemag.com to set up your time with a developer today.
productList: function () { getProducts().done(function () { productTableBuild(); }); },
The alternative to creating a new function and returning the $.ajax() promise is to use the newer syntax for $.ajax(). For this syntax (valid after jQuery 1.5), you take advantage of the promise right in the productList() function. Notice how you now only set parameters of the Ajax call in the first parameter and eliminate the success and error functions. Instead you use done() and fail() functions. The end result is the same; it’s just two different methods for making the call. productList: function () { $.ajax({ url: BASE_URL,
codemag.com
The Journey to Angular: Part 1
17
ONLINE QUICK ID 1605041
AngularJS 2 AngularJS has been a huge success. More than 1.1 million developers have embraced this platform and created thousands of applications using it. Writing complex applications in JavaScript can be challenging, and AngularJS made it approachable. In a language devoid of good design patterns and strict rules, AngularJS brought sanity and consistency. But let’s be honest; although AngularJS was a huge improvement over writing JavaScript by hand, it did have some issues. Learning AngularJS was a steep curve. Buying into it was embracing the whole platform. And most of all, there were so many ways to do it incorrectly, and so many ways to do it better than the last developer.
NameMalik Sahil Autor www.winsmarts.com www.internet.com @sahilmalik asdfasdfasdfasdfker, a .NET Sahil Malik is a Microsoft MVP, author, consultant and trainer. INETA speaker, a .NET author, Sahilasfasdfasdfasdfasdfasdfdconsultant, and trainer. fainings are full of humor and Sahil loves interacting withfind felpractical nuggets. You can low realtrainings time. Hisattalks moregeeks aboutinhis and trainings are full of humor http://wwasdfasdfasfasdfasdf and practical nuggets. You can find more about his training at http://www.winsmarts.com/ training.aspx. His areas of expertise are crossplatform mobile app development, Microsoft anything, and security and identity.
Over time, as browsers improved and Web technologies were applied to more than just browsers, AngularJS 1 didn’t take full advantage of those scenarios. Plus, TypeScript, which, in my mind is the single most important innovation in JavaScript since fast JavaScript engines, always felt like an afterthought in AngularJS 1. Yes, although you could use TypeScript, it was hard to take full advantage of it unless you consciously attempted to do so. As a result, in an AngularJS 1 world, you are more likely to see lots of JavaScript or incomplete usage of TypeScript. This is not unexpected: Times change and technologies evolve. AngularJS 2 is a natural evolution of AngularJS 1. AngularJS 2 brings it up to date with modern Web standards, better design patterns, and takes advantage of what’s new and awesome today.
Why Bother with AngularJS 2 There are four main reasons you should seriously consider AngularJS 2: • It is fast. Very fast. It takes full advantage of modern Web browsers and defaults to using the capabilities they offer to give you the best performance. If you wrote the same functionality in AngularJS 2 as you wrote for AngularJS 1, AngularJS 2 will most likely far outstrip the performance of AngularJS 1. • It is portable. Although it relies on the newest standards, it uses polyfills to support almost every browser you care about. Even IE9 is supported.
Listing 1: AngularJS 1 Controller (function () { angular .module(‚app‘, []) .controller(‚appController‘, appController) function appController() { var vm = this; vm.name = „Sahil“; } })();
Listing2: Using an AngularJS 1 controller <body ng-app=“app“ ng-controller=“appController as vm“> Name: <span ng-bind=“vm.name“></span> </body>
18
AngularJS 2
• It allows for a more logical code structure than its predecessor. AngularJS 1 had the bad habit of polluting your HTML, not giving you more control on CSS, and forcing you to think in MVC. AngularJS 2 allows you to do MVC, or Flux, or any other design pattern you wish. It also allows you to logically design and arrange your application in components rather than controllers. And finally, it tends to pollute your HTML less, and only in ways that semantically enhance HTML. You don’t need ng-href or ng-src, but you can still have your own tags, like <customer/>, etc. • And the best part about AngularJS 2 is that it’s easier than AngularJS 1. I’ll be honest, it took a lot of time to truly master AngularJS 1. And there were so many ways to mess it up. Parent scopes can be so complicated. As my applications got bigger, my head exploded. Developers smarter than me crafted amazingly intricate card houses that collapsed easily. I felt like an idiot. And this was after a few years of AngularJS 1. AngularJS 2, on the other hand, felt a lot more logical, and it was easier to understand and learn. I find myself more productive in AngularJS 2, and I have the confidence that I’m writing more reliable and easier-to-maintain code. Much more so than AngularJS 1. The obvious elephant in the room of course is that AngularJS 2, at the time of writing this article, is still in beta. Still, I like AngularJS 2 so much that even in beta, I have no reservations in recommending this platform for new development over AngularJS 1, especially if your release date is in the second half of 2016.
Thinking in AngularJS 2 AngularJS 2 requires you to think differently. There are some key differences compared to AngularJS 1, features you need to learn and decisions you need to make. The good news is that even though there are differences between AngularJS 1 and AngularJS 2, many concepts port over. As an AngularJS 1 developer, you’ll clearly see the improvements where you see the differences. As a newto-AngularJS 2 developer, you’ll find the new syntax and features easier to learn. With that, let me go through the various differences, features, and key decisions.
Language Choices
JavaScript is evolving. It needs to, because the original language simply isn’t going to scale to the level of the complex applications that you need to build. When writing for AngularJS 2, you have various choices: • ES5: The obvious advantage of using ES5 is that it’s only JavaScript and it needs no compilation. But
codemag.com
it’s not strongly typed, and it doesn’t offer the code structure advantages of other alternatives. • ES6: ES6 is a superset of ES5. Although it does give you some better code structure building blocks, it isn’t strongly typed, and it needs a compilation step. At the end of the day, it’s JavaScript, and with time, browsers will support ES6 natively. It’s sure taking a very long time for them to do so. • TypeScript: TypeScript is a superset of ES6. It’s strongly typed. It gives you much better code structure building blocks, but it requires a compilation step. At the end of the day TypeScript is JavaScript—okay, it’s a superset of JavaScript, but you can rename your .js to .ts and pretend it’s JavaScript. • Dart: Dart seems to offer the worst of all worlds. It isn’t JavaScript and it requires compilation, but it gives you better code structure alternatives. Because it’s not a super set of ES5, ES6, or TypeScript, using Dart feels like using an entirely different language that doesn’t mentally translate into JavaScript. At least, not easily. Given the above choices, I feel that we have a clear winner, and that’s TypeScript. I’m going to write my AngularJS 2 code in Typescript, and apparently the AngularJS 2 team is doing so too. You are, however, free to use ES5, ES6, or Dart. But I don’t see why you wouldn’t prefer to use Typescript.
Listing 3: An AngularJS 2 component import {Component} from ‚angular2/core‘; @Component({ selector: ‚my-app‘, template: ‚<h1>Hello {{name}}<h1>‘ }) export class AppComponent { name = „Sahil“; }
Listing 4: Bootstrapping an AngularJS 2 app import {bootstrap} from ‚angular2/platform/browser‘ import {AppComponent} from ‚./app.component‘ bootstrap(AppComponent);
Listing 5: Structural directives in AngularJS 1 <ul> <li ng-repeat=“hobby in vm.hobbies“> <span ng-if=“!hobby.hide“> {{hobby}} </span> </li> </ul>
Components versus Controllers
AngularJS 1 required you to create a controller. A controller was the fundamental “brain” of your application. It was way too easy to abuse the pivotal role of a controller in AngularJS 1, which is why the best practice was to separate most of the logic into services that the controller could use. A typical AngularJS 1 controller looks like the one shown in Listing 1. You could use this controller as shown in Listing 2. Controllers, over time, became very complex. They had to deal with parent and child scopes. There was no one-sizefits-all approach that you could use, making understanding and debugging the application for the next developer a lot harder. And it forced you to do MVC, whether or not it was the right choice for your application. AngularJS 2 replaces “thinking in controllers” with “thinking in components.” Components are the central artifact in AngularJS 2. <my-app>Loading...</my-app>
Using the component shown in Listing 3 is a matter of referencing the selector. The component can nest other components, and it can even give your “tag” its own CSS area, so your CSS doesn’t interfere with the page’s CSS and vice versa.
Bootstrapping
Bootstrapping your app requires explicit action from you. AngularJS 1 allowed you to bootstrap your app by placing certain attributes in your HTML, as shown in Figure 1. Or alternatively, you could bootstrap your app in AngularJS 1 using code also.
codemag.com
Figure 1: Bootstrapping an AngularJS 1 app AngularJS 2 takes a different approach. AngularJS 2 likes to split your application into components. And it allows you to bootstrap the starter component by importing it as a module first. This can be seen in Listing 4. This gives us a lot of control in modularizing the application. Because the module is a class that you export, you have very clear control of which portions of the module are visible and which are not. You can also bootstrap conditionally; what’s perhaps most interesting is the first line in Listing 4. AngularJS 2 imports bootstrap from angular2/ platform/browser. This means that the browser is separate from the core AngularJS 2. You could theoretically have different bootstrapping functionality for Cordova/ Electron etc.
Structural Directives
There are three kinds of AngularJS directives: there are components; there’s a directive with a template in AngularJS 2; and there’s the attribute directive, which can change the behavior or appearance of an element (for example, NgStyle); and structural directives. Structural directives can alter the structure of the DOM. For instance, ngIf, ngSwitch, ngFor are structural directives.
AngularJS 2
19
Listing 6: Structural Directives in AngularJS 2 <ul> <li *ngFor=“#hobby of hobbies“> <span *ngIf=“!hobby.hide“>{{hobby | json}}</span> </li> </ul>
Listing 7: Dependency injection in AngularJS 1 (function () { angular .module(‚app‘, []) .controller(‚appController‘, appController) appController.$inject = [‚MyService‘]; function appController(MyService) { var vm = this; // other details.. } })();
You’ve seen structural directives in AngularJS 1. An example can be seen in Listing 5. AngularJS 2 also has support for structural directives. The same example written in AngularJS 2 can be seen in Listing 6. As you can see, the syntax is slightly different, but it still works. Now here is the really interesting part. You can write your own structural directives in AngularJS 2 using the @ Directive decorator. This gives you very fine control on exactly how you want the DOM to be modified. Modifying the DOM is one of the most expensive things your application does, so this level of control is definitely very welcome. Sure, you could do this in AngularJS 1, but there, everything was a directive, and writing directives could get complex. AngularJS 2, with its @Directive decorator, puts writing complex directives within the reach of mere mortals.
DataBinding
Databinding is what truly set Angular apart. There are three kinds of databinding that AngularJS supports: Interpolation, one-way databinding, and two-way databinding. Let’s look at them: • Interpolation, the {{expression}} double-moustache syntax. Both AngularJS 1 and AngularJS 2 support interpolation in exactly the same manner. The key difference is that AngularJS 1 databound to properties on the view model or $scope of the controller. AngularJS 2 databinds to exported properties of the underlying class that the component is implemented as. • One way databinding, using ng-bind. This is where AngularJS 2 shows its superiority over AngularJS 1. AngularJS 1 required you to do one way databinding using the syntax shown below: Name: <span ng-bind=“vm.name“></span>
This syntax was functional, but it required you to intersperse attributes that sometimes were not very intuitive.
20
AngularJS 2
Why am I using ng-bind to change the value of innerText? And what if I wanted to bind to an alternate property such as CSS or width or href? This is where AngularJS 2 uses a much better syntax, as shown here: <span [innerText]=“name“></span>
AngularJS 2 binds to any property of the surrounding element. This is more intuitive to learn and program because there’s no hidden meaning of what an ng-bind may do. You know that you’re modifying the innerText property, no surprises there. And if you wanted to bind to an event, you’d use a slightly different syntax, like that as shown here: <button (click)=“clickHandler()“>Click me</button>
Here, you’re binding to the click event of the button. Can you guess how you would bind to the “mouse-up” event? You can think of event binding as the reverse of data binding. Both are one-way bindings. • And sometimes, you need two-way databinding. Two-way databinding in AngularJS 1 was implemented as shown here: <input ng-model=“vm.name“></input>
The same affect can be achieved in AngularJS 2 using the “banana in a box syntax” or the [()] expression as shown below: <input [(ngModel)]=“name“>
This cleaner syntax of databinding that AngularJS 2 uses has another side benefit: You have fewer directives to learn.
Fewer Directives to Learn
Here’s an AngularJS 1 quiz. What did ng-href do? If you remember, you couldn’t databind directly to the href property of an anchor tag in AngularJS 1. This is because the browser understood the expression before the result of the expression, thereby breaking your href location. Not good! So you had to create directives like ng-href, ng-src, etc. Similarly, you had numerous other directives, such as ng-style to modify the style of an element. Or ng-click, ng-blur, etc. to handle events! Because AngularJS 2 allows you to databind with a much more intuitive syntax using the property name instead, all those directives are no longer required. For example, consider the following AngularJS 1 code: <img ng-src=“vm.link“/>
This can be simplified in AngularJS 2 as follows: <img [src]=“link“/>
And you can guess that you don’t need ng-click either, because you can databind directly to the click property. In fact, AngularJS 2 has removed the need for 40+ directives because of this superior syntax.
codemag.com
Services versus Class
It was very easy to abuse controllers in AngularJS 1. As a best practice, we were advised to abstract the heavy lifting to services. But services weren’t just services. There were Services, Providers, and Factories – three ways to create Services (and not very intuitively named, either). And then there were constants, values, and the config method etc. on your module. You had to know exactly which one did what, and which got called before the other. This innate knowledge of the nature of provider versus service versus factory and config versus run, or constant versus value left many of us confused. It made the learning curve unnecessarily steep. But why did AngularJS 1 have to do all that? It was making up for the shortcomings of JavaScript. AngularJS 2 on the other hand has a class. A class can have a constructor, exported properties, getters and setters, etc. And with TypeScript, the way you write these classes is also very second-nature to those coming from other languages such as C# or Java. In that sense, AngularJS 2 is much simpler to write and maintain because all those concepts are subsumed by the concept of a class—something you already know.
Dependency Injection
Dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (such as a service). An injection is the passing of a dependency to a dependent object (a client) that will use it. Dependency injection is a strength of AngularJS. It allowed us to change the behavior of code at runtime by passing in the appropriate dependencies. This was achieved in AngularJS 1 using the code shown in Listing 7. It worked, but it had a huge downside: The magic string problem. The MyService is a string in Listing 7, and if you introduced a typo, it broke your code. What was worse, AngularJS 1 allowed you to write variable names such as $scope and $http, and AngularJS 1 attempted to resolve those for you. Because a lot of code online showed such patterns, the typical Google-driven development workflow meant that a lot of developers copy-and-pasted that code and painstakingly made it work. As soon as the code was minified, all the variable names changed and nothing worked anymore. AngularJS 2 supports dependency injection too, but a better and improved version of it. As can be seen from Listing 8, there is no longer the magic string issue going on. You type the name of the class as it appears, and you can choose to alias it using standard ES6 and TypeScript constructs. You can then set a class variable in the constructor that can hold an instance of the provided service and use it appropriately. This means that your code works the same in both minified and un-minified scenarios. Your dev environment code doesn’t break mysteriously when it’s thrown into production.
• Modules: AngularJS 1 and AngularJS 2 both have modules. But AngularJS 1 couldn’t make use of ES6 modules because ES6 wasn’t around when AngularJS 1 was born. AngularJS 2 uses proper ES6 modules, but yes, the concept of modules is still around. • Filters: Remember in AngularJS 1 there was a filter called “filter”? Confusing? They’ve changed the name of Filters to Pipes! It’s much more logical, especially given the syntax. But the concept is still around. • Routing: Routing in AngularJS 1 has undergone evolution and improvements. The routing in Angular 1.5x is quite similar to routing in AngularJS 2. • HTTP: Because you need to interact with the server too. The same concepts apply in Angular1 and Angular2. • Events: These are still around, but you have a much better and logical syntax using event emitters. There will be more on that in future articles. • Promises: are still in there. AngularJS 2 has chosen to give us another option, built on RxJS, called Observables. Observables can be thought of as a datasource of events that you can filter on. It’s a much better architecture and it’s come out of reactive extensions for C#. If you’ve used reactive extensions in either JavaScript or C#, you probably already know that once you go RxJs, you don’t go back! Perhaps the improvement I like the most in AngularJS 2 is much better error messages. As a developer, I’ve wasted way too much time over poorly worded generic error messages. AngularJS 1 tried, it tried hard to give me a decent error message about a typo. But frequently an ng-click mistyped as ng-clik yielded no error message. Stuff just didn’t work! Even when I did get an error message, it looked like a cryptographic version of a SharePoint site collection GUID salted in Russian. I am happy to say that AngularJS 2 error messages are usually a lot cleaner and more intuitive than their AngularJS 1 counterparts.
Summary This article isn’t intended to be an AngularJS 2 tutorial. It’s intended to illustrate the key improvements and changes and the reasons behind them. It’s intended to illustrate why I’m excited about AngularJS 2 and why any developer should be excited as well. I’m certain that AngularJS 2 will be quite successful. I’m also certain that it will be used in platforms other than just the browser. It will help us build bigger and better applications, applications that will scale better and perform better. I’m truly excited about AngularJS 2, and I hope to talk a lot more about it in future articles. In the meanwhile, happy coding! Sahil Malik
Similarities and Other Improvements As you can see, there are a number of differences between AngularJS 1 and AngularJS 2. But fear not—there are a lot of concepts that port over from AngularJS 1 also. For instance:
codemag.com
AngularJS 2
21
ONLINE QUICK ID 1605051
Taming the HTML5 Audio Control I’m taking a brief hiatus from my usual legal topics. It’s been a while since I’ve written a technical piece. In this issue, I address that by writing about one of the HTML5’s most useful features, the Audio Control. The HTML5 Audio control presents a standard way to play audio. The same goes for the Video Control (which won’t be discussed in this article). In spite of that usefulness,
John V. Petersen johnvpetersen@gmail.com linkedin.com/in/johnvpetersen @johnvpetersen John is a graduate of the Rutgers University School of Law in Camden, NJ and has been admitted to the courts of the Commonwealth of Pennsylvania and the state of New Jersey. For over 20 years, John has developed, architected, and designed software for a variety of companies and industries. John is also a video course author for Lynda.com. HIs latest course, Foundations of Programming, Open Source Licensing, teaches everything you need to understand about the legal aspects of open-source licensing.
major issues exist when attempting to use the control “out of the box” on a mobile device. In this article, I’ll take you through those issues with a simple prescription that will help you avoid the struggles and pitfalls with this control. If you’re new to Web development or have always relied on using third-party JavaScript and CSS frameworks as abstractions in lieu of working directly with base JavaScript and CSS, this article may prove to be good learning resource for you.
The Code The code for this solution can be found on GitHub: https://github.com/johnvpetersen/HTML5AudioControlCodeMagazineArticle. The code is licensed under the MIT Open Source License.
The Use Case Let’s say that you’re tasked to build a Web page that can play a number of audio clips. Features include: • • • • •
Start/stop Advance the clip Display clip’s progress Restart clip Identical UX for desktop and mobile devices
Figure 1 shows a mock-up of what these use-case requirements might look like.
The HTML5 Audio Control Out of the Box The following code illustrates a simple usage: <audio controls> <source src="myclip.mp3" type="audio/mpeg"> Your browser does not support the audio element. </source> </audio>
The HTML5 Audio Control has the capacity to alleviate a lot of work. Not too long ago, there was legitimate concern over whether all browsers supported HTML5. That concern has not been totally alleviated. Looking to Figure 2, you can see simple markup that is rendered in two very different ways. The desktop browser version looks good. The iPhone browser, Google Chrome in this case, is broken. The fact that it’s Chrome doesn’t matter. Safari doesn’t work either. The main point is that unless your Web application is limited to the desktop, which isn’t likely, the HTML5 Audio Control default visual features won’t prove to be very useful. Even in the desktop scenario, there’s no way to style the visual appearance. The important takeaway is that for most cases, the HTML5 Audio Control’s visual facilities are useless. Fortunately, there’s a remedy with JavaScript and CSS!
22
Taming the HTML5 Audio Control
The Importance of Testing with Different Browsers and Devices The disparity between the desktop and mobile versions underscores the importance of testing. It never ceases to amaze me how often developers certify that something is working without at least some rudimentary testing to verify that assertion. This extends to cases where something must work on a mobile device and such certifications are issued in spite of never having run the application on a mobile device! Too often, the assumption is made that if it works on the desktop, it works on a mobile device. Often, that assumption holds true. Consider the fact that with mobile devices, there is no mouse. You have to make sure that cases on the desktop where you account for a mouse click are compatible with the finger gestures employed on a mobile device. Stated simply, if your team isn’t undertaking this sort of diligence, they’re doing it wrong!
JavaScript and CSS to the Rescue To fully illustrate how to make things work regardless of platform, I’ll use baseline JavaScript and CSS. That means that there are no dependencies on additional frameworks and libraries. JavaScript frameworks and libraries can be valuable. However, it’s also important to understand that such things are not always required. For purposes of this article, I want to focus on the Audio Control itself and how to make it work with the basics. You also get to dispense with the Angular versus Knockout versus Ember versus whatever arguments. By going this route, I know for certain that you can take this code and work with it regardless of whatever frameworks and libraries you’ve chosen.
It never ceases to amaze me how often developers certify that something works without at least some rudimentary testing to verify that assertion.
Starting from the End: Our HTML The HTML, listed in Listing 1 for this solution, is very simple and is illustrated in Figure 3. Unlike what you see in Figure 2 with the default Audio Control display, you now have parity between desktop and mobile browsers. To make things manageable, I followed a simple convention: • Div id = X: This is the div that displays the play or pause image, depending on the audio control’s state.
codemag.com
• Div id = resetX: This is the div that displays the reset image and is used to allow the user to restart an audio clip. • Div id = progressX/progressbarX: This is a nested div used to host a progress bar that displays a clip’s progress as it is played. • Audio id = clipX: This is the HTML5 Audio Control for a specified clip. This convention makes it easy to link-up and associate different DOM Objects to work together. This avoids having to traverse the DOM hierarchy, which can be a major drain on performance. With the HTML markup out of the way, let’s get to the JavaScript and examine how the page comes to life.
Before going too much further, let’s take a 10,000foot view of the code and how it relates to our layout. Figure 4 illustrates the relationship between the various JavaScript functions and the UI elements that are defined in HTML code in Listing 1. • Line 3, clips variable: Module-level variable to hold the Clips collection so that it may be referenced for later use • Line 5, createClipHTML call: Combines data and a markup template to produce HTML that is injected into the DOM • Line 6, processClips call: Creates and iterates through the Clips collection in order to call the other functions • The remaining functions initialize various event handlers that will be discussed later in this article
The JavaScript As stated earlier, the solution here is sans any third-party JavaScript or CSS frameworks and libraries. The goal here is to focus on a solution in such a way that it can be easily applied to any context, regardless of any framework and libraries used. To make things more digestible, I elected to place all of the JavaScript code into one file. It should go without saying that in a production application, such a strategy would be ill-advised! All of the code blocks that follow is sequential and is hosted within the following anonymous code block: (function(d) { })(document);
Note that this anonymous block is automatically executed when the script loads. Note also that a reference to the DOM’s document object is injected into the function. Where you see the variable “d”, that’s a reference to the DOM’s document object.
Figure1: This is a Desktop and mobile UI mock-up depicting the use case visual requirements.
Figure2: Depending on your device, the HTML5 Audio Control with the Controls option set may not be functional.
codemag.com
Taming the HTML5 Audio Control
23
Listing 1: Sound clip HTML <html> <head> <meta name=”viewport” content=”width=device-width, initialscale=1.0”> <link rel=”stylesheet” type=”text/css” href=”background.css”> </head> <body> <div class=”clipContainer”> <div id=”1” class=”play”></div> <p class=”artist”>Artist 1: <span class=”title”>Clip 1</span></p> <div id=”reset1” class=”reset”></div> <div id=”progress1” class=”progress”> <div id=”progressbar1” class=”progressbar”></div> </div> <audio id=”clip1”> <source src=”SoundClips/clip1.mp3” type=”audio/mpeg”>
</audio> </div> <div class=”clipContainer”> <div id=”2” class=”play”></div> <p class=”artist”>Artist 2: <span class=”title”>Clip 2</span></p> <div id=”reset2” class=”reset”></div> <div id=”progress2” class=”progress”> <div id=”progressbar2” class=”progressbar”></div> </div> <audio id=”clip2”> <source src=”SoundClips/clip2.mp3” type=”audio/mpeg”> </audio> </div> </body> </html>
Listing 2: ClipData that defines the title, artists and audio file sources var clipData = { “clips”: [{ “title”: “Clip 1”, “artist”: “Artist 1”, “media”: { “src”: “SoundClips/clip1.mp3”, “type”: “audio/mpeg” } }, {
Listing 3: HTML template var clipTemplate = '<div class="clipContainer"> \ <div id="#id" class="play"></div> \ <p class="artist">#artist: <span class="title"> #title</span></p> \ <div id="#resetid" class="reset"></div>\ <div id="#progressid" class="progress">\ <div id="#progressbarid" class="progressbar"></div> \ </div> \ <audio id="#clipid"> \ <source src="#clipSource" type="#clipType"> </source> \ </audio> \ </div>'
“title”: “Clip 2”, “artist”: “Artist 2”, “media”: { “src”: “SoundClips/clip2.mp3”, “type”: “audio/mpeg” } }] }
The Data
Web applications are rarely—if ever—made up of static content. Although this is a very simple application without the benefit of third-party libraries, why should this be any different? Listing 2 illustrates the data that will be used for this solution. Each sound clip has the following pieces of information: • Artist Name • Clip Title • Media: • Clip’s file source • Clip’s file type In a production application, this data is sourced from a server and obtained via an Ajax call. With the data problem resolved, let’s get to the template.
The Template
Listing 3 illustrates the HTML template. I started with the outcome, so Listing 1 illustrated the end-result of combining the data in Listing 2 and the template in Listing 3. The process of joining the data and template is shown in the next section.
Combining the Data and Template
The basics of any Web application start with some level of templating. Listing 4 illustrates how that process works here. The last line injects the newly generated HTML into the document body.
Figure 3: Rendered display for the HTML in Listing 1.
24
Taming the HTML5 Audio Control
Once the DOM has been hydrated, the process of wiring up the event handlers can be initiated. Both the clipData and clipHTML elements are processed in the createClipHTML() function.
codemag.com
Wiring Up the Event Handlers
Up to this point, your framework is takes care of everything. What follows here are things you have to provide regardless of framework. It’s here that you provide the following capabilities: • Play and pause an audio clip • Display a clip’s progress via a progress bar • Interactive progress bar to allow the user to click or touch the bar in order to move a clip forwards or backwards in time • Ability to re-start a clip from the beginning
Using processClips()
The processClips() function hydrates the clips variable and loops through the collection. In that loop, several functions are called that will be discussed in a moment. Listing 5 illustrates the processClips() function. One variable that requires special attentions is the clips. activeClip variable. It’s important to remember that JavaScript is a dynamic language and you can, on the fly, define new elements. In this particular case, you want a variable to hold the active clip. That way, you don’t have to redundantly loop through the options to
determine which clip is playing. The reason you need ready access to the active clip is because the user may play another clip. Out of the box, the audio controls are not coordinated. In other words, one will play on top of another unless your code intervenes in some way. Listing 4: Code to loop through data and create HTML from template var clipHTML = “”; for (var i = 0; i < clipData.clips.length; i++) { var newClip = clipTemplate .replace(“#id”, i + 1) .replace(“#resetid”, “reset” + (i + 1)) .replace(“#progressid”, “progress” + (i + 1)) .replace(“#progressbarid”, “progressbar” + (i + 1)) .replace(“#clipid”, “clip” + (i + 1)) .replace(“#artist”, clipData.clips[i].artist) .replace(“#title”, clipData.clips[i].title) .replace(“#clipSource”, clipData.clips[i].media.src) .replace(“#clipType”, clipData.clips[i].media.type); clipHTML += newClip }; d.body.innerHTML = clipHTML;
Figure 4: Each UI element can be traced to a JavaScript function.
Figure 5: Illustration of how the play and pause CSS classes are implemented
codemag.com
Taming the HTML5 Audio Control
25
Listing 5: processClips() function function processClips() { clips = d.getElementsByTagName(“audio”); clips.activeClip = “”;
processResetButton(clips[i]); processPlayPauseDiv(clips[i]); processClip(clips[i]); }
for (var i = clips.length - 1; i >= 0; i--) { processProgressBar(clips[i]);
}
Listing 6: processProgressBar() function function processProgressBar(clip) { var progress = d.getElementById(“progress” + (clip.id[clip.id.length-1])); progress.rect = progress.getBoundingClientRect(); progress.clip = clip; clip.progress = progress;
processClickTouch(xy, this); }
function processClickTouch(mouseEvent, progressBar) { progressBar.clip.currentTime = progressBar.clip.duration * ((mouseEvent.clientX progressBar.rect.left) / (progressBar.rect.right - progressBar.rect.left)); }
progress.onclick = function(xy) { processClickTouch(xy, this); } progress.ontouchstart = function(xy) {
Using processProgressBar()
}
The processProgressBar() function wires up all of the event handlers for the progress bar, which itself is nothing more than a nested div element. For a specific clip, based on its ID value, you can determine the correct progress bar element to act on. To make things more efficient, the progress bar itself gets a reference to its associated clip. That way, you don’t have to continue to
Listing 7: processResetButton() function function processResetButton(clip) { var resetButton = d.getElementById(“reset” + (clip.id[clip.id.length-1])); resetButton.clip = clip; resetButton.onclick = function(mouseEvent) { this.clip.currentTime = 0;
determine the appropriate object based on the ID when the user interacts with the application. Note that the relationship is bi-directional. In other words, the progress bar has a reference to the clip and the clip has a reference to the progress bar. This is important because their respective event handlers act on the other object; the clip needs to act on the progress bar and vice versa. The processProgressBar() accounts for both mouse clicks and touch. A more nuanced approach here is to conditionally wire a touch handler only in cases where a touch device is used. That requires user agent and feature detection, which is not always a trivial matter. For purposes of this article, it’s only important to acknowledge the issue so that you can be aware of it. In this mobile-first world we live in, I don’t know if it’s worth the bother to detect and instead, simply wire it up in all cases. Listing 6 illustrates how the processProgessBar() function is implemented.
} }
Listing 8: processPlayPauseDiv() function function processPlayPauseDiv(clip) { var playPauseDiv = d.getElementById((clip.id[clip.id.length-1])); playPauseDiv.clip = clip; clip.playPauseDiv = playPauseDiv; playPauseDiv.onclick = function(mouseEvent) { if (this.className == “pause”) { this.clip.pause(); } else { if (typeof(clips.activeClip.id) != “undefined”) { clips.activeClip.pause(); } this.clip.play(); } } }
26
Taming the HTML5 Audio Control
Think about what you’re trying to accomplish with your code and whether the code’s text adequately conveys that story.
The processResetButton()
Wiring up the reset button is very simple. Like the previous example, a reference to the associated clip object is created for later reference. You can see in the onclick() handler where the clip reference is used. In this case, when the resetButton (actually a div element) is clicked, the clip’s current time is reset to zero. Listing 7 illustrates how the processResetButton() function is implemented.
The processPlayPauseDiv()
By now, you should see a distinct pattern as to object setup. In this case, the playPauseDiv holds a reference
codemag.com
to its clip and the clip holds a reference to its playPauseDiv. I chose not to call it the playPauseButton because its state can change. The resetButton, on the other hand, never changes. It’s just a preference on my part. There’s certainly nothing wrong with modifying the implementation to suit your needs. In fact, such a course of action is encouraged. The onclick() event makes use of the clips.activeClip property. This code block first checks to see if what was clicked is using the pause class. If so, that tells you that the clip associated with the playPauseDiv you clicked is playing and also that no other clip is playing. Therefore, there’s no need for further processing. On the other hand, if the playPauseDiv you click is using the play class, that means that another clip may be playing. That’s why you have the clips.activeClip property. If that property contains an active reference, you simply pause that clip and play the current clip. Figure 5 illustrates the relationship between the CSS classes and the rendered HTML. What you don’t see in Listing 8 is code that handles the class assignments. The class assignments are handled in the clip event handlers discussed in the next section.
The processClip()
There are three event handlers in the processClip() function: • ontimeupdate: Fires whenever the audio control’s current time changes. Remember when you assigned the clip’s progress property to reference the progress bar? This is where it comes into play. As the clip’s time changes, the progress bar changes to reflect that percent of time played. In addition, notice where the playPauseDiv classname is changed to reflect the associated clip’s current state. • onpause: Fires when the audio control is paused. When a clip is paused, that means there’s no active clip, which is why the clips.activeClip variable is cleared. Like the ontimeupdate event, the playPauseDiv’s classname is adjusted to properly reflect the associated clip’s (audio control) state. • onplay: Fires when the audio control is played. In this handler, the clips.activeClip property is set and the pausePlayDiv’s classname is set accordingly as well.
A user interface is like a joke. If you have to explain it, it’s not that good.
Listing 9: processClip() function function processClip(clip) { clip.ontimeupdate = function() { var id = this.id; var progress = (this.currentTime / this.duration) * 100; this.progress.childNodes[1].style.width = progress + ‘%’; if (this.currentTime == this.duration) { this.currentTime = 0; this.progress.childNodes[1].style.width = “0%”; this.playPauseDiv.className = “play”; } } clip.onpause = function() { clips.activeClip = “”; this.playPauseDiv.className = “play”; } clip.onplay = function() { clips.activeClip = this; this.playPauseDiv.className = “pause”; } }
joke. If you have to explain it, it’s not that good.” In my opinion, the same can be said of code. With that in mind, think about what you’re trying to accomplish with your code and whether the code’s text adequately conveys that story. Essentially, the entire JavaScript Module is nothing more than a view model. The only difference is that in this case, I didn’t rely on a third-party framework.
Conclusion As you’ve learned in this article, even if a browser states that it has full support for HTML5, you must still test and verify. With the HTML5 Audio Control, although a mobile browser supports the function, they don’t support the control display. It’s unfortunate because it requires you to resort to alternative approaches. Nevertheless, even with the required intervention, without the aid of thirdparty Javascript frameworks and libraries, the task isn’t that difficult.
The HTML 5 Audio and Video Controls Prior to HTML 5, rendering audio and video required a third-party plug-in. Today, all major browsers support most of the HTML5/ CSS3. Nevertheless, as this article shows, there’s a dichotomy to the support as between desktop and mobile browsers. Even among desktop browsers, levels of support can vary. For more information on how to use the HTML5 Audio and Video controls, the following link is a good place to start your research: http://www.w3schools.com/tags/ ref_av_dom.asp.
This article focused on taming the HTML5 Audio Control, and I hope it’s proven instructive to you on how those other tools can work at a core level and some JavaScript recommended practices. By no means am I suggesting that you eschew JavaScript frameworks. For complex applications, they can be valuable. As it turns out, taming the HTML5 Audio Control wasn’t that difficult after all. John V. Petersen
The processClip code illustrated in Listing 9 is very clean, in large part due to much of the housekeeping already addressed. Early on, the clip was outfitted with its reference properties. Therefore, there’s no need for redundant code to search for the right objects to act on. It goes without saying that good code is clean code. I think that going from good-to-great is determined by whether the code itself can tell a story. There’s a quote that goes along the lines of “A user interface is like a
codemag.com
Taming the HTML5 Audio Control
27
ONLINE QUICK ID 1605061
Why F#? F# is a functional-first language on the .NET platform, which focuses on helping you solve complex problems with simple, clean code. I’ll show you today how writing code that is similar to C#, and writing code that uses features that are completely unique to F#, empowers you to create robust, maintainable solutions. Let’s get started!
Concise and Familiar Code
The ability to write clean and concise code that interoperates with all .NET languages is one of the most-cited favorite features of F#. It’s possible—and common—to use familiar .NET libraries from F#. Let’s compare two very simple code samples that build a console app creating a stopwatch calculating elapsed time. First, the code in C#:
Rachel Reese rachel@jet.com http://rachelree.se https://twitter.com/rachelreese Rachel Reese is a long-time software engineer and math geek who can often be found talking to random strangers about the joys of functional programming and F#. She currently handles training and evangelism for Jet.com in the NYC area, and has a habit of starting user groups, so far, in Hoboken, NJ (Pluralsight Study Group), Nashville, TN (@NashFSharp) and Burlington, VT (@VTFun). She’s also an ASPInsider, an F# MVP, a Xamarin MVP, a community enthusiast, one of the founding @lambdaladies, and a Rachii. You can find her on twitter, @ rachelreese, or on her blog: rachelree.se
using System; using System.Threading; using System.Diagnostics;
You’ll notice first that the F# is somewhat shorter and lacking in punctuation, but that it’s otherwise quite comparable. F# doesn’t need curly brackets or semicolons because it’s whitespace-sensitive. For those of you who use, or have used, StyleCop, it’s like having some of that functionality built right into the language!
For those of you who use, or have used, StyleCop, it’s like having some of that functionality built right into the language!
namespace cstopwatch { class MainClass { public static void Main (string[] args) { var sw = Stopwatch.StartNew(); Thread.Sleep(150); sw.Stop(); Console.WriteLine( “elapsed time is {0}”, sw.ElapsedMilliseconds); } } }
In addition to the lack of curly brackets, there’s a return value but no return statement. This is because every function in F# is, well, a function; it should produce the same result every time it’s given the same input. When writing F#, consider your inputs and your outputs for each function you write, and strive to minimize the sideeffects in your code. Even the main function has a return value. To make it super easy, though, the return keyword is unnecessary. In F#, the last statement in a function that’s evaluated is the return value. In this case, that’s 0.
Next, the equivalent F#:
Type Inference
open System open System.Threading open System.Diagnostics [<EntryPoint>] let main argv = let sw = Stopwatch.StartNew () Thread.Sleep 150 sw.Stop () Console.WriteLine ( “elapsed time is {0}”, sw.ElapsedMilliseconds) 0 // return an integer exit code
F# also has very powerful type inference. Based on the code samples above, it appears that F#’s type inference is similar to C#’s when using var. However, consider the following C#: public static int square(int x) { return x * x; }
In F#, type inference works even at the function and parameter level. You only have to write: let square x = x*x
The function is automatically inferred to be of type x:int -> int because the compiler has no additional information as to what type it should be using. In F#, function signatures are written much differently. This means that the function square takes in a single integer x as a parameter, and returns an integer. But here’s where it gets interesting: if you need to call that function later using a different type, you have to convert the previous function.
Figure 1: Sending code to F# Interactive
28
Why F#?
public static float inverse(float y) { return 1/(square(y)); }
codemag.com
However, with F#, it’s merely a matter of writing the new function.
(in Xamarin Studio). This loads the F# Interactive window and sends the code to the REPL. It is automatically evaluated and the result is printed, as in Figure 1 and Figure 2.
let inverse y = 1.0/(square y)
The previous function, square, is automatically updated to be of type x:float -> float now, as will be any other relevant code snippets because of the new information given to the compiler about these functions.
REPL
F# provides a REPL (Read-Eval-Print-Loop) to evaluate your code easily. It’s a way to instantly evaluate your code without compiling an entire project. You can either type code directly into the REPL, or, if you’re working in a script file, it’s simply a matter of highlighting the code you’d like to run and choosing alt-enter (in Visual Studio) or control-enter
Concise and Powerful Code F# has several features that let you succinctly model a number of situations and lead you to write beautiful, declarative code. I’ll be covering discriminated unions, option types, and pattern matching in this article.
Discriminated Unions and Pattern Matching
If you’re unfamiliar with discriminated unions, you can think of them in two ways. First, they’re similar to a C# enum type. In the snippet below, I’ve declared a DateTimeInfo that can be one of the six subtypes. type DateTimeInfo = | OrderDate | PickDate | ShipDate | DeliveryDate | CancelDate | ReturnDate
Figure 2: Code that has been evaluated in F# Interactive
codemag.com
Second, unlike an enum type, you can add qualifying information to each subtype. In this usage, the discriminated union really acts like a simple object hierarchy. Consider the following Payment type:
Why F#?
29
Listing 1: C# Simple hierarchy to compare to F# discriminated union public abstract class Payment { }
CreditCard = creditCard; }
public abstract class CreditCard : Payment { public CreditCardNumber CreditCard { get; private set; } public CreditCard(CreditCardNumber creditCard) { CreditCard = creditCard; } } public abstract class DebitCard : Payment { public CreditCardNumber CreditCard { get; private set; } public DebitCard(CreditCardNumber creditCard) {
} public abstract class CheckingAccount : Payment { public RoutingNumber RoutingNumber { get; private set; } public AccountNumber AccountNumber { get; private set; } public CheckingAccount(RoutingNumber routingNumber, AccountNumber accountNumber) { RoutingNumber = routingNumber; AccountNumber = accountNumber; } }
Pattern matching is a natural fit with discriminated unions. It’s very similar to a select case statement in C#; it’s simply a way to branch your code based on certain cases, but with several more options. See Figure 3 for the many ways of handling pattern matching in F#. C# can only handle the first pattern: the constant pattern.
These four lines of code contain the same basic information as an idiomatic version of the same code in C#, which is normally written into four separate files!
What does pattern matching actually look like? For the Payment type in the previous snippet, you have:
Figure 3: The F# pattern matching options, according to MSDN type Payment = | CreditCard of CreditCardNumber | DebitCard of CreditCardNumber | Checking of Routing * AccountNumber
Each subtype here, CreditCard, DebitCard, and CheckingAccount, are types of payments, but have slightly different requirements and need to be handled differently in each case. These four lines of code contain the same basic information as in Listing 1, an idiomatic version of this code in C#, which is normally written into four separate files. In fact, there’s even more information in the F# version, as the C# code still lacks structural equality. In order to provide that, you’d have to override the equality implementation, the comparison implementation, and the GetHashCode. Additionally, the C# version still doesn’t have proper immutability; there should be no setter at all, and the backing field should be read-only.
30
Why F#?
let makePayment payment = match payment with | CreditCard number -> payWithCredit number | DebitCard number -> payWithDebit number | Checking(rout,acc) -> payChecking rout acc
Because payment is a discriminated union, I’m able to naturally pattern-match on the sub-types, CreditCard, DebitCard, and Checking. I’m also able to access any additional information—for example, the AccountNumber and RoutingNumber for the checking account—in order to use it after the arrow. Even more useful, however, is this feature: When you add a new sub-type to the discriminated union, such as ApplePay or AndroidPay, you’ll receive a warning the next time you compile for each and every time that you have pattern-matched on the Payment type but forgotten to include the new subtype. That doesn’t happen when you add a new overloaded class in C#! Warning FS0025: Incomplete pattern matches on this expression. For example, the value 'ApplePay' may indicate a case not covered by the pattern(s).
It’s only a compiler warning so it can be ignored, but this has saved many a developer from a 3am phone call to fix a bug in production.
codemag.com
Expanded Feature Set
[<Measure>] type C // Celcius [<Measure>] type m // Meters [<Measure>] type s // Seconds
There are many features of F# that don’t exist in C#. So far, you’ve seen type inference, discriminated unions, and pattern matching, which aren’t possible in C#, but do have similarities to some C# features. Let’s next check out option types, units of measure, and type providers. None of these are fully available in C#, but type providers are a completely unique feature to F#.
let windChillAus (t:float<C>) (v:float<m/s>) rh = t + 0.33<C> * (rh/100.0 * 6.105**((17.27*t)/(237.7<C> + t))) – 0.7<C> * float(v) - 4.<C>
Units of Measure
Option Types
Do you remember the Mars Climate Orbiter crash in 1999? NASA engineers lost a $125 million machine because a crucial section of code expected metric units, but was unfortunately sent English units instead, resulting in catastrophic miscalculations. If only those engineers had used F#! There’s a feature that allows any numeric input to be tagged with any identifier, indicating what type of unit it should have. It doesn’t have to be only scientific programming; in fact, it’s easy to use units of measure in the code around a warehouse to safely stock, pick, or ship a case instead of accidentally substituting a whole pallet of something. For example, the following snippet shows how you could write a function to calculate the wind chill temperature in the US: [<Measure>] type F // Fahrenheit [<Measure>] type Mi // Miles [<Measure>] type Hr // Hour let windChillU (t:float<F>) (v:float<Mi/Hr>) = 35.74<F> + 0.6215 * t - 35.75<F> * float(v) ** 0.16 + 0.3965 * t * float(v) ** 0.16
First, declare the new types: F, Mi, and Hr, for Fahrenheit, miles, and hours. You don’t need to use standard abbreviations, although where standard abbreviations exist, using them would be a good practice. It’s also a good idea to use comments to clarify (as in my examples) especially in cases where there might be several standard units for a specific abbreviation. In general, units of measure are often collected together in a single file, near the top of your project. More on this later. Compare that code to the next example, which adds types for Celsius and kilometers, and contains the formula for calculating the wind chill in Canada. [<Measure>] type C // Celcius [<Measure>] type Km // Kilometers let windChillC (t:float<C>) (v:float<Km/Hr>) = 13.12<C> + 0.6215 * t - 11.37<C> * float(v) ** 0.16 + 0.4275 * t * float(v) ** 0.16
Here’s an interesting fact: When researching the wind chill calculation, I discovered that, although the US and Canada use very similar formulas, the formula that Australia uses is completely different and much more complicated! The US and Canada can use the simpler formulas by assuming that the temperature is already sufficiently cold—at warmer temperatures, relative humidity plays a significant role. The Australian formula looks like this, where rh is the relative humidity:
codemag.com
Option types are a special kind of Discriminated Union, similar to nullable types in C# with a few added features. In F#, anything can be optional, not just a numeric type. A string, a custom type, even a function all can be optional. Let’s consider the following function, containing a pattern match to convert a string array to a string array option. You use this in a console app to safely handle the command line arguments. let input = match argv with | [|””|] -> None | x -> Some x
You can also use combinators, which are higher-order functions, such as map, iter, and fold, to act on the optional types. In the following code, you want to retrieve the first element of an array that could be empty, so you use Array.tryHead. This returns an option, possibly containing the first element of the array. Next, you use Option.map to evaluate the Name property of that first element, if it exists. If the element doesn’t exist, the following code returns None.
Real-World F# at Jet.com! At Jet.com, we’re heavy users of F#, and all of the techniques you’ve seen in this article are leveraged extensively in our code every day. To learn more about the most exciting startup in e-commerce, visit Jet.com. To see more of our code, visit github.com/jet.
items |> Array.tryHead |> Option.map (fun item -> item.Name)
Type Providers
Let’s turn now to type providers, which is a powerful feature, completely unique to F#. Simply put, type providers are a means of accessing data, any data that has a schema. The most common type providers connect you with SQL Server, csv files, JSON, and XML, but there are type providers available for nearly all data forms. There are even type providers for other languages, including R and Python. Using type providers is quite easy, just a matter of a couple lines of code to connect to your data source. Writing a type provider is a very complicated endeavor and won’t be covered here. The benefits to using a type provider over an ORM, such as Entity Framework, are huge. • Type providers work within both script files and the REPL, making it much faster and simpler to prototype data-driven code • There are no code-generated files that need to be maintained, even for the SQL Server-type provider that sits on top of Entity Framework. The necessary code generation happens, the files are incorporated into the dll, and then they’re cleaned up. This means that your source is always in sync and there’s no code lying around that you have to remember to keep updated. Most importantly, though, your data layer is scalable to millions of types. There’s a type provider for the World Bank database, which
Why F#?
31
The first piece of code sets up the type providers. Connecting to the World Bank is only one line of code. Setting up the JSON type provider is only slightly more complicated. You must provide a sample set of JSON so that the type provider knows how to parse what it will receive. To do this, you create a literal string value, pointing to a file in the current directory. You declare the type using the sample, then call GetSample. Now you’re connected. This is all the set up needed to connect to a data source. // Get data let wb = WorldBankData.GetDataContext() [<Literal>] let Path = __SOURCE_DIRECTORY__ + “\example.json” type Venues = JsonProvider<Path> let rest = Venues.GetSample()
The main piece of code involves parsing the JSON for each city, but first, you need to load the response by calling Venues.Load. let venues = try Some(Venues.Load(nearPlaceUrl + city)) with | _ -> None
Figure 4: Using the World Bank type provider
This returns an option type, so the processing relies heavily on option combinators to process the several levels of returned information. First, you request all of the groups for that city.
F# is Fully Open-Sourced!
venues |> Option.map (fun v -> v.Response.Groups)
F# was completely open-sourced in 2010 and runs on many platforms besides Windows. For information on learning, teaching, or using F#, check out the guides on the fsharp.org site, the official home of the F# Software Foundation
contains thousands of sets of information for every country in the world. Freebase, the now-defunct online database, also had a type provider; imagine an ORM trying to manage that scale! • Type providers also provide IntelliSense in the data source, as you can see in Figure 4, which calls in to the World Bank type provider. For a database or csv file, you’ll see a list of table names and then column names. For a Web service, you see a list of available methods to call.
Your source is always in sync and there’s no code lying around that you have to remember to keep updated. Most importantly, though, your data layer is scalable to millions of types!
Let’s see an example using two type providers: the World Bank type provider and the JSON type provider. First, I’ll connect to the World Bank type provider to find the capital city for every country in the world. Then, using that information, I’ll call into the Foursquare API with the JSON type provider to determine the top venue in each capital city. For the complete code, see Listing 2.
32
Why F#?
Next, you look for the group containing “recommended” items, and ask for the first recommended item in that array. groups |> Array.filter (fun g -> g.Name = “recommended” && g.Items.Length > 0) |> Array.map (fun g -> g.Items) |> Array.tryHead
Finally, you try to return the name of that first recommended item. items |> Array.tryHead |> Option.map (fun item -> item.Venue.Name)))
None of these levels necessarily exist, yet you’re able to continue piping the requests in as normal, knowing that the code is safe. Now that you have a function to handle finding the venue for a city, you create a simple list expression to return all cities. In this case, you choose Option.iter because you’re printing each item to the output screen (and causing a side effect) rather than creating a list to manipulate later. [for country in wb.Countries -> let city = country.CapitalCity
codemag.com
if city <> “” then getVenuesFor city |> Option.iter (fun v -> printfn “Top recommended venue in %s, %s is %s” city country.Name v)]
This is all the code you need—54 lines total—to take data from two data sources, combine it, and produce a meaningful result. There’s no lingering generated code to maintain and no cached files to sort through. As soon as a new country is in the World Bank and the capital city has venues listed in Foursquare, your code will see it and print out a result.
Getting Started Now that you know all the reasons to try F#, let’s get started. If you don’t have a copy of Visual Studio installed, you can download a free copy of Visual Studio Community Edition or Xamarin Studio. There are also plug-ins available for Emacs and Atom, among others.
In F#, a code snippet can only reference another piece of code that’s above it, literally. This is true both within a file and within the solution. For example, Figure 5 shows an example project containing some of the code from this article. Code that’s placed at the end of the TP.fsx file will be able to reference all of the code in the project. Code in keys.fsx, however, won’t be able to reference any code in either Intro.fsx or TP.fsx. This completely eliminates circular references in your code.
Conclusion As you can see, using the F# language allows you to write clean, concise code to solve complex problems. Using some of the features that I’ve covered here, you can accomplish significant tasks in a very few lines of code. Visit fsharp.org, the F# Software Foundation’s official website, for additional information and learning tools. Rachel Reese
Figure 5: File listing in a solution
Once you’ve chosen your editor, it’s time to code. F# supports script files, so you don’t have to create an entire solution just to play around with code; one file can act as a whole project. To start, simply create a script file called “code.fsx” and some of the code from this article. F# is whitespace-significant, so be sure that your indentations are correct. Next, highlight the code, and choose Alt-Enter (VS) or Ctrl-Enter (Xamarin Studio). This brings up the F# Interactive window (the REPL) and runs your code automatically. One more thing to note: In order for F#’s type inference to work properly, F# also uses a strict file ordering in projects. Files are not listed alphabetically in Visual Studio. They’re listed in compile order, from top to bottom. Listing 2: Using the World Bank and JSON type providers // Get data let wb = WorldBankData.GetDataContext() [<Literal>] let Path = __SOURCE_DIRECTORY__ + “\example.json” type Venues = JsonProvider<Path> let rest = Venues.GetSample() // Parse venue data let foursquareBaseUrl = “https://api.foursquare.com/v2/” + “venues/explore?client_id=” + keys.ClientId + “&client_secret=” + keys.ClientSecret + “&v=20150907” let nearPlaceUrl = foursquareBaseUrl + “&near=” let getVenuesFor city = if city = “” then None else let venues = try Some(Venues.Load(nearPlaceUrl + city)) with | _ -> None
codemag.com
venues |> Option.map (fun v -> v.Response.Groups) |> Option.bind (fun groups -> groups |> Array.filter (fun g -> g.Name = “recommended” && g.Items.Length > 0) |> Array.map (fun g -> g.Items) |> Array.tryHead |> Option.bind (fun items -> items |> Array.tryHead |> Option.map (fun item -> item.Venue.Name))) // Get top venue by capital city [for country in wb.Countries -> let city = country.CapitalCity if city <> “” then getVenuesFor city |> Option.iter (fun v -> printfn “Top recommended venue in %s, %s is %s” city country.Name v)]
Why F#?
33
ONLINE QUICK ID 1605071
Arranging Views with Xamarin.Forms Layouts Xamarin apps run on a variety of device sizes—from small phones to bigger tablets—and the devices can change from portrait to landscape mode in an instant. The app UI needs to adapt to these size and orientation changes, look good, and be functional. Every UI framework has the notion of positioning and sizing elements on the screen. As systems have evolved, these layout tools have advanced too, adding features that simplify commonplace layout problems. It’s these layouts that provide the means to building adaptive user interfaces. In this article, I’ll look at the Xamarin.Forms layout views and show how to use them to build various UI design structures.
want to use a custom set of colors; here’s how to make that happen. Start by creating a static class and add some static properties: namespace Common { static class Hues { public static readonly Color Sky = Color.FromHex(“#226765”); public static readonly Color Fire = Color.FromHex(“#AA4B40”);
Views and Layouts Walt Ritscher waltr@scandisoft.com 425-269-5677 xamlwonderland.com @waltritscher Walt’s enthusiasm for crafting software interfaces blossomed early. Just a few days after discovering how to move pixels around a computer screen, he was devouring books on the topic of computer graphics and UI design. Before long, he was sharing his discoveries with other technology buffs, a lifelong pursuit that has led to teaching engagements at universities, private training companies, and the international conference circuit. As a consultant, he has worked with a wide spectrum of clients, including Microsoft, HP, and Intel. He’s now a staff author at Lynda.com, part of the LinkedIn family, where his content team produces hundreds of technical training courses for software developers each year. His current UI obsession revolves around the XAML space; Windows 10 Universal Apps, HoloLens, and WPF APIs. Walt is also an MVP and the author of the free Shazzam Shader Editor at Shazzam-tool.com.
The visual elements in Xamarin.Forms fall into two separate categories: controls and arrangers. Both are considered visual elements, share a common base class (View), and are collectively known as Views. But their missions are different. View classes (the controls) are responsible for gathering user input or showing information to the user. They are called controls or widgets in other UI frameworks. Layouts (the arrangers) serve a different purpose. A layout class is a View class that contains a collection of child views. It’s responsible for arranging the child views into position in the UI. It also collaborates with each child to determine the final rendering size. To be clear, layouts are also views, but are rarely called that by the community. Figure 1 shows a list of classes that derive from the View class. Most of them fall into the input/output control category. The classes outlined with red rectangle in Figure 1 are the base classes for the layout views.
Layout Classes
The layout classes do the work of arranging the elements on the screen. Each layout specializes in a style of arrangement. If you’re familiar with other XAML frameworks, many of these layout classes (shown in Figure 2) will seem familiar, albeit with different names. The simplest layout containers are the ContentView, Frame, and ScrollView. They’re designed to provide a few simple services to the child items. Although they can contain multiple child items, there’s no good reason to have more than one child in these layouts. They simply don’t offer layout options that help arrange multiple child items. The other layouts (AbsoluteLayout, Grid, RelativeLayout, and StackLayout) excel at arranging multiple child items. It’s common to mix and combine multiple layouts into the page UI. Therefore, understanding the strengths of each layout is essential for crafting your pages.
A Word About Color
The Xamarin.Forms prebuilt color palette is small. There are nineteen color constants available. For this article, I
34
Arranging Views with Xamarin.Forms Layouts
} }
In this example, I used the Color.FromHex method to define the two custom colors. The Color struct has other methods to describe colors (FromRGB, FromHlsa, and FromUint); use whichever method you prefer to define your color set. To use these custom colors in a page, add a custom XML namespace to the root element: <ContentPage xmlns=”...” xmlns:x=”...” xmlns:c=”clr-namespace:Common;assembly=Demo” >
I used the prefix c for my XML namespace. Normally, I’d use a more readable name, like common, but I chose the shorter name so that the example fits into the code snippet size constraint for the magazine. Notice the use of the clr-namespace: syntax in the namespace declaration: That’s how you indicate which .NET namespace to use. This snippet uses the Common namespace in the Demo assembly. Look at the code snippet shown earlier and you can see that the color properties are inside the Common. Hues class. Now that the c namespace is registered, I can access the colors with this snippet: BackgroundColor=”{x:Static c:Hues.Sky}”
The ContentView and Frame
ContentView is not intended to be used directly in your XAML tree. Rather, it’s designed to be a base class for building your own composite views. The Frame, which derives from ContentView, is a better choice for a wrapper around a child view. Its biggest asset is that it can render a visible border around the child. This next snippet shows how to set the OutlineColor and add a dropshadow to the Frame:
codemag.com
<Frame OutlineColor=”White” HasShadow=”True” > <Label Text=”The Label” /> </Frame>
The Frame inherits a useful property, Padding, from the Layout base class. Non-layout views don’t have margin or padding properties. The only way to add space around a view is it to put it inside a layout and set the Padding property like this:
WidthRequest and HeightRequest property on the child view. There will be times when the requested size (or desired size) is smaller than the layout container. For example, a 180-pixel width Label within a 300-pixel width StackLayout. In this scenario, the panel defers to the VerticalOptions and HorizontalOptions properties to determine where to place the child element within the extra space.
<Frame Padding=”30”> <Label Text=”The Label” /> </Frame>
Because the Padding property is part of the Layout base class, it’s settable on any of the other layout classes.
VerticalOptions and HorizontalOptions
Every view has a size that’s set by a collaboration between the child view and its layout container. Use the VerticalOptions, HorizontalOptions, WidthRequest, and HeightRequest properties to influence the final rendered size. Let’s look at the default size of an element. <Frame Padding=”10,20” BackgroundColor=”{x:Static c:Hues.Sky}”> <BoxView Color=”{x:Static c:Hues.Fire}”/> </Frame>
The example in the code snippet uses a Frame as the layout and a BoxView as the child. The BoxView is a simple view, useful for drawing solid color rectangles. I’m not specifying a width or height for the BoxView. Nor am I setting any value for the VerticalOptions and HorizontalOptions. Figure 3 shows the UI running on an Android emulator. By default, the BoxView is sized to match its container, the Frame. The blue buffer shown around the edge of the BoxView is due to the Padding = “10,20” property on the Frame. In this case, that means 10 pixels of padding on the left and right and 20 pixels of padding on the top and bottom.
Figure 1: The View classes in Xamarin.Forms
The Dynamics of Sizing
Sizing and positioning can get complicated, especially in sophisticated multilayered interfaces. Here are some pointers that will help understand the way the system works. Each view has a desired size, the size that it wants to be rendered at, if possible. The desired size is calculated internally within the child view. Take a Label as an example. Its desired width depends on many factors. How many characters are in the Text property? What is the font size? It’s the same with the desired height. The desired height will be taller when there are lots of characters, a big font size, and word wrap are enabled. In general, it’s better to let the view decide on its own size. You can overrule the desired size if necessary by setting the
codemag.com
Figure 2: The layout classes in Xamarin.Forms
Arranging Views with Xamarin.Forms Layouts
35
There are several settings available for the LayoutOptions enumeration. The discussion here is around the HorizontalOptions and it’s similar for the VerticalOptions property. • • • • • • • •
Testing on Each Platform Xamarin.Forms does an excellent job of hiding the platformspecific layout mechanics behind their layout views. Quirky issues are bound to appear during development. Be sure to test your layouts on each platform to see that the UI looks and works as expected.
Start Center End Fill (Default) StartAndExpand CenterAndExpand EndAndExpand FillAndExpand
I’ll look at the top four items in this list. LayoutOptions.Start sets the child width to the requested size and then positions the element to the side of the container. In left-to-right languages, the start position is the left side of the container, in right-to-left languages, the start position is on the right side. LayoutOptions. End does the opposite, positioning the element on the right side in left-to-right languages and on the left side in right-to-left languages. LayoutOptions.Center centers the element and keeps the requested size. LayoutOptions.Fill stretches the width of the child element to match the parent container. LayoutOptions.Fill is the default LayoutOption setting.
Color=”{x:Static c:Hues.Fire}” HeightRequest=”1400” /> </ScrollView>
Figure 4 shows the result. If you look closely at the Android and Windows Phone screenshots, you can see the scrollbar on the right side of the screen. It’s not visible on the iPhone screenshot, but that’s due to the limitations of my screen capture software.
StackLayout
This layout is one of my favorites, and I use it everywhere in my UI. It has a simple mission: position the child elements in a stack, either horizontally or vertically. The order that the child elements are added to the layout is the order in which they are stacked. This snippet adds two BoxViews with default HorizontalOptions, and three more with specific HorizontalOptions set: <StackLayout > <!-- defaults to HorizontalOptions=”Fill”--> <BoxView Color=”{x:Static c:Hues.Fire}” /> <BoxView Color=”{x:Static c:Hues.Sky}” /> <BoxView Color=”{x:Static c:Hues.Sky}” HorizontalOptions=”Start”/>
Take a look at this next snippet. The BoxView doesn’t specify a size, so it’ll get the default size. It also doesn’t indicate the VerticalOptions or HorizontalOptions either, so it defaults to the LayoutOptions.Fill value. As a result of these settings, the parent Frame stretches the BoxView to fit horizontally and vertically. <Frame Padding=”30” BackgroundColor=”#226765”> <BoxView BackgroundColor=”#AA4B40” /> </Frame>
To override LayoutOptions, use this this XAML: <Frame Padding=”30” BackgroundColor=”#226765”> <BoxView BackgroundColor=”#AA4B40” HorizontalOptions=”End” VerticalOptions=”Start” /> </Frame>
Now the BoxView is positioned to the upper-left position on my phone (because English is a left-to-right language). The BoxView isn’t stretched inside the Frame, so it will fall back to its default size of 40x40-pixels.
ScrollView
ScrollView is useful when the child content is bigger than can fit in the UI. For example, when the text content on a page is too tall to fit on the screen, wrap the Label in a ScrollView. This can make your UI readable on smaller screen sizes. Content that fits on a big-screen phone like the Nexus 6 will still be readable on the smaller Moto G phone. This code snippet shows a tall BoxView within the ScrollView: <ScrollView Padding=”30” BackgroundColor=”{x:Static c:Hues.Sky}” > <BoxView
36
Arranging Views with Xamarin.Forms Layouts
Figure 3: Default Size for BoxView on Android
codemag.com
Figure 4: ScrollView in action
Figure 5: StackLayout on the devices
codemag.com
Arranging Views with Xamarin.Forms Layouts
37
Figure 6: Horizontal layout of images
<BoxView Color=”{x:Static c:Hues.Fire}” HorizontalOptions=”Center”/> <BoxView Color=”{x:Static c:Hues.Sky}” HorizontalOptions=”End”/> </StackLayout>
<Grid.ColumnDefinitions> <!-- Absolute size--> <ColumnDefinition Width=”113”/> <ColumnDefinition Width=”37”/> </Grid.ColumnDefinitions>
Figure 5 shows the results.
An Auto-sized column’s width is determined by its content. An Auto-sized column with a 120-pixel-wide label and an 80-pixel-wide button will be set to 120 pixels. This example shows two columns with automatically sized widths:
Each item is positioned immediately below the previous one, with six pixels spacing between the items. The spacing is adjustable with the Spacing property. <StackLayout Spacing=”18”>
Figure 6 is an example of a StackLayout with a horizontal orientation: <StackLayout Orientation=”Horizontal”> <Image Source=”twitter.png”/> <Image Source=”facebook.png”/> <Image Source=”pinterest.png”/> </StackLayout>
Two-Dimensional Layout with the Grid
The grid is a time-tested way to lay out information in a two-dimensional space. Designers determine placement of important elements with gridlines. As UI design has formalized, the grid has triumphed as a prime tool in the layout workflow. The Xamarin Grid is similar to other XAML-based grids, although it has one feature that you haven’t seen before. It sports the ColumnSpacing and RowSpacing attributes, which define how much room to put between the rows and columns in the grid. <Grid ColumnSpacing=”6” RowSpacing=”4”>
Other than this small change, the other Grid attributes are familiar territory for XAML devs. For those new to XAML, here’s the overview. You define columns within a column definition section and rows in a row definition section: <Grid ColumnSpacing=”6” RowSpacing=”4” BackgroundColor=”{x:Static c:Hues.Sky}”> <Grid.ColumnDefinitions> <ColumnDefinition Width=”100”/> <ColumnDefinition Width=”Auto”/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height=”3*”/> <RowDefinition Height=”3*”/> </Grid.RowDefinitions> <!-- child elements go here. --> </Grid>
This snippet creates a two-row, two-column grid. To control the size of the row or column, use the sizing option specified with the GridUnitType enumeration. There are three choices for GridUnitType sizes: Absolute, Auto, and Star. An Absolute-size column’s width is hard-coded to the specified value. This example shows two columns with fixed widths:
40
Arranging Views with Xamarin.Forms Layouts
<Grid.ColumnDefinitions> <!-- Auto size--> <ColumnDefinition Width=”Auto”/> <ColumnDefinition Width=”Auto”/> </Grid.ColumnDefinitions>
A Star sized column (also known as a proportional size) width is determined by its relation to the other columns in the grid. Take a look at this example: <Grid WidthRequest=”450”> <Grid.ColumnDefinitions> <ColumnDefinition Width=”113”/> <ColumnDefinition Width=”37”/> <ColumnDefinition Width=”2*”/> <ColumnDefinition Width=”3*”/> <ColumnDefinition Width=”5*”/> </Grid.ColumnDefinitions> </Grid>
To determine the sizes of each column, the Grid starts by resolving the Absolute-size and Auto-size columns. In this case, the two Absolute-sized columns total 150 pixels. The Grid has requested 450-pixels width. Assuming that the Grid is rendered at 450-pixels, that leaves 300-pixels for the remaining three columns. Next, the grid tallies a sum of all the star-sized items (2* + 3* + 5*=10*) and divides each column by the column star value. The results for the third, fourth, and fifth columns are (2/10 (20%), 3/10 (30%), and 5/10 (50%)). Using these percentages, the final rendered sizes for the columns are: 113, 37, 60, 90, and 150. Even though I’m not showing examples of RowDefinitions, they work the same way for adjusting row heights. Once the grid is defined, it’s time to add the child elements. Unless otherwise indicated, a child is positioned in column 0, row 0. To modify the default placement, use the Grid.Column and Grid.Row attributes. <BoxView Grid.Row=”1” Grid.Column=”2”/>
You can also use the Grid.ColumnSpan and Grid.RowSpan attributes to specify how many columns and rows the child view should span. <BoxView Grid.RowSpan=”2” Grid.ColumnSpan=”4” />
AbsoluteLayout
Use the AbsoluteLayout when you need precise control over the x and y positions, and the width and height of the child elements. It has its place in the layout world. In
codemag.com
most situations, it’s better to use one of other layouts; they make for a more responsive design. Absolute layout harkens back to the old days when we brute-forced screen elements into position. Although that’s less common now, it does have its place for certain layout challenges. Each child element describes its location and size with the AbsoluteLayout.LayoutBounds attribute. Use a comma-separated list to indicate the bounding rectangle (x, y, width, and height) values: <AbsoluteLayout> <BoxView AbsoluteLayout.LayoutBounds=”90, 50, 75, 25” BackgroundColor=”{x:Static c:Hues.Sky}” /> </AbsoluteLayout>
Proportional sizes are an improvement over the absolute sizes; an even better option is to use the RelativeLayout.
RelativeLayout
A RelativeLayout is useful for creating adaptive layouts where the UI adjusts to screen rotation and screen sizes. Use it to lay out UI elements based on their parent’s position and dimensions as well as other views’ positions and dimensions. Another key benefit of the RelativeLayout (and the AbsoluteLayout) is the ability to overlap or layer child elements. Each element in the RelativeLayout can define a constraint, which is a rule that defines how the element relates to other items (parent, siblings). There are several constraint types available:
The parameters for the AbsoluteLayout.LayoutBounds attribute are as follows: • • • •
X : the horizontal position of the element Y: the vertical position of the element Width: the width of the element Height: the height of the element
This snippet results in a 75-pixel wide, 25-pixel tall BoxView that is positioned 90-pixels left and 50-pixels down from upper-left corner of its parent. As I said, hard-coded size and position is a throwback to older times. It’s better to use a proportional size in the AbsoluteLayout. To switch to proportional layout, use the LayoutFlag attribute: <AbsoluteLayout> <BoxView AbsoluteLayout.LayoutFlags=”All” AbsoluteLayout.LayoutBounds=”90, 50, 75, 25” BackgroundColor=”{x:Static c:Hues.Sky}” /> </AbsoluteLayout>
This sets all four values in the LayoutBounds attribute to proportional size. Now the BoxView is 75% of the width of the parent, instead of 75-pixels. It’s the same for the other three values; for example, the top of the BowView is located at the halfway mark in the parent instead of 50-pixels from the top. The All flag sets each value in the bounding box to proportional. There are other flags that switch on proportional values for the other bounding box values: <AbsoluteLayout> <BoxView AbsoluteLayout.LayoutFlags=”WidthProportional” AbsoluteLayout.LayoutBounds=”90, 50, 75, 25” BackgroundColor=”{x:Static c:Hues.Sky}” /> </AbsoluteLayout>
The WidthProportional flag sets the Width to a proportional value and the other three values remain absolute. Check out the Xamarin documentation to see all the combinations.
codemag.com
• • • •
RelativeLayout.YConstraint RelativeLayout.XConstraint RelativeLayout.WidthConstraint RelativeLayout.HeightConstraint
Xamarin Test Cloud
The names are self-explanatory. It’s not hard to understand what each constraint type does. Next, I’ll look at several of the constraints to show you how they work. Imagine a scenario where you have two views, a Label and a Button. The Label is positioned at the top of the RelativeLayout and set to 90% of the parent’s width. The Button goes below the Label and matches the Label’s width and is positioned at the left edge of the button. The width of the RelativeLayout changes when the user rotates the phone to landscape mode. The Label’s width needs to adjust accordingly and the left position of the Button needs to change too.
The Xamarin Test Cloud is a paid service available from Xamarin. Within the cloud are thousands of physical devices, ranging from older Android phones to the newest iPhones. This is an effective way to test your Xamarin.Forms user interface and verify that it looks and acts as anticipated.
A constraint can have the following values: • ElementName: the name of the element that the constraint is relative to • Type: whether the constraint is relative to the parent or another view • Property: which property on the relative element to use for the constraint • Factor: the multiplier for the constraint • Constant: an offset for the constraint Here’s the XAML for the Label: <RelativeLayout BackgroundColor=”{x:Static c:Hues.Sky}” > <Label x:Name=”MainLabel” Text=”Example” BackgroundColor=”Black” FontSize=”24” RelativeLayout.WidthConstraint= “{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.9, Constant=0}” RelativeLayout.XConstraint= “{ConstraintExpression Type=RelativeToParent, Property=X, Constant=20}” />
Arranging Views with Xamarin.Forms Layouts
41
The Label has two constraints, WidthConstraint and XConstraint. The Type=RelativeToParent parameter sets the constraint to the parent RelativeLayout. The width of the Label is tied to the parent width with the Property=Width value and the Factor=0.9 sets the Label width to 90% of the parent. The Property=X and Constant=20 values set the X Position of the Label. Here is the XAML for the Button: <Button Text=”Do Work” RelativeLayout.YConstraint=
“{ConstraintExpression Type=RelativeToView, ElementName=MainLabel, Property=Y, Constant=40}” RelativeLayout.XConstraint= “{ConstraintExpression Type=RelativeToView, ElementName=MainLabel, Property=X, Constant=0}” />
Note the use of Type=RelativeToView and Element Name=MainLabel to set the YConstraint and XConstraint to the MainLabel view. Next, set the Property=Y and Constant=40 values to position the Button 40 pixels below the Label. Also use the Property=X and Constant=0 values to keep the left edge of the Button aligned with the left edge of the Label. Figure 7 shows the RelativeLayout in action on the Android emulator. Figure 8 shows the same UI in landscape mode.
Conclusion
Generally speaking, the Xamarin layout views follow a similar structure to other XAML frameworks. They’re designed to work in a cross-platform situation, which results in some unfamiliar territory for XAML veterans. Each layout solves a specific problem. Once you understand how each one works, you’ll find yourself layering them together to build the UI of your dreams. Walt Ritscher
Figure 7: Label and Button and RelativeLayout
Figure 8: Label and Button and RelativeLayout in Landscape mode
42
Arranging Views with Xamarin.Forms Layouts
codemag.com
ONLINE QUICK ID 1605081
Integrating ASP.NET MVC and AngularJS AngularJS took the Web development world by storm. Overnight, writing Single Page Applications (SPAs) was the hot new thing and everyone wanted to do it. Most people set out to learn via books, blogs, and video courses, but there’s one problem with that. Angular is a Google product and has no dependency on the Microsoft stack. I’m not saying this as a negative statement, it’s just a fact. Most training uses other editors and teaches you how to use HTML and JavaScript (Angular), period. But many of us are Microsoft developers and have a lot invested in the Microsoft stack, so we shouldn’t have to turn away from it in order to jump on the Angular bandwagon.
Miguel Castro miguelcastro67@gmail.com @miguelcastro67 Whether playing on the local Radio Shack’s TRS-80 or designing systems for clients around the globe, Miguel has been writing software since he was 12 years old. He insists on staying heavily involved and up-to-date on all aspects of software application design and development, and projects that diversify into the type of training and consulting he provides to his customers. He believes that it’s never just about understanding the technologies but how technologies work together. In fact, it’s on this concept that Miguel bases his Pluralsight courses. Miguel has been a Microsoft MVP since 2005 and when he’s not consulting or training, he speaks at conferences around the world, practices combining on-stage tech and comedy, and never misses a Formula 1 race. But best of all, he’s the proud father of a very tech-savvy 12-year-old girl.
44
Integrating ASP.NET MVC and AngularJS
If you start to look into typical Angular training, you’ll learn how to ditch Visual Studio, learn Web Storm and Node, and dive head first into the world of HTML and JavaScript without many—if any—other dependencies or additional technologies. This isn’t totally bad because if you’re going to learn something like Angular, you do need to learn it in full and understand all its parts intimately. This article is not about that. This article won’t teach you Angular from the ground up. There’s a lot of great material, both printed and digital about Angular and how to work with it. This article is about showing you how to leverage your ASP.NET MVC skills together with your Angular skills in order to have your cake and eat it too. Not only will I show you how to integrate the two technologies into a hybrid app, but I’ll also teach you my design and organization style that I use everywhere I have to build these kinds of applications, including file location choices and coding conventions that I hope you’ll also find beneficial.
Leverage your ASP.NET MVC skills together with your Angular skills in order to have your cake and eat it too.
The Traditional SPA Frameworks like Angular, Ember, Backbone, and even Knockout are used to build something that’s become known as a Single Page Application, or SPA. IMHO, this is one of the most misused and dangerous terms I’ve seen come around in a long time. You see, the sad truth is that many people take the terms they hear very literally, and in this case, they set out to write a Web application that is indeed one single page with just a lot of view templates swapped in and out of a placeholder. If the term SPA is to be taken literally and we all set out to write our Web applications in this fashion, things start out nice, pretty, and simple at first, but as the application grows, all hell can break loose. Why would I say something like that? Well, because literal SPAs have some limitations and can grow to be hard to manage, especially in a team environment.
The Single Layout Shell (w/o ASP.NET)
I’m going to start by explaining and illustrating a traditional Angular SPA. Keep in mind that I won’t be explaining what views and view-models are or how MVVM binding works. I’ll be assuming that anyone reading this has Angular knowledge already, and later I’ll be making the same assumption about ASP.NET MVC skills. By starting with a single layout SPA, and then building on my examples to show you how to handle a more complex site with more than one layout, I’ll be able to describe the problems I’ll solve later when I introduce ASP.NET MVC into the mix. A typical Angular-based SPA has an HTML view that sets up the layout of the website and a template placeholder noted by the ng-view directive. This HTML page also needs to load up any necessary JavaScript file, including, but not limited to, the Angular framework itself, the site’s module, and all required view-models. What I call viewmodels are, in fact, what Angular refers to as controllers. Listing 1 shows you a sample Angular shell view called Index.html. Notice that it uses an Angular module called appMain along with an Angular view-model called indexViewModel. I refer to the Index.html view as the “shell view” because its content defines the shell layout for this simple SPA site.
What I call view-models are, in fact, what Angular refers to as controllers.
The layout for this site is very simple. It consists of a heading provided by the variable headingCaption, a horizontal line, and some content beneath it. The content swapped into the ng-view directive comes from various HTML templates, each binding to their own view-model. And of course, this is all configured in the module setup using Angular’s routing capability, as shown in Listing 2 along with the indexViewModel view-model that’s bound to this layout view in its <html> tag.
Basic Routing
Each one of the HTML templates displays not only content but also links that route to other views. These links are intercepted by the routes defined in Listing 2 and the appropriate view and view-model is brought together and replaced as the content in the ng-view placeholder. The first route in the routing definition shows what view and view-model is used when the routing path is a simple “/”,
codemag.com
meaning the root of the application. The views for the single-shell-layout app are shown in Listing 3 and the rest of the view-models are in Listing 4. Take a look at the first view in Listing 3, Home.html. As you can see, it shows a heading variable and two simple links to the routes /customer/list and /customer/detail. I’ll use the first link to describe what happens. This route gets picked up by Angular and runs through the routing table, and the result is the display of the CustomerList.html view bound to the customerListViewModel view-model.
the initial browsing URL ({host}/Index.html), it runs that route through the routing table. Because there’s no route that specifies that page, the otherwise clause is hit and the route switches over to /customer. This not only loads the CustomerHome.html template and binds it to the homeViewModel view-model, but it also has the effect of changing the URL that appears on the browser bar to /customer. Without some additional help, I can’t “friendly-up” the URLs any further for that initial browsListing 1: A simple Angular shell view
The ngRoute Angular module is necessary for angular routing to function.
It’s important to understand the order of operation here as it will become even more important in the next section, and is crucial to understand when I begin involving ASP.NET MVC. The initial browsing must happen to Index.html. Until this page loads, Angular hasn’t loaded and the routing tables haven’t been defined. Once this page is rendered, Angular takes over and remembering
<html data-ng-app=”appMain” data-ng-controller=”indexViewModel”> <head> <title>{{ headingCaption }}</title> <link href=”Content/bootstrap.min.css” rel=”stylesheet” /> </head> <body> <h2>{{ headingCaption }}</h2> <hr /> <div ng-view></div> <script src=”Scripts/angular.min.js”></script> <script src=”Scripts/angular-route.min.js”></script> <script src=”Scripts/App.js”></script> </body> </html>
Listing 2: A simple Angular module and routes var appMainModule = angular.module(‘appMain’, [‘ngRoute’]) .config(function ($routeProvider, $locationProvider) { $routeProvider.when(‘/’, { templateUrl: ‘/Templates/Home.html’, controller: ‘homeViewModel’ }); $routeProvider.when(‘/customer/list’, { templateUrl: ‘/Templates/CustomerList.html’, controller: ‘customerListViewModel’ }); $routeProvider.when(‘/customer/detail’, { templateUrl: ‘/Templates/CustomerDetail.html’, controller: ‘customerDetailViewModel’
}); $routeProvider.otherwise({ redirectTo: ‘/’ }); $locationProvider.html5Mode({ enabled: true, requireBase: false }); }); appMainModule.controller(“indexViewModel”, function ($scope, $http, $location) { $scope.headingCaption = ‘Customer Maintenance’; });
Listing 3: Views Single Shell Layout App Home.html <h2>{{ heading }}</h2> <br /> <br /> <a href=”/customer/list”>Go to customer list view</a> <br /> <a href=”/customer/detail”>Go to customer detail view</a> CustomerList.html <h3>{{ heading }}</h3> <br /> <table class=”table table-hover”> <thead> <tr> <th></th> <th>First Name</th> <th>Last Name</th> </tr>
codemag.com
</thead> <tbody ng-repeat=”p in people”> <tr> <td> <a href=”#” ng-click=”showPerson(p)”>select</a> </td> <td>{{ p.FirstName }}</td> <td>{{ p.LastName }}</td> </tr> </tbody> </table> <br /> <br/> <a href=”/customer/detail”>Go to customer detail view</a> CustomerDetail.html <h3>{{ heading }}</h3> <br /> <a href=”/customer/list”>Go to customer list view</a>
Integrating ASP.NET MVC and AngularJS
45
ing experience. There is no ASP.NET involved here at all, so the browsing is physically performed directly to the Index.html page. You can add as many views/view-models as you need to continue building this single-shell SPA application. Each requires an entry in the module’s routing definition and each requires the shell view, Index.html, to load its necessary JavaScript file. In the example, all of the view-models are contained in the App.js file that’s loaded by the shell view, but remember that this file can grow and grow and grow as the application gets larger. The first drawback here is that your user may never get to use some of the JavaScript view-models because he never even gets to a particular view. In small apps, this may not be a problem, but in larger ones, it can result to the unneeded loading of a lot of JavaScript that may never be used. This can easily become the case if the application grows to a point where there are different sections, for example, Customer, Product, and Order sections. The second drawback is that this single-shell application is, in fact, a SPA in its entirety. This means that I can add views for product- and order-based information and their corresponding view-models, and all of them load into the existing ng-view directive that you saw in the index.html file. This limits you to a single layout for the entire site. That sounds fine, except that now we’re talking about multiple sections of the application. It’s normal for a site as a whole to share a common theme, but
it’s also very possible for different site sections to have a sort-of sub-theme within themselves. Perhaps you want the Product section of the site to show some fixed information on the left and the switchable content on the right, whereas the Customer section is to show some fixed information on the right with the switchable content on the left. With the existing stack, you’re now forced to have multiple shell views. The challenge there is code-repetition and navigation. First of all, I now need more than one shell view. Secondly, to navigate to that shell view, I need a server trip. This isn’t a problem; I just need to know that the navigation currently taking place with the anchor tags is being routed by the client through the Angular routing engine. Obviously, this means it isn’t doing a server trip and any view that’s loaded gets placed in the ng-view placeholder. This is not what I want if I need to do what will now become a kind of cross-shell-view navigation.
Multi-SPAs What I’m really after here is two separate SPAs, if you will. One for the Customer section of my site and another for the Product section. I’m after two separate sections because the shell layout of each one will be different. If I make the Customer and Product sections both as part of my original shell and add navigation information to the Angular routing definition, I’m forced to share the visible layout as defined by my single shell view.
Listing 4: All other View-Models for Single Shell Layout App { FirstName: ‘Kevin’, LastName: ‘Goff’ } ];
appMainModule.controller(“homeViewModel”, function ($scope, $http, $location) { $scope.heading = ‘This is the Home view.’; }); appMainModule.controller(“customerListViewModel”, function ( $scope, $http, $location) { $scope.heading = ‘Customer List View’; $scope.people = [ { FirstName: ‘Miguel’, LastName: ‘Castro’ }, { FirstName: ‘Rod’, LastName: ‘Paddock’ }, { FirstName: ‘Sahil’, LastName: ‘Malik’ }, { FirstName: ‘John’, LastName: ‘Petersen’ },
});
$scope.showPerson = function (person) { alert(‘You selected ‘ + person.FirstName + ‘ ‘ + person.LastName); }
appMainModule.controller(“customerDetailViewModel”, function $scope, $http, $location) { $scope.heading = ‘Customer Detail View’; });
Listing 5: CustomerIndex.html <a href=”/ProductIndex.html” target=”_self”> <html data-ng-app=”appCustomer” data-ng-controller=”customerIndexViewModel”> Go to the product home page (link-based) <head> </a> <title>{{ headingCaption }}</title> <br /> <link href=”Content/bootstrap.min.css” rel=”stylesheet” /> <a href=”#” ng-click=”ProductHome()”> </head> Go to the product home page <body> </a> <h2>{{ headingCaption }}</h2> </div> <hr /> <div class=”col-md-9”> <div class=”row”> <div ng-view></div> <div class=”col-md-3”> </div> <a href=”/customer/list”> </div> Go to customer list view </a> <script src=”Scripts/angular.min.js”></script> <br /> <script src=”Scripts/angular-route.min.js”></script> <a href=”/customer/detail”> <script src=”Scripts/AppCustomer.js”></script> Go to customer detail view </body> </a> </html> <br />
46
Integrating ASP.NET MVC and AngularJS
codemag.com
codemag.com
Title article
47
Listing 6: ProductIndex.html <html data-ng-app=”appProduct” data-ng-controller=”productIndexViewModel”> <head> <title>{{ headingCaption }}</title> <link href=”Content/bootstrap.min.css” rel=”stylesheet” /> </head> <body> <h2>{{ headingCaption }}</h2> <hr /> <div class=”row”> <div class=”col-md-3”> <a href=”/product/list”> Go to product list view </a> <br /> <a href=”/product/detail”>
Go to product detail view </a> <br /> <a href=”/CustomerIndex.html” target=”_self”> Go to the customer home page </a> </div> </div> <script src=”Scripts/angular.min.js”></script> <script src=”Scripts/angular-route.min.js”></script> <script src=”Scripts/AppProduct.js”></script> </body> </html>
Multiple Layouts
Using raw HTML and AngularJS, I can break up my application into sections, each with its own shell layout. Each section is, in effect, a SPA. What’s important to understand here is that each section is rendered with a server trip, just like the Index.html page in my previous example, and everything housed within it is handled by the client. I’ve set up two index pages, called CustomerIndex.html and ProductIndex.html and they can be found in Listing 5 and Listing 6. Note that each of these represents a fully valid HTML page, complete with html, head, and body tags, and each loads up the appropriate Script and Content files. Each of these two pages is also the container for the Angular ng-view directive, but notice that the layout slightly varies. The Customer page has some navigation links on the left, with the ng-view on the right. The Product page has them reversed. I made the difference in layout simple for the sake of the article, but this could have just as well been a massive visual difference between Customer and Product sections, complete with images and everything. Navigation within and without of the current SPA occurs a little different, as I’ll explain soon. Each of these shell views loads its own Angular module and set of view-models. The files that contain this JavaScript are called AppCustomer.js and AppProduct.js. The routing table definitions for both sections vary in their URLs of course. What is also very different from my first example is the URL path that renders the Home view, which is essentially the landing view for this SPA. In my first example, the Index.html view came up as the root of the site. In this example, I don’t really have a root of the site, though I most certainly could have, if needed. Instead, the Customer section’s landing view comes up if the path is “/customer”. $routeProvider.when(‘/customer’, { templateUrl: ‘/Templates/CustomerHome.html’, controller: ‘homeViewModel’ });
Similarly, the Product section’s landing view comes up if the path is “/product”. As I explained earlier, the initial browsing point is the shell view, either CustomerIndex. html or ProductIndex.html. It’s not until one of these pages loads that Angular can take over and provide additional routing help. This lack of URL friendliness is one of the problems I’m going to address later when I introduce ASP.NET MVC into the mix. By browsing to the
48
Integrating ASP.NET MVC and AngularJS
CustomerIndex.html page, Angular bootstraps and the modules and view-models are defined, along with the routing table. And, as in my first example, the original URL, {host}/CustomerIndex.html, is remembered by Angular. This means that when the page loads and the ng-view is encountered, Angular looks at the routing table to see what view/view-model needs to be loaded into that placeholder. Because no route actually matches the CustomerIndex.html path, the “otherwise” clause is hit and the route switches over to “/customer”, loading the CustomerHome.html template into the placeholder as well as replacing the URL displaying in the browser bar. A rather negative side effect of my current routing definition is that if I load up CustomerIndex.html, letting it go through the process I described above, and then clicked “refresh,” I’m presented with a 404 error. This is because Angular has changed the URL in the browser bar to the one that matches the route it was able to process, in this case “/customer”. Hitting the refresh button causes a complete reload of that URL from the server. Because we’re not using ASP.NET, there’s nothing to handle the route “/customer” and so a 404 is returned. This can be fixed by adding a route to the routing definition. $routeProvider.when(‘/CustomerIndex.html’, { templateUrl: ‘/Templates/CustomerHome.html’, controller: ‘homeViewModel’ });
Although this is an acceptable solution, because this route is now found by Angular, it’s the route that displays in the browser bar. You can decide if this is good or bad. It certainly isn’t a pretty URL, and it is, in fact, one of the problems I’ll address with ASP.NET later. As in my first example, you can click on links that are configured to a route in their href attribute, and that route will be parsed by Angular. However, to jump sections and go out of the current SPA is a different story. Traditionally, an href route is handled by Angular and the proper view template and view-model is loaded and displayed in the shell view’s ng-view placeholder. If I’m sitting somewhere in the Customer section and want to jump to somewhere in the Product section, I have to handle things a little differently. My goal now becomes trying to route over to {host}/ProductIndex.html from somewhere in the Customer section. Although it may seem that you can easily
codemag.com
do this by simply adding another route to the Customer’s routing table, it can get a little complicated. Because I’m sitting in the Customer section, it’s the appCustomer Angular module that’s currently loaded, and thus its viewmodels. Adding a route definition for /ProductIndex.html tells Angular that a view template and view-model needs to be loaded from the current module and the view placed in the CustomerIndex.html’s ng-view placeholder. Now you see the problem. These two sections should be treated separately, as indeed, they’re separate SPAs. If you look back at Listing 5, you’ll notice that the anchor-tags for the links differ a little. The first two are customer-section-based and are routed easily by the current routing table, as defined in the appCustomer Angular module. The other two links are designed to jump to the Product section by loading ProductIndex.html. What makes the first link unique is the target attribute.
customerIndexViewModel is the view-model bound to the shell view and logically sits above the ng-view directive, thus sitting above any view-models bound to views placed in the placeholder. View-models bound to views that render in the ng-view placeholder are considered nested view-models and have access to $scope variables set by the view-model bound to the shell view. This is the case for anything I place in the $scope variable in this view-model. The rest of the appCustomer.js file is the same as I showed you in Listing 4, when I discussed the single-shell app. It consists of the appCustomer Angular module with its routing definitions, as well as all the same view-models that are in the single-shell-layout app. The rest of the Product section is set up very similarly to the Customer section. It consists of the shell view that you’ve already seen, as well as the appProduct.js file that contains the appProduct Angular module, routing definitions, and all view-models. The sidebar shows you where you can download the complete set of projects.
<a href=”/ProductIndex.html” target=”_self”> Go to the product home page </a>
Without it, that route is handled by the currently loaded Angular routing table and will, of course, fail. Proactively specifying a target causes the route to execute a full pageload from the server, just as if you altered the browser bar yourself and pressed enter. This starts the process from the very beginning, but loads up the ProductIndex.html page instead, thereby loading its Angular module, defining its own routing table, and defining its own view-models. The second link to the Product section demonstrates how to accomplish this from the view-model. There are many times that you need to execute code before navigating somewhere. For these scenarios, you can simply tell the link to call a function in the view-model. In this case, I’m calling a function called ProductHome. This function is defined in the customerIndexViewModel like this: appCustomerModule.controller(“customerIndexViewModel”, function ($scope, $http, $location, $window) { $scope.headingCaption = ‘Customer Maintenance’; $scope.ProductHome = function () { // assume it’s a route in the current SPA //$location.path(‘/ProductIndex.html’); // perform a full page-load $window.location.href = ‘/ProductIndex.html’; } });
The two techniques you see are the equivalent of an anchor tag with or without a specified target attribute. The one that’s commented out looks for the route within the current SPA and is handled by the currently loaded routing table. This is the procedural equivalent to a standard <a> link WITHOUT a target attribute. The second one, which is the active code, performs a full page-load. Notice that each of these depends on an Angular service, $location or $window, which I inject into the view-model. A nice benefit of where I decided to place the ProductHome function is that it’s available to all the view-models loaded by the appCustomer module. This is because the
codemag.com
View-models bound to views that render in the ng-view placeholder are considered nested view-models and have access to $scope variables set by the view-model bound to the shell view
Although the multi-shell approach I’ve taken here is quite acceptable, notice that the two shell views do have some repetitive code. The navigation links and ng-view placeholder are reversed in position and the rest of the view is exactly the same. The header is the same, as is the loading of the scripts. The exception is the AppCustomer.js versus AppProduct.js scripts that load appropriately for each view. Common code can be extended to become a full header and footer, site-map information, or anything you need to give your site a polished production lookand-feel. In such a case, all that common markup needs to be duplicated in each shell view. Although I’m certain that there are products or frameworks out there to help you accomplish this, you’re reading this because you’re an ASP.NET developer, so why don’t I show you how to leverage what you already know and accomplish this easily?
The ASP.NET MVC/Angular Hybrid App I know I’ve said this previously, but I still can’t help but be amazed at how many developers see AngularJS applications and ASP.NET MVC applications as two different things. Yes, they are two different technologies, but I think there is too much of an “our way or their way” mentality in Web development. Our way, meaning with the Microsoft stack, and their way meaning without. Coming from a Web Forms background, it took me a while to get on the MVC bandwagon, but once I did, I never looked back. Having said that, I rarely develop an application in the conventional ASP.NET MVC fashion. By conventional, I mean using MVC controllers for Gets and Posts, and using Razor with HTML Helpers for all of my form rendering. This was great when it came out, but we have so many better ways to do it now! Again,
Integrating ASP.NET MVC and AngularJS
49
I never looked back. ASP.NET MVC remains a fantastic delivery platform for HTML, and JavaScript for that matter. That statement is key in my integration of MVC and Angular, because itâ&#x20AC;&#x2122;s in that capacity that I will concentrate next.
Overview
The overall architecture of this Hybrid App, as Iâ&#x20AC;&#x2122;m calling it, is quite simple and is displayed in Figure 1 and Figure 2. The Web application itself is, of course, an ASP.
NET MVC site and renders one or more views using an MVC controller and action as any other MVC app. Primary site navigation is defined in the Layout page and uses the MVC routing engine to route to other MVC views. These views constitute the root of each site section. Note that previously, a site section was an entire HTML page, each with repeated static content. The root view of each section is considered the root of a SPA and renders everything that SPA needs, and provide the layout for that SPA. In
Figure 1: Architecture of the Web Application
Figure 2: Structure of a SPA Silo
50
Integrating ASP.NET MVC and AngularJS
codemag.com
Listing 7: The ASP.NET MVC Layout View <!DOCTYPE html> <html> <head> <meta charset=”utf-8” /> <meta name=”viewport” content=”width=device-width, initial-scale=1.0”> <title>@ViewBag.Title - ASP.NET MVC with Angular JS</title> @Styles.Render(“~/Content/css”) @Scripts.Render(“~/bundles/modernizr”) </head> <body data-ng-app=”main”> <div class=”navbar navbar-inverse navbar-fixed-top”> <div class=”container”> <div class=”navbar-header”> <button type=”button” class=”navbar-toggle” data-toggle=”collapse” data-target=”.navbar-collapse”> <span class=”icon-bar”></span> <span class=”icon-bar”></span> <span class=”icon-bar”></span> </button> @Html.ActionLink(“ASP.NET MVC/Angular JS”, “Index”, “Home”, new { area = “” }, new { @class = “navbar-brand” }) </div> <div class=”navbar-collapse collapse”> <ul class=”nav navbar-nav”> <li>@Html.ActionLink(“Customers”, “Customer”, “Home”)</li> <li>@Html.ActionLink(“Products”,
the previous examples, I referred to these views as shell views. This layout is rendered within the layout defined for the site as a whole in the MVC Layout page. This is illustrated in Figure 1. Once in a site section, we’re inside a SPA and, for the most part, everything is the same as the previous SPA examples I’ve shown you. I’ll go through the order of operation starting with what is now the primary shell of the entire Web application, the Layout page.
The Layout Page
In the previous example, I demonstrated how to set up an application with two sections, each being its own SPA and each having its separate Angular module, views, and view-models. However, you might recall that I was forced to repeat some code in each of the shell views. Now I’ll work in an ASP.NET MVC application with Web API services. This is a standard choice when creating a Web project in Visual Studio. My project will also have the AngularJS and Bootstrap NuGet packages installed. I won’t be making any adjustments to the Global.asax or the Web.config in the interest of simplicity, but if you use my techniques, you’re free to add anything you need to your Web application, including a DI container, custom filters, or anything else. As in the prior example, this site will have a Customer section and a Product section. The code download that accompanies this article also includes an Order section but in the interest of space, I won’t be covering it in the article. The shell views, as I described them before, still exist, but as CSHTML views. These views start by sharing a common
codemag.com
“Product”, “Home”)</li> <li>@Html.ActionLink(“Orders”, “Order”, “Home”)</li> </ul> </div> </div> </div> <div class=”container body-content”> @RenderBody() <hr /> <footer> <p>&copy; @DateTime.Now.Year</p> </footer> </div> @Scripts.Render(“~/bundles/angular”) @Scripts.Render(“~/bundles/jquery”) @Scripts.Render(“~/bundles/bootstrap”) <script type=”text/javascript”> window.MyApp = {}; MyApp.rootPath = ‘@Url.Content(“~”)’; </script> <script src=”~/App/Validator.js”></script> <script src=”~/App/App.js”></script> @RenderSection(“scripts”, required: false) <script type=”text/javascript”> @RenderSection(“jsCode”, required: false) </script> </body> </html>
Layout page. This pattern isn’t new and is core to any ASP. NET MVC application. The Layout page sets up the common look and feel for the entire application. This means that it needs to define the static content that resides outside of each SPA section. The Layout page in its entirety can be seen in Listing 7. I refer to each SPA section as a SPA Silo, a term coined by my friend, author Brian Noyes. Navigation links on the Layout page route to a conventional MVC controller action, as in any other ASP.NET MVC application. <ul class=”nav navbar-nav”> <li> @Html.ActionLink(“Customers”, “Customer”, “Home”) </li> <li> @Html.ActionLink(“Products”, “Product”, “Home”) </li> </ul>
The body of the Layout page can look however you want and contain any static content you need. The placeholder for the actual SPA Silo that makes up the section of the site is a standard ASP.NET MVC RenderBody statement. <div class=”container body-content”> @RenderBody() <hr /> <footer> <p>&copy; @DateTime.Now.Year</p>
Integrating ASP.NET MVC and AngularJS
51
The bottom of the Layout page adds any scripts that are to be common to the entire application. This is one of the items that I needed to repeat in each shell view in the previous example. Here, I only need to add it once in the Layout view. What I won’t be adding in this view are the Angular modules and view-models used by each SPA Silo.
<script> tags needed for that SPA Silo. Remember, this view constitutes the Root View of that SPA Silo, similar to the shell views in the previous example, but one level beneath the primary Layout page, which is now the site’s main shell. The second section is called jsCode and resides inside <script> tags, so the code contained in that section is raw JavaScript. Later, when views are rendered by the RenderBody statement, whatever is placed in the scripts and jsCode sections is rendered in these placeholders of the Layout view.
@Scripts.Render(“~/bundles/angular”) @Scripts.Render(“~/bundles/jquery”) Scripts.Render(“~/bundles/bootstrap”)
If you look at the complete Layout page code in Listing 7, you may notice that I’m bootstrapping an Angular module called main in the <body> tag.
Next, I want a set of a global variables that contains the root URL to this application. This makes it easy to deploy anywhere later without worrying about what the root address is. This problem rears its ugly head quite a bit when you’re developing applications on your localhost using a virtual directory, and then deploy to an actual website where your applications sit at the root level. In many situations, you may need to refer to the root path of the application and having this variable handy makes that much easier.
<body data-ng-app=”main”>
</footer> </div>
<script type=”text/javascript”> window.MyApp = {}; MyApp.rootPath = ‘@Url.Content(“~”)’; </script>
The root path of the application is easily obtained at the server, so combining Razor syntax here with JavaScript will work nicely. Remember, this view is being parsed and rendered by ASP.NET, so I’m free to include and leverage any server-side power that I want. The creation of the MyApp namespace is so not to pollute JavaScript’s global namespace and avoid any potential conflict—although the possibility of a conflict is minimal. Now I need to load up any additional scripts that the entire site will use. <script src=”~/App/App.js”></script>
The App.js file contains JavaScript code that’s available and leveraged to the entire application, meaning all SPA Silos. I’ll go over its design and contents in detail later. Notice that this file resides in the App folder. This folder contains all of the Angular-related code for the entire application. You’ll see other App.js files in this same Web application later, but their locations will be very different. The last two parts of the Layout page are code section definitions. This is a feature that’s been used by traditional MVC applications to render HTML code-parts but can also be used for scripts. I’ve added two sections, both nonrequired. @RenderSection(“scripts”, required: false) <script type=”text/javascript”> @RenderSection(“jsCode”, required: false) </script>
The first, called scripts, will be used by the view that gets rendered by the RenderBody statement to contain any
52
Integrating ASP.NET MVC and AngularJS
This module is the only Angular module bootstrapped declaratively. Angular doesn’t allow more than one module to be assigned using data-ng-app, but it doesn’t mean that only one module is allowed. Later, you’ll see that this is the reason for the aforementioned jsCode section of the Layout page. Also, if I wanted, I could also assign an Angular view-model to the Layout page using the data-ng-controller directive. This gives my Layout page the ability to benefit from binding to $scope variables like any other Angular view. I’ve chosen, however, to keep the Layout page simple and limit it to providing the static shell layout design for the site and a place to put sitesection navigation links, as I’ve already shown you. This does bring me to the main Angular module. Because this module is assigned here in the Layout page, the script file that contains its definition is here as well. This is the App. js file you saw earlier.
The Site-Wide App.js and Index.cshtml View
Following the regular ASP.NET MVC order of operations, running my site would use the default MVC route {controller}/{action}/{id}, and thus seek out a controller class called HomeController and an action called Index. It’s entirely up to me to maintain this pattern or change it to a different default route or a different default view. To keep things simple, I’m maintaining the default pattern and going with a default view called Index.cshtml. The App.js file I recently mentioned is used to contain code used by the entire site, hence its declaration in the Layout page. The location of this file is a folder called App. This is the pattern I’ve chosen for my MVC application. The App folder itself contains this JavaScript file and any others that are used by the site. Later, you’ll see the sub-folder structure I’ll add in the App folder. This file declares two modules that are very important. The first Angular module declared is called common. var commonModule = angular.module(‘common’, [‘ngRoute’]);
This module won’t be attached to any view directly. I use it as a place to hang shared services from and also as a place to inherit other modules from, as in this case, ngRoute. You might have noticed the ngRoute module in previous code examples. It’s declared in the Angular script and is necessary for routing to function properly. Adding other Angular services to my site will become easier now, because I just need to add them to the in-
codemag.com
Listing 8: Index.cshtml @{
<p>AngularJS is the leading JavaScript-based client framework for web development.</p> <p>ASP.NET MVC remains Microsoft’s prefered web development framework and HTML delivery platform</p> <p> When combined, these two platforms can build rich web applications while maintaning great code manageability as well as offer great server-side support. </p> </div> </div> </div>
ViewBag.Title = “Integrating ASP.NET MVC and AngularJS”;
} <div data-ng-controller =”indexViewModel”> <div class=”jumbotron”> <h1>{{ topic }}</h1> <h2>Author: <b>{{ author }}</b></h2> <p class=”lead”>This site brings together two great platforms for web development.</p> </div> <div class=”row”> <div class=”col-md-8”>
heritance array in the common module declaration. The App.js file also declares a shared service called viewModelHelper. Its contents are beyond the scope of this article, but it’s included in its entirety with the accompanying code download. I declare the viewModelHelper service by using the factory method from the common module. commonModule.factory(‘viewModelHelper’, function ($http, $q, $window, $location) { return MyApp.viewModelHelper( $http, $q, $window, $location); });
above syntax, the name of the module as attached to the Layout page is main, but the variable that represents the module for procedural code is mainModule. The Index.cshtml view that comes up by default (based on the default MVC route) when the site runs is bound to an Angular view-model called indexViewModel. This view-model is also declared in this App.js file and it hangs off of the main module. mainModule.controller(“indexViewModel”, function ( $scope, $http, $q, $routeParams, $window, $location, viewModelHelper) {
Then, I simply create the viewModelHelper function. var self = this; (function (myApp) { var viewModelHelper = function ( $http, $q, $window, $location) {
$scope.topic = “Integrating ASP.NET MVC and AngularJS”; $scope.author = “Miguel A. Castro”;
var self = this;
});
// function members go here
In the case of this view, the currently bootstrapped Angular module is main because it was attached in the Layout page. The services I’m injecting into this viewmodel are the common ones that I inject into all my view-models, although I’m not really using any of them here except $scope. Notice the injection of the viewModelHelper service as well. The Index.cshtml view can be seen in Listing 8, where you can see me use the topic and author variables as they are declared in the view-model.
return this; }; myApp.viewModelHelper = viewModelHelper; }(window.MyApp));
Notice that I’m using the myApp namespace I declared back in the Layout page. As I stated earlier, this is to avoid polluting the global namespace. Having the common module makes it easy for the main module and all other Angular modules going forward to inherit functionality that I need across the entire site, because all they have to inherit from in order to gain such functionality is the common module. The declaration of the main module does just that. var mainModule = angular.module( ‘main’, [‘common’]);
This module is attached to the Layout page’s <body> tag, as you saw earlier, and because it inherits from the common module, it also inherits from the ngRoute module and the declaration of the viewModelHelper shared service. Using this service in a controller now is as easy as injecting it by using its name. Note that, as noted in the
codemag.com
Clicking on the site heading of the Layout page routes you to the Index.cshtml view from anywhere in the site in the conventional MVC fashion. Clicking on any of the links for Customer or Product calls upon the Home Controller’s Customer or Product action methods. These actions are designed to render the Customer.cshtml or Product. cshtml MVC views respectively. These views are the root views for the Customer and Product SPA Silos.
The SPA Root Views
The standard MVC route {controller}/{action}/{id} allows me to route to Home/Customer or Home/Product and get to the Customer.cshtml and Product.cshtml views. Later, I’ll alter these routes, but for now, I’ll continue based on the fact that it was the default route table entry that got me to these views. For the purposes of the example going forward, I’ll stick to the Customer
Integrating ASP.NET MVC and AngularJS
53
section, but the Product section will be set up exactly the same way. The Customer.cshtml view is the “root” of the Customer SPA Silo. The primary goal for this view is similar to the shell views in the first examples I went over when describing more traditional SPAs. It sets up the layout for this section of the website and loads up any necessary scripts. The main difference between this and the shell views is that this is the root of the Customer section and it is, in its entirety, rendered inside the Layout page by the RenderBody statement. This is where you see a huge benefit of using ASP.NET MVC as the primary delivery mechanism. I can have site-wide layout and site-wide scripts, and also section-wide layouts and section-wide scripts. For this difference, I refer to Customer.cshtml as a root view. Because this view is the root view for a SPA Silo, the Customer SPA Silo, it uses a separate Angular module along with its own set of Angular view-models. The JavaScript files that contain all of this information are loaded in the scripts section of this view. Remember, this view is loaded by ASP.NET MVC, meaning that I have all the power of the server-side Razor engine at my disposal. The scripts section is combined with the Layout page where the section was defined. @section scripts { <script src=”~/App/Customer/App.js”> </script> <script src=”~/App/Customer/ViewModels/ RootViewModel.js”> </script> <script src=”~/App/Customer/ViewModels/ CustomerHomeViewModel.js”> </script> <script src=”~/App/Customer/ViewModels/ CustomerListViewModel.js”> </script> <script src=”~/App/Customer/ViewModels/ CustomerViewModel.js”> </script> }
You may recall me mentioning that there will be other App.js files later. You can see one here, but notice its location. This is key to the naming convention I use. Because I declared this to be the Customer section of my site, I have a Customer folder under the App folder. The App.js file in this folder accommodates everything for the Customer section only, whereas the App.js that is one level up in the App folder accommodates all sections. The Customer folder under the App folder is an example of the folder structure I use throughout my entire site. The sub-folders immediately beneath the App folder each represent a site section. I use the same name as the action method in the HomeController MVC controller class that navigated me to this section. Under these sub-folders, I have the SPA Silo’s App.js file, which I’ve just described. I also have sub-folders for everything else that deals with this particular SPA Silo. These subfolders include, but are not limited, to a ViewModels folder that contains the Angular view-models for this
54
Integrating ASP.NET MVC and AngularJS
Figure 3: Hybrid App Folder Structure SPA Silo, and a Views folder that contains the HTML templates. The files in these two folders have a one-toone correspondence with one another and their naming differs only in their suffixes, ViewModel and View. Additional sub-folders can include scripts and content as well. Given this convention, my site’s App folder looks like Figure 3. In reference to the recent code-snippet, where I show you the “scripts” section, notice that I’m loading four view-models. The one declared in the RootViewModel. js file is the view-model that applies to this root view. This view needs to bind to its own Angular module. The reason for this is primarily to isolate the view-models, services, and routes that will be handled within this section. It also makes things much more manageable. The module, to be called customer, cannot however be attached using data-ng-app. That directive can only be used once per current rendering, and if you recall, I used it in the <body> tag of the Layout page to attach the main module. I can, however, load and attach it procedurally, and that is what the jsCode script section is for. @section jsCode { angular.bootstrap( document.getElementById(“customer”), [‘customer’]); }
Here, I’m telling Angular to attach the customer module (in the square brackets) to the element with the name customer (in the parentheses). The body of the view is wrapped in a div tag marked with the Angular ng-non-bindable directive. <div ng-non-bindable>
codemag.com
By definition, this tells Angular to ignore the contents of the div tag. In reality, what it’s telling it is to ignore what’s been set up for Angular before this. I’m referring to the main module that was bootstrapped by the Layout page. This allows another module to be attached without confusing the system. I’ll identify the next div tag down with the name customer, performing the procedural bootstrapping that I showed you earlier. <div id=”customer” data-ng-controller=”rootViewModel”>
I’m also binding this tag and all its content to the rootViewModel Angular view-model. The rest of the view is made of HTML markup in combination with Angular directives and bindings. It draws the layout for the Customer section and provides any state and behavior that will be used throughout the entire section. The area to be used to swap inner views around is handled, of course, by the ng-view directive. The body of this view in its entirety can be seen in Listing 9. Notice the link to take me to the Products section. It has the additional attribute target, as I described earlier in this article. This forces the link to go to the server, thus turning the process back over to ASP.NET MVC. Later I’ll explain why this route is /product and not the /home/product that the default MVC route table entry requires. Everything in the body of this view is bindable to any $scope exposed by the rootViewModel Angular view-model that’s declared in the App.js file located in the App/Customer folder. The contents of this file, in conjunction with the other view-models loaded by this root view, along with the Customer.cshtml root view itself, make up the Customer SPA Silo as illustrated back in Figure 2.
A SPA Silo
The Customer SPA Silo is where a user finds anything to do with managing customer information. The root view, Customer.cshtml, designs the layout and provides the ng-view placeholder for every sub-view to be contained within. The rootViewModel Angular view-model is bound to the root view, as previously described, and that viewmodel is created from the customer Angular module, also as previously described. The customer module is the first construct contained in the Customer section’s App.js file. This module inherits from the aforementioned common module.
SPONSORED SIDEBAR: CODE Framework: Free, Open Source, and Fully Supported CODE Framework consists of various components and tools that help developers with common aspects of business application development; such as simplified SOA development with various clients, WPF development, data access, and much more. Best of all, the CODE Framework is completely free and open source with no strings attached! This framework is supported with periodic feature updates. Email info@eps-software.com with any questions; we’ll never charge you for answering a few questions in email. For those looking for more sophisticated and handson support, we also offer premium support options.
var customerModule = angular.module(‘customer’, [‘common’]);
This module also defines the routing tables to be used by the Customer SPA Silo. Each route results in a view and view-model combination to be placed in the root view’s ng-view placeholder. customerModule.config(function ($routeProvider, $locationProvider) { $routeProvider.when(‘/customer’, { templateUrl: ‘/App/Customer/Views/ CustomerHomeView.html’, controller: ‘customerHomeViewModel’ }); $routeProvider.when(‘/customer/list’, {
codemag.com
Integrating ASP.NET MVC and AngularJS
55
templateUrl: ‘/App/Customer/Views/ CustomerListView.html’, controller: ‘customerListViewModel’
$routeProvider.when(MyApp.rootPath + ‘/customer’, { templateUrl: MyApp.rootPath + ‘/App/Customer/Views/ CustomerHomeView.html’, controller: ‘customerHomeViewModel’ });
}); $routeProvider.when(‘/customer/show/ :customerId’, { templateUrl: ‘/App/Customer/Views/ CustomerView.html’, controller: ‘customerViewModel’ }); $routeProvider.otherwise({ redirectTo: ‘/customer’ }); $locationProvider.html5Mode({ enabled: true, requireBase: false });
Now, no matter what my execution location is, I also have the correct URL path. The Customer Silo’s App.js file also contains the declaration of the customerService Angular service. This service can be used to share state and behavior among all the view-models bound to views that get placed in the ngview placeholder.
});
Notice that each route starts with the path /customer. Also, notice the physical path to the view templates, / App/Customer/Views. If you recall back in the root view’s scripts section, the location of the view-model files was /App/Customer/ViewModels. In my environment, I’ve been running this project using IIS Express right in Visual Studio. I’ve found that if I set it up to run in fullblown IIS, I sometimes run into that problem I previously described where it can’t find certain files when the path merely starts with /. This has to do with how I set up the application to run in IIS, be it in an application or a virtual directory. To avoid this and any other confusion that may occur during deployment, I can preface the URL path as well as the templateUrl path with MyApp.rootPath.
customerModule.factory(‘customerService’, function ($http, $location, viewModelHelper) { return MyApp.customerService($http, $location, viewModelHelper); }); (function (myApp) { var customerService = function ($http, $location, viewModelHelper) { var self = this; // shared state and behavior can go here
Listing 9: Body of the Customer.cshtml View <div ng-non-bindable> <div id=”customer” data-ng-controller=”rootViewModel”> <h2>{{ pageHeading }}</h2> <div class=”row”> <div class=”col-md-4”> <button class=”btn btn-primary” ng-click=”customerList()”> Customer List </button> </div> <div class=”col-md-4”> <button class=”btn btn-primary” ng-click=”showCustomer()”> Customer
</button>&nbsp; <input type=”text” ng-model=”customerService.customerId” /> </div> </div> <hr /> <div ng-view></div> <hr /> <div> <a href=”/product” target=”_self”>Products</a> </div> </div> </div>
Listing 10: Customer SPA Silo’s rootViewModel customerModule.controller(“rootViewModel”, function ($scope, customerService, $http, viewModelHelper) {
}
$scope.showCustomer = function () { if (customerService.customerId != 0) { $scope.flags.shownFromList = false; viewModelHelper.navigateTo( ‘customer/show/’ + customerService.customerId); } }
$scope.viewModelHelper = viewModelHelper; $scope.customerService = customerService; $scope.flags = { shownFromList: false }; var initialize = function () { $scope.pageHeading = “Customer Section”; } $scope.customerList = function () {
56
Integrating ASP.NET MVC and AngularJS
viewModelHelper.navigateTo(‘customer/list’);
});
initialize();
codemag.com
return this; }; myApp.customerService = customerService; }(window.MyApp));
The rootViewModel view-model that’s loaded and bound to the Customer.cshtml root view is also contained in the App/Customer folder under the ViewModels sub-folder. Listing 10 shows this view-model in its entirety. I declared it from the customer module, depicted by the customerModule variable, and am injecting into it not only some standard Angular services, but also the viewModelHelper service declared in the application-centric App.js file and the customerService declared in the customersection-centric App.js file. The contents of this view-model are specific to the Customer.cshtml root view, so if you look again at Listing 9, you can reconcile the customerList() and showCustomer() function calls with the functions declared in Listing 10. The incoming services, viewModelHelper and customerService are given $scope assignments so that their contents can be used in bindings by the view.
default route pattern, this was /Home/Customer. Now that the Angular module has loaded and an ng-view directive has been found in the Customer.cshtml root view, Angular will attempt to take that route and run it through the routing table that I declared into the customer module. If you look back at the code-snippet above that where I showed the routing configuration, you’ll see that the route /Home/ Customer isn’t in the list. This means that the otherwise clause will be hit and the route will be changed to /customer. Again, Angular runs this through the list of configured routes and this time it finds it. The result is that it loads the Home.html view from the App/Customer/Views folder and binds it to the customerHomeViewModel Angular viewmodel. This view-model was declared when the CustomerHomeViewModel.js script was loaded in the scripts MVC section in the Customer.cshtml root view. customerModule.controller( “customerHomeViewModel”, function ($scope, customerService, viewModelHelper) { $scope.viewModelHelper = viewModelHelper; $scope.customerService = customerService;
$scope.viewModelHelper = viewModelHelper; $scope.customerService = customerService; var initialize = function () {
Also, remember that the rootViewModel’s $scope contents are accessible by any view-models bound to views that get placed in the ng-view placeholder. I described this nested view-model pattern earlier when I discussed traditional SPAs. I’m not going to get into the implementation details of what each of my view-models shows or does, as it’s all standard Angular stuff that was a prerequisite for this article. I will, however, bring attention to an example of how I’m using the viewModelHelper service by calling the navigateTo method in order to route me to another view/view-model within this SPA Silo. viewModelHelper.navigateTo(‘customer/list’);
This method encapsulates a common way I call upon Angular’s $location.path command to perform the loading of a new view. self.navigateTo = function (path, params) { if (params == null) $location.path(MyApp.rootPath + path); else $location.path(MyApp.rootPath + path). search(params); }
This is how all methods or variables are declared in the viewModelHelper service, or any other shared service for that matter. If that service is injected into a view-model, as it in the case of rootViewModel, all of these members can be accessed. When the Customer.cshtml root view is rendered, it loads up the customer module and the rootViewModel view-model, bootstraps the module to the view, and binds the view-model to the div tag with the data-ng-controller directive. When the customer module is declared and its routing table configured, Angular recalls the original URL that got it here to begin with. Remember, sticking to the standard ASP MVC
codemag.com
} initialize(); });
The initialize pattern that you see is my standard for doing things in all of my view-models. The view to which this view-model gets bound is very simple, as it doesn’t contain anything special and neither does its view-model. <div> <h3>Select an option using one of the Customer buttons above</h3> </div>
It’s very important to point out that, unlike the Customer.cshtml root view, you don’t see any data-ng-controller directive here. This is because the connection between the view and the view-model is done by the routing table entry itself. $routeProvider.when(‘/customer’, { templateUrl: ‘/App/Customer/Views/ CustomerHomeView.html’, controller: ‘customerHomeViewModel’ });
An alternative is to add a data-ng-controller directive to the div tag at the top of the CustomerHomeView.html view, but I need to completely omit it from the routing definition. If I have both, the view-model loads twice and any code that’s immediately executed, such as the initialize function, will be executed twice. Going forward, any functions called by HTML elements in the view will also be called twice, and, depending on what that function does, this can be dangerous for the application. It’s also very bad practice to simply “let it go.” Although I can choose either of the two methods of attaching the view-model to the view, I prefer to omit the
Integrating ASP.NET MVC and AngularJS
57
directive from the view and declare it in the routing definition so it can be seen by the developer immediately. This now becomes the view and view-model combination that gets placed into the ng-view placeholder of the Customer.cshtml root view upon first rendering this SPA Silo. A byproduct of this route shuffle is that the URL in the browser bar now changes to {host}/customer. This can be a little misleading because the link that got me to the Customer.cshtml view to begin with was {host}/ home/customer. I’ll show you how I plan to remedy this and more later, when I cover routing specifics.
Contained views inside an ng-view should either bind their view-model through data-ng-controller or in the routing definition, but not both.
From this point on, it’s all about adding views and viewmodels and providing navigation to them though links or view-model functions. If you look back at the code for Customer.cshtml, you’ll see that I declared two buttons. One was to show the customer list and the other was a specific customer, based on a bound value to a textbox. <div class=”col-md-4”> <button class=”btn btn-primary” ng-click=”customerList()”> Customer List </button> </div> <div class=”col-md-4”> <button class=”btn btn-primary” ng-click=”showCustomer()”> Customer </button>&nbsp; <input type=”text” ng-model=”customerService. customerId” /> </div>
The two functions in the rootViewModel view-model navigate to one of the other routes declared by the routing configuration of the customer module in the SPA Silo’s App.js file. $scope.customerList = function () { viewModelHelper.navigateTo(‘customer/list’); } $scope.showCustomer = function () { if (customerService.customerId != 0) { $scope.flags.shownFromList = false; viewModelHelper.navigateTo( ‘customer/show/’ + customerService.customerId); } }
I chose to perform both navigations procedurally in the interest of consistency and the possibility of enhancing the
58
Integrating ASP.NET MVC and AngularJS
functions further in the future. However, the link to display the customer list could have been handled entirely by the <a> tag instead of leveraging an ng-click directive. The result of each navigation attempt is another view/ view-model combination placed in the ng-view placeholder of the Customer.cshtml root view. If such an attempt fails because the route isn’t found, the otherwise clause is invoked and the /customer route is handled. It’s important to understand the distinction of which routing engine handles what. The routing I’ve just described is handled by the Angular routing table, and each SPA Silo (in this case, the Customer SPA Silo) is in charge of defining and handling its own set of routes. The procedural way of performing this kind of routing is through the path method of the $location Angular service, which I wrapped in my viewModelHelper shared service. An anchor tag with a target=”_self” attribute on it will hit the server. If the server is hit, ASP.NET handles the request first and the MVC routing engine is engaged first. This brings me back to the link in the Customer.cshtml root view that contained the route “/product”. <a href=”/product” target=”_self”>Products</a>
This link hits the server and thus invokes the ASP MVC routing engine. Remember so far, I’ve only been using the default route entry of {controller}/{action}/{id}, meaning that the route should say /Home/Product/ or it will fail. Also, what if I wanted to cross from this SPA Silo, not to the root view of the Product SPA Silo, but directly to the list of products. Ideally this should be handled by a link that jumps to the URL “/product/list”. <a href=”/product/list” target=”_self”>Product List</a>
In my current configuration, not only would the “/product” link fail if I’m in the Customer SPA Silo, but so will the “/ product/list” route. Both of these routes are defined in the product module of the App.js located in the /App/Product folder. Making the ASP.NET MVC routing engine and Angular routing engine play nicely together isn’t difficult but it requires some adjustment on my part—or yours.
Dual Routing Engines
No website is production quality without proper route validity and integrity. This means that once a route is visibly made available to a user, it should be available from any part of the site. Today, you must consider the fact that users write URLs down to use later, or even more frequently, bookmark things. They also email and text URLs to others, so it’s up to us as developers to ensure that any route they learn works with direct navigation as well as with a browser refresh. Take, for example, the route “/product/list”. From what you’ve learned so far, this is a route handled by the Angular routing configuration assigned in the product module of the Product.cshtml root view. But the problem is that the “/product/list” route means nothing to ASP. NET MVC. Again, from what you’ve seen so far, in order to get to the Product SPA Silo, you need to route to “home/ product” so you can hit the Home MVC controller and Product action. As it stands right now, a link in a customer view that looks like this: <a href=”/product/list” target=”_self”>Product List</a>
codemag.com
will error out for two reasons. First, this route is not defined in the Angular route definition of the customer module. And it cannot be added because the view and, more specifically, the view-model that it needs, don’t belong to the customer module and haven’t even been scriptloaded. Secondly, the route isn’t specifically defined in the MVC routing table either. In fact, it would have been interpreted as needing to hit a controller class called ProductController and an action method called List; neither of which exist. Fixing this is easy and is done in the RouteConfig class in the Web application’s App_Start folder. This is where the default MVC route is declared. routes.MapRoute( name: “Default”, url: “{controller}/{action}/{id}”, defaults: new { controller = “Home”, action = “Index”, id = UrlParameter.Optional } );
This is where I need to make an addition for each SPA Silo. If you recall, the Home controller contains two actions in addition to the Index action, Customer and Product. I’m going to add a route that looks like this: routes.MapRoute( name: “customer”, url: “customer/{*catch-all}”, defaults: new { controller = “Home”, action = “Customer” });
This route will be used by any URL that starts with customer, and routes it to the Customer action in the HomeController controller class, just as if the route had said Home/ Customer. The term catch-all can be anything you want, as long as it has the asterisk (*) in front of it. This route allows me to call the URL customer/list right from the browser bar. The result is the Customer.cshtml root view, which is what’s called from the HomeController class’ Customer action. But, as I’ve stated several times, Angular remembers the original route called. Because getting to the Customer root view loads up the Angular module and Customer SPA Silo’s routing configuration, it then takes this original URL of customer/list and routes it through the Angular routing engine. When it does this, it finds the route and renders the proper view and view-model in the ng-view placeholder of the Customer.cshtml root view.
ration, and eventually finding the route and rendering the ProductListView.html template as well as binding it to the productListViewModel view-model. Just don’t forget that if a link like this is outside of the Product SPA Silo, it must include the target=”_self” attribute. If it’s within the Product SPA Silo, it shouldn’t have that attribute. In fact, placing this attribute on a link in a view in the very SPA Silo to where you want the link to route causes an unnecessary server trip. Not only that, by initiating a server trip, any state you were holding in a shared service such as a productService Angular service will be lost because the HTTP request-response process starts from the top again. Of course, it’s up to you to account for any page-order that you need to enforce in your application. This means that if a route gets a user to a specific view but there was a business prerequisite before that view is properly used, you need to place logic in the view-model to either routeaway or signal other instructions to the user.
Conclusion So there you have it, the worlds of ASP.NET MVC and AngularJS working together, not only in harmony but benefiting from each other’s strengths. You see no jamming square pegs into round holes in this scenario. Each technology is used in the manner for which it was designed and in an elegant fashion. The two technologies coexist nicely and aren’t fighting each other in any way. It’s truly a match made in heaven.
Accompanying Download You can download the complete set of projects for this article at www.melvicorp.com/uploads/... Although all are Visual Studio projects, the first three solution folders have absolutely no dependency on .NET in any way. VS was used as the editor and to group the files together into projects for ease of transport.
The techniques I’ve used for integrating the two technologies don’t necessarily have to apply to Angular only. I’ve used the same design with ASP.NET MVC and Knockout, and I have no reason to believe that it wouldn’t work with Ember, Backbone, or any other JavaScript framework. Make sure that you download the code that accompanies this article from the information provided in the sidebar. It contains quite a bit more than what I was able to cover in this article, although it’s all extras that aren’t key to the integration techniques I’ve illustrated. Miguel Castro
Each SPA Silo needs a similar MVC route entered into the routing table. routes.MapRoute( name: “product”, url: “product/{*catch-all}”, defaults: new { controller = “Home”, action = “Product” });
Now, the /product/list link that I showed you earlier will work just fine and route to the Product.cshtml root view first, loading the product module and its routing configu-
codemag.com
Integrating ASP.NET MVC and AngularJS
59
ONLINE QUICK ID 1605091
Implementing Lean Controllers in iOS Applications The Model View Controller (MVC) design pattern is no stranger to iOS developers. Every iOS application uses the MVC pattern to distribute the responsibilities of the application flow. Unfortunately, most of the time, this distribution ends up with a lot of code in the controllers of the application. These bloated controllers are responsible for creating technical debt and also create maintenance nightmares for the future. In this article, you will learn how to tame these monsters. I’ll start with an app that consists of a massive controller and work my way toward a leaner implementation.
Mohammad Azam
The basic principle behind lean controllers is the single responsibility principle. This means that every component of the application should have a single job and purpose. By isolating each component to perform a single task, you make sure that the code remains lean and easy to maintain.
azamsharp@gmail.com www.azamsharp.com @azamsharp
Scenario
Mohammad Azam works as a Senior Mobile Developer at Blinds.com. He’s been developing iOS applications since 2010 and has worked on more applications than he can remember. Azam’s current app, Vegetable Tree - Gardening Guide, is considered the best vegetable gardening app on the app store and was featured by Apple in 2013. He’s a frequent speaker at Houston Tech Fest and Houston iPhone Meetup. When not programming, Azam likes to spend his time with his beautiful family and planning for his next trip to the unknown corners of the world.
The iOS application under discussion is a simple user task-management app. The application consists of a single screen where the user can add tasks to a list. Users can delete the tasks by swiping right to left on the row and then pressing the delete button. The user can mark the tasks as complete by selecting the cell using a single tap gesture. All of the tasks are persisted in the SQLITE database using the FMDB wrapper. The screenshots of the app running in action is shown in Figure 1 and Figure 2. In the first section, you’ll inspect the TasksTableViewController implementation, which orchestrates the complete flow of the application. You’ll quickly realize that the implemented code is not reusable and is hard to maintain. You’ll take the whole application apart and create reusable, single-responsibility components that protect the application for future changes.
Implementing a Lean View Controller The best place to start is the viewDidLoad event of the TasksTableViewController. The viewDidLoad event is invoked each time the app runs. Inside the viewDidLoad event, you trigger the initializeDatabase function. The initializeDatabase function is responsible for copying the database from the application bundle to the documents directory of the application. Because this procedure needs to be performed only once for each app, there’s no need to call it from the viewDidLoad event. You’re going to move the initializeDatabase function into AppDelegate where it can be called from within the didFinishLaunchingWithOptions event. By moving the initializeDatabase call inside the didFinishLaunchingWithOptions, you’ve made sure that initializeDatabase is called only once during the lifecycle of the application. Listing 1 shows the implementation of the initializeDatabase function. Next, move to the cellForRowAtIndexPath function. At first glance, the cellForRowAtIndexPath implementation doesn’t show any warning signs, but the devil’s in the
60
Implementing Lean Controllers in iOS Applications
details. The properties associated with the cell are assigned inside the function. In the future, you might be interested in displaying some additional properties on the user interface. This means that you’ll assign more cell properties inside the cellForRowAtIndexPath function, hence polluting it with unnecessary code, as indicated in Listing 2.
The concepts and techniques learned in this article can be applied to any iOS application regardless of the data access layer used.
The best way to deal with this problem is to refactor the configuration of the cell into a separate function. Luckily, the TaskTableViewCell is a perfect candidate to define such a function. The implementation of the configureCell function is shown here: func configure(task :Task) { self.titleLabel.text = task.title self.shortTitle.text = task.shortTitle self.imageURL = task.imageURL }
The configure function accepts a Task model object and then populates the cell properties based on the properties of the model. The next snippet shows the call to the configure function inside the cellForRowAtIndexPath event. override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { guard let cell = tableView. dequeueReusableCellWithIdentifier (“TaskTableViewCell”, forIndexPath: indexPath) as? TaskTableViewCell else { fatalError(“TaskCell not found”) } let task = self.tasks[indexPath.row] cell.configure(task) return cell }
codemag.com
Implementing TasksDatasource
In the current implementation, the data source is tied to the TasksTableViewController. This means that if you have to use the same data in a different controller, you have to manually copy and paste the code into a new controller. As a developer, you realize that copy/pasting is the root of all evil and that it contributes to the technical debt. Instead, you will refactor the data source into a separate TasksDataSource class. The TasksDataSource class implements the UITableViewDataSource protocol. The TasksDataSource initializer consists of the following parameters: • cellIdentifier: A string instance representing the UITableViewCell unique identifier • items: An array of task instances • tableView: The UITableView instance The TasksDataSource class also provides the implementations for the cellForRowAtIndexPath and numberOfRowsInSection functions. Now, the TasksTableViewController can call the setupTableView function from inside the viewDidLoad event to initialize the TasksDataSource, as shown in Listing 3. Apart from eliminating the unnecessary code implemented in TasksTableViewController, TasksDataSource also
Figure 1: TODO app in action
Figure 2: Entering new tasks in the TODO app
Listing 1: InitializeDatabase function to setup the database func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let fileManager = NSFileManager.defaultManager() let success = fileManager.fileExistsAtPath(self.databasePath)
initializeDatabase() return true } private func initializeDatabase() { let documentPaths = NSSearchPathForDirectoriesInDomains (NSSearchPathDirectory.DocumentDi rectory, NSSearchPathDomainMask.UserDomainMask, true) guard let documentDirectory = documentPaths.first else { return }
guard let databasePathFromApp = NSBundle .mainBundle().resourcePath? .stringByAppendingPathComponent (self.databaseName) else { return } print(self.databasePath) if success { return } try! fileManager.copyItemAtPath(databasePathFromApp, toPath: self.databasePath)
self.databasePath = documentDirectory .stringByAppendingPathComponent (self.databaseName)
}
Listing 2: Cell properties assigned inside the cellForRowAtIndexPath function override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCellWithIdentifier (“TaskTableViewCell”, forIndexPath: indexPath) as? TaskTableViewCell else { fatalError(“TaskCell not found”) }
codemag.com
let task = self.tasks[indexPath.row] cell.titleLabel.text = task.title; cell.subTitleLabel.text = task.subTitle; // Future properties polluting code cell.imageURL = task.imageURL // Future properties polluting code return cell }
Implementing Lean Controllers in iOS Applications
61
helps to create a reusable data source that can be unit tested and used in different parts of the application. The setupTableView also exposes another problem related to data access. Currently, the data access code is littered throughout the TasksTableViewController, which not only makes it harder to test but also harder to reuse and maintain. In the next section, you’ll refactor the data source into a separate service, which allows more flexibility in the future.
At first glance, the cellForRowAtIndexPath implementation doesn’t show any warning signs, but the devil is in the details
Xcode Swift Refactoring Support Unfortunately, at the time of this writing Xcode still doesn’t provide any refactoring support for the Swift language. I’m sure that the Xcode team is hard at work, but currently, refactoring tools are limited to Objective-C language. One solution is to use AppCode from Jetbrains, which does provide advanced refactoring tools.
init() { var token :dispatch_once_t = 0 dispatch_once(&token) { let appDelegate = UIApplication. sharedApplication().delegate as! AppDelegate let databasePath = appDelegate .databasePath self.db = FMDatabase(path: databasePath) } }
Next, you’ll implement the GetAll function that will be responsible for retrieving all the tasks from the database. The GetAll implementation is shown in Listing 4. The congested setupTableView function can be replaced with the new leaner implementation, as shown in Listing 5.
Implementing TasksDataService
TasksDataService will be responsible for all of the CRUD (Create, Read, Update, Delete) operations related to the application. TasksDataService maintains the communication bridge between the view controller and the persistent storage system, which, in this case, is SQLite3. The first step is to initialize the TasksDataService, which also sets up the FMDatabase instance, as shown in the next snippet.
You can apply the same approach to the save and delete operations and move the implementations in the TasksDataService class. This eliminates a lot of code from the TasksTableViewController and moves you one step closer toward Lean Controller implementation. In the next section, you’re going to look at the tableView delegate events and how they can be refactored into their own classes.
var db :FMDatabase!
Swift 2.0 introduced the concept of Protocol Extensions, which allowed developers to provide default implementa-
Extending TasksTableViewController
Listing 3: Setting up UITableViewDataSource func setupTableView() {
}
// get the data from the database let db = FMDatabase(path: self.databasePath)
// close the connection defer { db.close() }
db.open() let result = db.executeQuery(“select * from tasks”, withArgumentsInArray: []) while(result.next()) { let task = Task(title: result. stringForColumn(“title”)) // add to the collection self.tasks.append(task)
// initialize the data source self.dataSource = TasksDataSource (cellIdentifier: “TaskTableViewCell”, items: self.tasks,tableView: self.tableView) self.tableView.dataSource = self.dataSource self.tableView.reloadData() }
Listing 4: GetAll function to retrieve all tasks func getAll() -> [Task] { var tasks = [Task]()
// add to the collection tasks.append(task) }
db.open() defer { db.close() }
let result = db.executeQuery(“select * from tasks”, withArgumentsInArray: []) while(result.next()) { let task = Task(title: result.stringForColumn(“title”))
62
Implementing Lean Controllers in iOS Applications
return tasks }
codemag.com
Listing 5: The setupTableView function utilizing the TasksDataService func setupTableView() {
“TaskTableViewCell”, items: self.tasks, tableView: self.tableView) self.tableView.dataSource = self.dataSource
self.tasksDataService = TasksDataService() self.tasks = self.tasksDataService.getAll()
self.tableView.reloadData() // initialize the data source self.dataSource = TasksDataSource(cellIdentifier:
}
Listing 6: The didSelectRowAtIndexPath implementation inside the TasksTableViewController extension override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let task = self.tasks[indexPath.row] guard let cell = self.tableView. cellForRowAtIndexPath(indexPath) as? TaskTableViewCell else { fatalError(“Cell does not exist”) }
tions to protocol functions. Currently, the TasksTableViewController contains a lot of code that deals with the look and feel of the UITableView control. This includes the implementations for didSelectRowAtIndexPath and commitEditingStyle events. By providing an extension to the TasksTableViewController, you can move a lot of clutter from the TasksTableViewController class to the extension.
FMDB is a thin Objective-C open source wrapper on top of the SQLite database that provides a lot of helper methods for performing database operations from within your applications.
if(!task.isCompleted) { cell.titleLabel.attributedText = task.title.strikeThrough() task.isCompleted = true } else { cell.titleLabel.attributedText = task.title.removeStrikeThrough() task.isCompleted = false } }
completed and uncompleted tasks. The implementation is shown in the following snippet. func configure(task :Task) { self.titleLabel.text = task.title if(task.isCompleted) { self.titleLabel .attributedText = task.title.strikeThrough() } else { self.titleLabel .attributedText = task.title.removeStrikeThrough() } }
Run the app again and now you’ll notice that the strike through rows persists while scrolling the UITableView control!
Conclusion The great thing about extending the TasksTableViewController using protocol extensions is that it will have access to all of the properties defined in the TasksTableViewController implementation. The implementation of the didSelectRowAtIndexPath defined inside the TasksTableViewController extension is shown in Listing 6. You’ll also notice that I’ve used an extension method for String type to create the strikeThrough effect. This allows you to reuse the strikeThrough function in other parts of the application.
Implementing lean controllers takes more effort than writing massive controllers. It might be tempting to put all the code into one controller, but keep in mind that although you might move fast initially, you’ll quickly hit a wall. It’s always better to refactor and move toward a more solid design initially when things are in motion, rather than waiting until the components have been fixed into place.
SPONSORED SIDEBAR: Need Help? Looking for help optimizing your application architecture and design application? The experts at CODE Consulting are here to help with your project needs! CODE has a 20-Hour Introductory Service. Get full access to a CODE team member for 20 hours to have them help you on your project. Use this time for one-on-one mentoring, give your project the push it needs to get started, or anything in between! Hours can be used consecutively or divided up as needed. Email info@codemag.com to set up your time with a CODE specialist!
Mohammad Azam
Run the application. On the surface, everything looks fine, but as you strikeThrough the top rows and scroll to the UITableView to the bottom, you’ll notice that that your strikeThrough rows are reverted to the default style. The reason is pretty simple: UITableView reuses the cells that it displays. This helps to increase the performance dramatically, as new cells are never created. In order to fix this issue, you need to edit the configure function of the TaskTableViewCell to include the logic for
codemag.com
Implementing Lean Controllers in iOS Applications
63
ONLINE QUICK ID 1605111
SQL Server Reporting Services: Eight Power Tips SQL Server Reporting Services (SSRS) has come a long way since the initial release of SSRS in SQL Server 2000. Early on, many developers and technology managers viewed SSRS as an average report writer that lacked functionality and scalability compared to other established reporting solutions. Fortunately, Microsoft greatly enhanced the SSRS feature set over the last eight years (from SSRS 2008 to the present)thereby empowering SSRS advocates to make a stronger and more compelling case that SSRS is indeed a primetime player in the world of business reporting. In this article, I’ll demonstrate some tips and reusable approaches in different areas of report authoring/generation that help make that case.
Kevin S. Goff kgoff@kevinsgoff.net http://www.KevinSGoff.net @KevinSGoff Kevin S. Goff is a Microsoft SQL Server MVP. He is a database architect/developer/speaker/ author, and has been writing for CODE Magazine since 2004. He’s a frequent speaker at community events in the midAtlantic region and also speaks regularly for the VS Live/Live 360 Conference brand. He creates custom webcasts on SQL/ BI topics on his website.
SQL Server Reporting Services: The Ultimate Bread and Butter of Reporting Tools
Recently, I heard someone say, “Third-party report and charting tools might have many bells and whistles, but SQL Server Reporting Services is still the bread and butter for many developers.” From the late 1990s to 2007, I was a die-hard Crystal Reports fanatic and published a book on Crystal Reports development with .NET. Toward the end of that period, I took a serious look at SQL Server Reporting Services 2005 (I’ll use the acronym SSRS for the rest of this article). I concluded that SSRS 2005 was a good but not outstanding product, and lacked functionality compared to Crystal Reports. That changed in SSRS 2008. Microsoft didn’t try to match every single bell and whistle of Crystal Reports in SSRS 2008; instead, they expanded their existing reporting (and charting) functionality on their own terms, and greatly improved the scalability and performance of the reporting engine. So instead of adding the bells and whistles, Microsoft fortified their bread and butter. Although Crystal Reports still had an edge on the total number of features, SSRS 2008 became a truly viable option for internal business reporting. Microsoft continued to expand SSRS in SSRS 2008R2, and has given certain UI areas of the product a much-needed make-over in the upcoming SQL Server 2016 release. Still, SSRS suffers from a bit of a perception issue. To many, SSRS remains merely adequate, but lacks some of the power found in third-party tools, competing products, and even the quasi-reporting capabilities in Excel. One of my roles as a consultant is to show developers and managers the full power of SSRS and how experienced developers can extend it. Several of the tips in this article reflect those exercises to demonstrate the power of the product.
What’s on the Menu? For over a decade, I’ve been using the Baker’s Dozen theme of giving you 13 tips. In the last year, I’ve even
64
SQL Server Reporting Services: Eight Power Tips
been writing articles as test or interview questions, and this article will also diverge in that I’m only covering the following eight items: 1. Implementing column level security in reports 2. Implementing drilldown functionality with options to set initial drilldown 3. Creating cross-chart filter refresh effects 4. Repeating column headings across multiple pages 5. Dealing with page margins and blank extra pages 6. Relating multiple tables in a report 7. Implementing a nested “Page X of Y” on a report 8. Implementing Running Aggregations in a report I’ll present many of these tips by saying, “You want to implement functionality/behavior ABC, but you’re running into a limitation. Is there a solution or workaround?” This reflects many scenarios that I encounter in my consulting practice.
Tip #1: Implementing Column-Level Security in Reports
Suppose you build a report that both regular users and executives will view. In addition to other data, the report contains cost and profitability measures that only executives should see. The regular users should only see specified data and the space for the cost columns shouldn’t appear. Users should see the report as if the cost and financial measures never existed. Does SSRS provide security features to hide columns based on domain accounts/ groups? The answer is that SSRS doesn’t provide any built-in functionality to address column-level security. However, just like many developer products, SSRS doesn’t stand in your way from manually implementing such functionality.
Just like many developer products, SSRS doesn’t stand in your way from manually implementing column-level security functionality.
I’ll keep the example very simple and will focus on the mechanics. Figure 1 shows a very basic report, with a column (“Modified Date”) that you want to configure for visibility based on the current user. You can implement this in five steps. Essentially, you’ll create a SQL Server table to define user/column visibility,
codemag.com
and a stored procedure to return that information for the current authenticated user. In the SSRS report, you’ll read this information into a hidden/internal SSRS parameter, and tap into the SSRS column visibility feature to show/ hide the column based on the SSRS parameter.
the engine shows the report as if the ModifiedDate column never existed. This makes the output appear clean for either scenario.
Step 1: Create a database table in SQL Server called dbo. RightsToColumns. For simplicity, add a single row for the user domain account name and a flag for whether to see the Modified Date column (Figure 2). Step 2: Create a basic stored procedure to return the table information for a specific User ID based on a parameter that the report will ultimately supply. CREATE PROCEDURE [dbo].[GetRightstocolumns] @UserID varchar(100) as select * from RightsToColumns where UserID = @UserID
Step 3: In the report, create a dataset that reads the stored procedure based on the current authenticated user that SSRS recognizes. Figure 3 shows mapping the SSRS Globals variable called User!UserID to the SSRS stored procedure parameter of the same core name (@UserID). When the report executes, SSRS calls the procedure and pass in the current authenticated user as the parameter. Step 4: Here is where you bring it all together. You need to collect the results of the procedure (in this case, whether SSRS allows the user to see the column). You can implement a common SSRS pattern: Create a hidden/internal SSRS parameter and map the results of this dataset to the parameter. In Figure 4, I’ve added an SSRS parameter called ShowModifiedDate (as an internal parameter, so that SSRS never prompts for it), and I’ve mapped the results of the ColumnVisible column from the dataset to the default value. In the next step, I’ll use the value of this parameter to determine column visibility.
You might find that you’ll need to greatly enhance this to work for your scenario.
Figure 1: An SSRS report with a column that you only want certain users to see
Figure 2: A basic SQL Server table that holds each user name and column visibility information
Figure 3: An SSRS DataSet that calls the stored procedure and passes the current user as a parameter
Step 5: Finally, in the SSRS designer, right-click on the column separator for the column you want to configure (the “ModifiedDate” column in this example), and select Column Visibility. In the Column Visibility dialog, enter an expression to define whether you should hide the column based on the value of the ShowModifiedDate report parameter. Note that the expression must return a Boolean value based on whether you want to hide the column, so in this case, you’re returning the reverse (“not”) of the ShowModifiedDate parameter. One final note-that I didn’t demonstrate here: Suppose the ModifiedDate column had another column to the right. If the user running the report doesn’t have the rights to view the column, the SSRS engine won’t even bother showing an empty space for the column. Instead,
codemag.com
Figure 4: An internal SSRS parameter that holds the value for the column visibility
SQL Server Reporting Services: Eight Power Tips
65
I’ve presented an overall approach, a pattern, for solving this problem. You might find that you’ll need to greatly enhance this to work for your scenario. But hopefully I’ve provided a major start on the mechanics.
Tip #2: Implementing Drill-Down Functionality with Options to Set Initial Drill-Down Sometimes users ask for the same type of functionality that’s found in end-user tools like Microsoft Excel. Here’s
one example: Users want to show an aggregation at a high level (such as Sales by State) and then expand a state to show more detailed information (such as Sales by City for the selected state). Most people refer to this functionality as drill-down, although in practice, you’re initially suppressing the display of lower levels of detail (like the city), and toggling the display of that lower level based on how a user clicks on a higher level (like the state).
I’m on a Mission. I admit it.-I’m on a mission. That mission is to show developers and managers what SSRS can do. Although no tool can accomplish every imaginable task, SSRS remains a powerful “bread and butter tool” that you can often extend. It might require some elbow grease and the end result might not win any beauty awards-but often the result will be good enough.
Figure 5: An internal SSRS parameter that will hold the value for the column visibility
Figure 6: SSRS drill-down with options for whether to expand or collapse
66
SQL Server Reporting Services: Eight Power Tips
codemag.com
Figure 6 shows an example of SSRS drilldown. Using the Microsoft AdventureWorks demo database, I’ve built a report that initially collapses sales by Shipper and Vendor. I need to create a report with two row groups that summarize by Ship Method and Vendor, and I need to create a grand total row (by summarizing all shippers). The user can expand the initial grand total to see the summarized ship methods, and then expand any ship method to see the summarized vendors for that ship method. And, although I haven’t displayed it, if the user expands any vendor, the report will show individual purchase orders. You can implement this in two major steps. Step 1: In the SSRS Report, I’ve created two Boolean parameters (Figure 7) that allow the user to determine if SSRS should initially expand or collapse the Ship Methods group and the Vendors group. Although this first step isn’t necessary for implementing basic drill-down, it does give the user the ability to define whether SSRS will initially expand or collapse a group (i.e., set each group’s ToggleState). Step 2: To implement drill-down at the Ship Method level, you need to define the initial visibility (based on the value from the ExpandShipMethods parameters in Step 1). You also need to allow the user to toggle the display of the ship methods by the textbox for the Grand Total
codemag.com
(so that the user can expand or collapse the grand total to show or suppress the ship methods). In Figure 8, I right-clicked on the row group for ShipMethodName and loaded the Group Properties dialog. To complete the example, you’d need to follow the same pattern for the Vendor row group, using the ExpandVendors parameter and setting the display toggle to the parent vendor textbox.
Tip #3: Creating Cross-Chart Filtering Refresh Effects
In the last few years, software vendors have released self-service Business Intelligence tools that allow users to create attractive and interactive dashboards and charts. These tools also boast the ability to create powerful visualizations without always needing a developer. Examples of these products include Microsoft Power BI, Tableau, QlikView, Domo, SpotFire, and others. Vendors of these tools often demonstrate (and even promote as a selling point) the ability to create a dashboard page with multiple charts, where the user can click on one data point of one chart within the page, and the tool instantly refreshes other charts to reflect the initial selection. Here’s the question: Does SSRS contain built-in functionality for this type of cross-chart filtering? Similar to Tip #1, the answer is no. SSRS doesn’t provide this ability straight out of the box. However (and this becomes a fa-
SQL Server Reporting Services: Eight Power Tips
67
miliar theme), SSRS provides enough open capabilities and hooks to allow you to create a functional workaround. For example, look at the two charts in Figure 9. The first chart on the left plots sales as a vertical stacked bar chart by country and year. The second chart on the right plots sales by country and month. Suppose you wanted to click on a specific series of data in the first chart (sales in the United Kingdom in 2013) and force all charts on the page to highlight everything associated with U.K./2013 (Figure 10). I’ll throw in a pop-culture reference here. Do you remember the old TV show, “Name that Tune?” The signature response in that show was: “I can name that tune in (N) notes.” Well, I can sing the “cross-chart filter visualization” in three steps. Essentially, you’ll tap into the SSRS Report Action feature for a plotted data series point, and re-launch the report. As you do that, you’ll pass forward to the report a hidden parameter for the year/country
Reporting is More Than the Report I wrote a book on reporting functionality in 2006. In the book, I stressed that the report writer is but one part of reporting. You wind up spending more effort on defining the data requirements, the parameters, automated report generation, etc. That’s as true today was it was 10 years ago.
that the user selected. In the reload, you’ll set a Fill Color expression to dynamically color the current series in yellow if the current year/country matches the value of the hidden parameter. I must warn readers that because you’re essentially relaunching the same report, this won’t perform as instantly as tools that provide this feature out of the box. So use and implement with caution.
Let’s stop and reflect that you’ve used hidden/internal parameters as a means to drive different SSRS behaviors.
Having said all of that, here are the three steps: Step 1: Create a hidden/internal SSRS parameter called HotSelect. You don’t need to provide any special prompts for the parameter, as the user will never see it.
Figure 7: Two SSRS parameters to define the initial ToggleState for the Ship Method and Vendor Groups
Step 2: For each chart on the report page, go to the Series properties and then the Actions tab (Figure 10). Set the action to Go to report and specify the same report name (to relaunch the report). In the parameters area at the bottom of the dialog box shown in Figure 11, map the HotSelect Parameter based on the current tear and country. If you haven’t realized it already, this works because SSRS allows you to hook into the Actions area when the user clicks on a chart data point. It also works because
Figure 8: Using the SSRS Group Properties to define the visibility and the report element that toggles the display of that group
68
SQL Server Reporting Services: Eight Power Tips
codemag.com
Figure 9: The initial report page with two charts.
Figure 10: The report page after the user clicks on U.K. Sales in 2013 on the left SSRS keeps track of the current country/year series when you click on a specific series.
that you’re implementing some SSRS design patterns here, ones that could be used for other report functions.
Step 3: For each chart, go to the Series Properties dialog box and the Fill tab (Figure 12). Set the Color Expression to yellow if the current year and country that SSRS is plotting happens to be the same value in the HotSelect parameter.
Tip #4: Repeating Column Headings Across Multiple Pages
Before you go any further, let’s stop and reflect that you’ve used hidden/internal parameters as a means to drive different SSRS behavior. It’s not a stretch to say
Suppose you implement a standard tabular report (i.e., a fixed number of columns) that spans many pages. When the user views the report in a browser and navigates be-
codemag.com
Even the best tools in the world have an Achilles heel, and this topic represents a very strange issue and even stranger workaround in SSRS.
SQL Server Reporting Services: Eight Power Tips
69
yond page one, the SSRS viewer doesn’t show the column headings. Although SSRS provides an option in the SSRS table properties to repeat column headings across each page, that option has no affect. So what’s going on?
The truth is that the “Repeat header columns on each page” option in the Table/Tablix properties only works for matrix reports. The option has no effect on tabular reports. In order to repeat column headings, you need to tap into the Advanced Mode section at the bottom of the report designer. In Figure 13, I’ve clicked the down arrow on the far right of the report column groups. Once you go into advanced mode, you’ll notice that the Row Groups area now shows several static sections (Figure 14). You’ll want to click the F4 key to bring up the full property sheet for each static group above the first non-static group and set the RepeatOnNewPage property to TRUE (Figure 15).
This might go down in history as one of the more obscure workarounds.
This might go down in history as one of the more obscure workarounds. Even more interesting, this problem didn’t exist prior to SSRS 2008. However, an obscure workaround trumps no workaround in any poker game I’ve ever played!
Tip #5: Dealing with Extra Blank Pages When Exporting Reports
Figure 11: Setting the Report Actions
When printing reports or exporting reports to PDF, sometimes the SSRS report engine produces an extra page at the end of the report, or (worse), inserts a blank page between each regular page. The former scenario might not anger users, but the latter scenario definitely requires immediate attention. The question is, why is SSRS doing this and how can you prevent it? Let’s take the latter scenario. This usually occurs because the width of the content body exceeds the allowable width. You can calculate the allowable content body width as the page width (based on the report property orientation: portrait or landscape), less the left/right margins. In Figure 16, the full content body is roughly 10.5 inches, whereas the total page width is 8.5 inches and the left/ right margins are one inch each. So the maximum allowable content body is 6.5 inches. (In practice I’d never go beyond 6.3 inches to allow a margin of error). Some people might address the issue in Figure 16 by simply stretching the report table inward to stay with the allowable width, but they might leave the outer design area at the original width (10.5 inches). As I’ve shown in Figure 17, you need to also stretch that outer edge of the design area inward to stay within the range, or SSRS generates a blank page at the end.
Figure 12: Setting the Report Series fill color based on whether the current plotted country/ year matches the hidden parameter
Figure 13: Click the drop-down to the right of Column Groups to enter Advanced Mode
70
SQL Server Reporting Services: Eight Power Tips
Figure 14: SSRS Advanced Mode with Static Row Groups
codemag.com
you to specify one primary dataset to drive the content. Is there any way for the report to show related information from the second dataset? Before I continue, someone might respond with a suggestion to modify the database code that returns the first result set, and add additional columns so that you don’t need the second result set. For this example, assume you can’t modify the database code and that you’re stuck with two procedures/two datasets. Although this constraint isn’t very common, it’s certainly not unheard of either.
Figure 15: Set the RepeatOnNewPage property to TRUE for each Static Group above the first actual group
Prior to SQL Server 2008 R2, this was a very difficult challenge. Fortunately, Microsoft implemented three new functions in SSRS 2008R2 (released in spring 2010). They are Lookup, LookupSet, and MultiLookUp. These functions allow SSRS developers to return information from a secondary SSRS DataSet, so long as the secondary DataSet contains a column that you can relate from the primary dataset.
SSRS 2016 Gets Some Love! As I type this, Microsoft is close to releasing SQL Server 2016. SSRS will receive a much-needed particularly the endFigure 16: Produces blank pages because the total ruler widthmakeover, (10.5 inches) exceedsinallowable user experience. width less margins (6.5 inches)
Tip #6: Relating Multiple Tables in a Report
Suppose you create an SSRS report that contains two datasets. The first dataset contains the primary report data and the second dataset contains lookup information. You’d like the SSRS designer to relate the two datasets based on some common field/key. Unfortunately, the SSRS designer doesn’t contain any such feature. Furthermore, the SSRS table (Tablix) report control only allows
codemag.com
Figure 17: Also produces a blank page at the end because of the extra white space.
SQL Server Reporting Services: Eight Power Tips
71
You can use Lookup to search a second dataset based on a specific common value and return a single column from the second dataset. You can use LookupSet when you want to return multiple columns from the second dataset. Finally, you can use MultiLookUp to lookup in a second dataset based on multiple values. Here is an excellent blog entry that covers these three functions: http://www.sql-datatools.com/2015/07/lookup-functions-in-ssrs.html
Tip #7: Implementing a Nested “Page X of Y” on a Report
Figure 18: Report that shows an Overall Page X of Y and a nested Page X of Y for a business context (the vendor)
Nearly every report writer provides sufficient pagination features to show “Page X of Y” on reports. However, users might want a nested “Page X of Y”. For example, suppose you’re generating a long report (160 pages) of detailed transactions by customer. You want to define overall pagination (Page 1 of 160, Page 2 of 160, etc.), but you also want to define pagination scoped to the customer (Page 1 of 2, if the current customer has two pages of transactions). Similar to Tip #6, prior to SQL Server 2008R2, developers struggled to implement this. You needed to manually calculate page definitions as part of the result set. This meant determining exactly where page breaks would occur, which might become error-prone if someone changes any aspect of the report layout (report margins, orientation, etc.). Fortunately, Microsoft implemented a new feature in SSRS 2008R2. Specifically, they redesigned the pagination to implement two sets of page numbering global variables: Globals!OverallPageNumber and Globals!OverallTotalPages, as well as Globals!PageNumber and Globals!TotalPages. In Figure 18, I’ve created two textboxes in the page header to use both sets of pagination values. However, that in itself does nothing. SSRS still generates “Page 1 of 166,” “Page 2 of 166,” etc., for both sets of page counts. So what else do you have to do? SSRS did exactly what you told it to do instead of what you wanted it do to. Even though the report contains a vendor grouping, the report could theoretically contain multiple groups, and SSRS doesn’t know which group to use for the nested pagination. You need to provide that information!
Figure 19: To set nested pagination, pull up the full property sheet for the Tablix control and set the Page Break properties.
In Figure 19, I’ve pulled up the full-blown property sheet for the Tablix control, and I’ve set the PageBreak properties (the dataset column that represents the grouping as the PageName property, and a value of TRUE for the ResetPageNumber property).
Tip #8: Implementing Running Aggregations
Even SSRS novices are aware that SSRS allows you to create report subtotals and grand totals very easily. However, does SSRS allow you to show running totals and running aggregations? Fortunately, the answer is “yes.” Not only that, but SSRS provides some very nice functionality to make this task easy. Figure 20 shows a sample report with three sets of running averages for chronological months in a year. Figure 20: An example report that shows running totals, running averages, and running maximum values (highest month to date)
72
SQL Server Reporting Services: Eight Power Tips
At first glance, you might think that SSRS provides three separate functions for the three columns. As it turns out,
codemag.com
CODE COMPILERS
May/June 2016 Volume 17 Issue 3 Group Publisher Markus Egger Associate Publisher Rick Strahl
Figure 21: RunningValue function with parameters SSRS provides just one function, called RunningValue. You provide the aggregation method as a parameter to the RunningValue function. Figure 21 shows an example. You pass three parameters to RunningValue: • The column value you wish to aggregate (the Monthly sales amount) • The aggregation method (Sum, Avg, Max, etc.). Note that SSRS color-codes the aggregation method in blue, indicating that SSRS treats it as a keyword. • The scope of the aggregation (the name of the group). In this instance, you’re aggregating months within a year. Every running aggregation contains a scope, so you need to provide the actual SSRS group name.
(Continued from 74) of the other driver. This ensures that I can’t use your speeding as an excuse to get out of my drunk driving, or vice versa; instead, it remains entirely focused on the exact moment of the crash. And this makes a certain amount of sense, except that it’s too hard, in most scenarios, to determine where that last responsible moment was. The code crashed because of a NULL reference on which an object was invoked—certainly, this is the fault of the developer who wrote that method call, right? Unless, of course, that object reference is an argument into this function, and it’s clearly documented that it cannot be NULL; then it’s the fault of the individual who wrote the code that passed it in, right? Unless, of course, that reference is actually set by the object/relational mapper when pulling data out of the database, and that relationship is supposed to have at least one member and it doesn’t, in which case it’s the fault of whomever wrote last to that table…and so on. And even if you believe in the most paranoid “defensive programming,” as soon as you move into multithreaded/ parallel programming, references can change even in the small amount of time between the null-reference check and its usage. We could do this for days. This all presumes, of course, that developers can even find the bug in question—in some cases, that’s assuming a lot.
codemag.com
Editor-in-Chief Rod Paddock
Final thoughts:
I hope I’ve provided some information to help you in building SSRS reports. In future articles, I’ll continue to show different SSRS power tips. As a SQL Server/Business Intelligence contractor/consultant, I’ve always found that working near the report layer helps me to understand the breadth and depth of the client’s business application. Users will always want functionality that the product doesn’t provide out of the box, so the value of SSRS depends on whether the tool provides enough hooks or open architecture for developers to extend it. Overall, SSRS does a very good job here. Kevin S. Goff
Managing Editor Ellen Whitney Content Editor Melanie Spiller Writers In This Issue Mohammad Azam Miguel Castro Sahil Malik John Petersen Walter Ritscher
Jason Bender Kevin Goff Ted Neward Rachel Reese Paul Sheriff
Technical Reviewers Markus Egger Rod Paddock Art & Layout King Laurin GmbH info@raffeiner.bz.it Production Franz Wimmer King Laurin GmbH 39057 St. Michael/Eppan, Italy Printing Fry Communications, Inc. 800 West Church Rd. Mechanicsburg, PA 17055
Summary
Regardless of where you fall on this question, it’s not going away any time soon. In fact, as software becomes more ubiquitous (is that even possible?), it stands to reason that software will, before too long, be directly responsible for more and more aspects of our lives. It already directs when we begin our day (alarm clocks), when we engage with others (schedulers), and for many, when we go to sleep (personal health monitors like the Fitbit or Band). And, if the reports from Google are any indication, before too long it will be driving our cars. Which, of course, brings this question all the way back to a much more practical, real question: if two self-driving cars get into an accident, with whom, exactly, does the fault lie for insurance purposes? If a self-driving car drives off the road for some reason, and there is a provable software bug that caused it, are the developers who worked on that code now liable for the damages to the car, the surrounding terrain, or even the families of those who were injured or killed?
Advertising Sales Tammy Ferguson 832-717-4445 ext 026 tammy@codemag.com Circulation & Distribution General Circulation: EPS Software Corp. International Bonded Couriers (IBC) Newsstand: Ingram Periodicals, Inc. Media Solutions Subscriptions Subscription Manager Colleen Cade 832-717-4445 ext 028 ccade@codemag.com US subscriptions are US $29.99 for one year. Subscriptions outside the US are US $44.99. Payments should be made in US dollars drawn on a US bank. American Express, MasterCard, Visa, and Discover credit cards accepted. Bill me option is available only for US subscriptions. Back issues are available. For subscription information, email subscriptions@codemag.com or contact customer service at 832-717-4445 ext 028. Subscribe online at www.codemag.com CODE Developer Magazine 6605 Cypresswood Drive, Ste 300, Spring, Texas 77379 Phone: 832-717-4445 Fax: 832-717-4460
Because—and let’s get this clear right from the start—if we don’t think about this, it’s certain that others will when the time comes. And we may not like their answers. Ted Neward
Managed Coder
73
MANAGED CODER
On Responsibility For an industry that prides itself on its analytical ability and abstract mental processing, we often don’t do a great job applying that mental skill to the most important element of the programmer’s tool chest—ourselves.
Over the last year or so, I’ve been writing a lot about management and related topics. But two incidents in the last several months have finally pushed me over the edge into getting back to a topic that is closer to the individual developer again. The first is the (as of this writing) ongoing saga around the San Bernardino shooter’s iPhone, and the US court order demanding that Apple comply with the FBI request for assistance in breaking into the device to retrieve information from it that the FBI deems useful and/or necessary to the investigation. The second is the ongoing saga around the Volkswagen emissions benchmark “hack” that changed the engine’s performance characteristics. This was based on information fed to the emissions benchmark from the sensors elsewhere in the car so that when the vehicle was being tested for noxious emissions, it operated in a less-performant but eco-friendly way, and otherwise pumped out much higher toxins than allowed by various national environmental standards (including both Germany’s and the US’s). In each of these cases, software lies at the heart of the story. But my concern is this: To what degree are software developers responsible for their actions in each case; to what degree will we be held responsible by the legal framework in which we live (be that in the US or elsewhere); and to what degree should we hold ourselves responsible?
Background Although it would be easier to debate or discuss if readers have a relatively basic understanding of each situation, each one can be abstracted away from the details pretty easily. In the VW case, software engineers were instructed by management—and as of right now, it’s not clear exactly from where those orders came, but popular perception holds that it definitely “came from above,” rather than originating with one of the engineers themselves—to deliberately change the operation of the vehicle under particular conditions. Whether the engineers understood that those conditions were the ones that would more than likely identify the vehicle as being tested for emissions standards is not entirely clear to me, but this is not new ground for us as an industry.
74
Managed Coder
Relational databases have been doing this for years: A number of different benchmark tools, called the TPC-A (through -E) benchmarks, are routinely run against the various RDBMS vendors’ products to determine relative performance numbers. And, as the story goes, database vendors cheat outrageously inside their databases in order to optimize specifically to those use cases, thereby distorting the actual numbers in order to return faster (and therefore presumably better) numbers against their competitors.
“Just Following Orders”
Programmers must build what they are told to build. We are, after all, performing “work for hire,” most of us, and in the same vein that we garner no benefit (in the form of profits, royalties, license payments, or some other money) from having written the software, neither can we therefore be held responsible for its effects.
This raises an interesting ethical question: If a software developer is asked to implement a feature that either leads to, or is, or is itself directly unethical, immoral, or illegal behavior, is that software developer liable for the damages caused?
In many ways, this is the same argument used at the Nuremburg Trials against the various members of the German Army and SS for their crimes against humanity. In a military hierarchy, if a superior officer gives you an order, you’re compelled to obey it or face court-martial. That means, of course, the officer who gave that order holds the responsibility for its effects, but as the individual who carried it out, you are immune from its effects.
Ethics, Not Legalities
The Last Responsible Moment
Just to be clear, the argument I want to have here is based around the morals and/or ethics of the issue, not its legalities. The reason for this is manifold: To start, the classic IANAL (I am not a lawyer) disclaimer is in full force here, and I defer any and all questions of the law to my friend and colleague, John V. Petersen, who also writes in this publication and may be so moved as to take up this question in a future issue. Secondly, though, is the simple fact that laws change with the times—the question of slavery in the US being just but one extreme example—and so what holds as legal today, may not be so tomorrow or vice versa. It is fully safe, I believe, to assume that the laws around software and liability will change as the role, nature, and responsibility of software increases in our daily lives.
But the “following orders” argument holds significant flaws: One is that, except for those programmers who are a part of the armed forces, we aren’t in a strict hierarchy, and there are no legal consequences from disobeying a superior. If instructed to implement a feature that you find unethical, immoral, or illegal, you are certainly capable of refusing to do so, and many in the industry would applaud you for it. You’d be unemployed, granted, but you wouldn’t be the one responsible—legally or otherwise—for said feature. Somebody else might, who can’t really afford to be unemployed for any length of time, but that’s their problem, not yours.
This is not without precedent—in the earliest days of the Industrial Revolution, no laws around machine safety, even for food production, were in place. As the impact of industrial processing grew, however, and abuses grew (particularly against those who worked the machines—read Upton Sinclair for more graphic details), legal frameworks were established and liabilities enforced. One might argue that the pendulum has swung too much the other way by this point, and another might argue the reverse and that still more protections are necessary, but either way, the fact remains that laws change.
Perhaps the right way to think about this is in the same way we think about technical decisions— that the individual who has the last responsible moment before an incident occurs is the one to whom we assign responsibility. Thus, since you are the one who wrote the code, you are the one responsible for it, irrespective of what your boss or management chain demanded. This is not without precedent—in the US, many states have a similar concept in place around the application of fault in a driving accident. If one driver had an opportunity to avoid the accident and didn’t take it (for whatever reason), that driver is at fault regardless of the actions or behavior (Continued on page 73)
codemag.com