Wednesday, July 24, 2013

Responsive Layout with AngularJS

Context

Over the last month or so, I have been diving into the maelstrom of web development. That being said, I'm new to JS, AngularJS and HTML5 in general. I have found it considerably hard-going to determine the "best" path to comprehensively learn the trade of developing a web application. The purpose of this post is to submit a question/idea about one particular aspect that is still not clear to me and get feedback/direction from more experienced developers.


The Problem (with Media Queries)

One of the first concepts I came across that started to put my concerns about designing for the browser at ease was responsive design. The concept is great, but the practice is dissatisfying to me. In trivial cases (static web pages), changing the styles of my components based on screen sizes demonstrably does the trick, but when I started to try to apply this approach to even simple applications, it quickly felt very wrong. Showing/hiding components from a style sheet is, to me, supplementing content-structure changes. Additionally, using logical expressions in a style sheet seems off the wall and out of nowhere. The problem boils down to this for me: I cannot look at my template and tell what content will be present and when. That's a red flag to me that my application is going to get difficult to manage quickly.


HTML is the Template

AngularJS allows you to realistically treat your HTML markup as your template. A significant result of this is that you can look at your template and see how it will become your view into your data model. So far, there is no need to refer to a style-sheet in order to understand what your view will be. So, can we leverage the flexibility AngularJS gives us to prevent this awkwardness? Yes, I'm confident there is a way. Do I know the best way? No, but I have an idea.


A (better?) Solution

As soon as I start hiding and showing elements from my style-sheet, I think that indicates structure change, which should be the template's job. So, What if we had a HTML component that indicated what its contents would be based on its width, for example... Something like:



Ignore the "resp-div" for the moment, I'll get to that, but notice how breakpoints are described on the element and corresponding templates are also identified here. The benefit here, I think, is that you can tell from looking at the template what the contents of that div will be at different break points. If this is more appropriate than putting this info into the styles, how could we achieve this?

Angular provides a tool called a Directive which:
...are a way to teach HTML new tricks
To make this work, we'd need that div to behave the way it describes itself. This can be done with a very simple directive:



I tried to comment the code well, but let me describe what this is doing. The directive defines a component that will change its contents based on which break-point its width falls within. It defines a small set of breakpoints (in this case small and med) and corresponding templates that can be passed via the attrs argument of the link() function. When the (window) width changes, checkBreak() is called and checks if we have entered a new break-point. When a new break-point is hit, the scope.template value changes, triggering the ng-include directive to insert the corresponding template into its element (a div). It's very simple and very re-usable. I can use it on any container and even include a "default" template when no breakpoints are satisfied. So I can use the exact ideal syntax I proposed earlier:



I can simply change the break-point or template values in the HTML tag and the Directive will respond appropriately. Additionally, my Directive could have better, more robust parameters for breakpoints, but I think these two demonstrate the concept. 

Now my styles can be applied to the appropriate templates and I never have to look at my style-sheets to determine the structure of my view. 


Notes

A couple things are not ideal:

  • I'm not sure how to trigger on the element itself's width changes, so I had to inject the $window and bind to its resize changes.
  • I'm not sure if clientWidth is the appropriate value to check


Nothing is preventing "view/small.html" and "view/med.html" from sharing and using some of the same components, so we have not painted ourselves into the corner of having to rewrite different templates for different screen sizes. We are just composing them differently. Now, I can look at "view/small.html" and tell which components are included, instead of always including every component and using CSS to hide and show them when necessary.

Note that this allows you to have a responsive layout within sub components and not only based on window size.


Conclusion

Ok, so there are my thoughts. I'm really hoping to get guidance and feedback on this. I apologize in advance if this has already been covered, I have been able to find little discussion concerning AngularJS and Responsive Design.