Skip to main content

Mobomo webinars-now on demand! | learn more.

polymer

This article originally appeared on the Divshot blog. Divshot is a developer-focused platform for deploying modern front-end applications. It was also started with Intridea's support by Michael Bleigh!

There's so much new ground to cover with Web Components and Polymer that oftentimes some of the smaller conveniences are overlooked. Here are some of the cool things Polymer can do (or does for you) that you might not have noticed.

Built-In Element Methods

You may have used fire() to trigger custom events from a Polymer element, but that's not the only convenient little method that's available.

async(fn, inArgs, inTimeout) allows you to delay the execution of code (similar to setTimeout or requestAnimationFrame) while keeping this bound appropriately. It comes in handy for things like allowing an event to fully propagate before performing a dependent action.

// executes at the next micro-task checkpoint this.async(function() {   this.$.request.go(); }) 

job(jobName, fn, inTimeout) gives you the ability to limit how frequently an action is performed. Jobs are only executed after inTimeout milliseconds have passed without the same job being called again. You might have heard of this pattern as debounce. It's great for handling things like listening for noisy event streams like keystrokes or mouse movements.

// executes when it hasn't been called for 750ms this.job('keyup', function() {   this.fire('save'); }, 750); 

Layout Attributes

If you've browsed through Polymer examples and source, you might have come across attributes like layout, center, or flex sprinkled here and there. Polymer ships with declarative attributes that let you quickly and easily create complex layouts using CSS Flexbox.

Let's say I want to create a mobile-friendly layout with a header, footer, and content area.

<body fullbleed layout vertical>   <header>This will be at the top</header>   <main flex>This will fill available space</main>   <footer>This will be at the bottom</footer> </body> 

There's quite a bit to this, and the official documentation does a great job of laying it out.

Auto-Binding Templates

Polymer's template binding is powerful stuff, but what if you want to use it outside the context of an element's Shadow DOM? Well, then it's auto-binding to the rescue!

The auto-binding template is an extension of the <template> tag that automatically binds the element itself as the model.

{% raw %} <template id="greeter" is="auto-binding" repeat="{{name in names}}">   <p>Hello, {{name}}, how are you today?</p> </template> <script>   document.querySelector('#greeter').names = ["Alice", "Brenda"]; </script> {% endraw %} 

Because auto-binding templates can exist in the Light DOM (anywhere on the page, really) and don't depend on a user-defined custom element, they come in quite handy on occasion.

Attribute Reflection

Polymer makes it dead easy for attributes defined in the source code to configure properties on the DOM node, but did you know it goes both ways? By using a special form of published in your element definition, you can make it so that the attribute values change to match changing properties.

<polymer-element name="two-way">   <script>     Polymer('two-way', {       published: {         name: {           value: 'Bob',           reflect: true         }       }     });   </script> </polymer-element> 

If you were to use the above element in your page and changed the name property in JavaScript, the name attribute in the web inspector would change, too. Cool stuff!


Polymer is a library that keeps surprising me with how deep and well-designed it is (without even mentioning all of the core and paper custom elements!). It's way more than just a set of polyfills.

Have any Polymer hidden gems of your own you'd like to share? Let us know!

Categories
Author

Angularjs

Working on a project recently, I went through a process where I converted jQuery-style selectors and DOM manipulation to “The Angular Way”, and I thought I’d share my results.

This is one part of AngularJS I feel every web designer should become familiar with, as it shows how much easier it can be to understand JavaScript written by someone else when you use AngularJS. The reason AngularJS can be easier to understand is a result of its declarative nature, meaning you define click handlers right in your HTML, which in a larger project, can make it much easier to understand just what is going on under the hood.

This example will use a few simple buttons to show and hide content, and I’ll first demonstrate how we might accomplish this with jQuery, and then convert it to Angular. Suppose we were creating an app that allows users to create blog posts, and has three associated views. We might have the following buttons, and content sections:

<button id="btnCompose">Compose</button> <button id="btnPreview">Preview</button> <button id="btnPost">Post</button>  <div id="blogCompose">      <h1>Blog Compose</h1> </div> <div id="blogPreview" class="hide">      <h1>Blog Preview</h1> </div> <div id="blogPost" class="hide">      <h1>Blog Post</h1> </div> 

In jQuery, we would typically have to get all our buttons and assign a click handler that manages the display of the content (probably using a class attribute). Given the above HTML markup, our jQuery might look something like this:

$('button').on('click', function() {      var target = $(this).attr('id'); });  if (target == '#btnCompose') {      $('#blogCompose').removeClass('hide');      $('#blogPreview, #blogPost').addClass('hide'); } else if (target == '#btnPreview') {      $('#blogPreview').removeClass('hide');      $('#blogCompose, #blogPost').addClass('hide');  } else if (target == '#btnPost') {      $('#blogPost').removeClass('hide');      $('#blogCompose, #blogPreview').addClass('hide');  } 

We’re getting all the buttons on the page, and finding their id attribute. We then conditionally add or remove classes based on which button we’re clicking. The major drawback here is that it relies heavily on the structure of the HTML. If the HTML structure changes in the future, the jQuery selectors all have to be rewritten. Let’s take a look at how we might convert this to AngularJS.

First, here’s how we would change the markup:

<button id="btnCompose" ng-click="composeBtn()">Compose</button> <button id="btnPreview" ng-click="previewBtn()">Preview</button> <button id="btnPost" ng-click="postBtn()">Post</button>  <div id="blogCompose" ng-show="state.blogCompose">      <h1>Blog Compose</h1> </div> <div id="blogPreview" ng-show="state.blogPreview">      <h1>Blog Preview</h1> </div> <div id="blogPost" ng-show="state.blogPost">      <h1>Blog Post</h1> </div>  

You’ll notice two main changes here. First, we added what looks like an 'ng-click’ attribute to the buttons. ngClick is an Angular directive that calls a function in the associated controller. Next, we removed the classes from the content, and replaced with ngShow. ngShow is another Angular directive that shows or hides content based on its value. In this case, the values are state.sectionName. We’ll see what the state values are when we get to the JavaScript.

For now, take a moment and compare the jQuery and Angular HTML. The Angular markup gives you clues that clicking a button calls a function (and once you know more about Angular, you’ll know instinctively this function will live in the controller associated with this section of the DOM), while the jQuery buttons could have associated click handlers, but we don’t know without looking through the JavaScript, and we don’t have any assumptions about where those click handlers might reside in our JavaScript.

The “hide” classes we use with jQuery do communicate functionality, but can’t actually accomplish anything functionally. They’re more than likely associated with a CSS class with the display property set to “none”. The Angular ngShow directive allows us to define any number of conditions for the display of the content sections, and like ngClick, we know to look for this functionality in our associated controller.

Given this Angular markup, you might find the associated code in our controller for managing the application’s state:

//Defaults, page load $scope.state = {   blogCompose: true,   blogPreview: false,   blogPost: false }; //Manage state per button $scope.composeBtn = function () {   $scope.state.blogCompose = true;   $scope.state.blogPreview = false;   $scope.state.blogPost = false; };  $scope.previewBtn = function () {   $scope.state.blogCompose = false;   $scope.state.blogPreview = true;   $scope.state.blogPost = false; };  $scope.postBtn = function () {   $scope.state.blogCompose = false;   $scope.state.blogPreview = false;   $scope.state.blogPost = true; }; 

Here, we set the default state so the “compose" content is shown. This is what will be displayed when the app initially loads. We then manage the display of the content through functions assigned to the buttons. The “magic” here is that Angular handles assigning the functions to the buttons, and the state of the app. When we set the state property to either true or false, Angular knows to show or hide the associated content, via the ng-show directive in our markup.

The real beauty here is that we as developers aren’t responsible for parsing the DOM and making sure we have the right buttons or content sections based on the DOM structure. There’s no conditional logic in our JavaScript; we define a few variables and functions, and Angular takes care of the rest.

This is a very simple example, but you can imagine how it can make things easier as the size of your app scales and you have multiple developers changing your HTML markup. It not only makes it less error-prone, but its easier to understand what’s going on simply by reading the HTML or JavaScript, without having to actually run the app and interact with it.

I’ve created a functional JSFiddle demonstrating the AngularJS version, if you’re interested in seeing it in action: http://jsfiddle.net/xna5odq7/1/

Categories
Author

5apps

Paperless Post | For planning.

Paperless Post is an ecommerce company with a focus on design-driven online and printed stationery. E-cards can often feel subpar to the “real thing”, with personality driven options though, Paperless Post makes the digital card world aesthetic and beautiful. In addition, not only are the cards custom and unique, but PP offers a variety of features from tracking and internal updates to community event boards.

Uber | For time.

If you haven’t heard of these guys yet - you’re missing out. Uber is an on-demand taxi service that can be requested at any moment. Got a relative coming into town, but nobody to pick them up? Request an UberX for them via your phone and give your relative minute by minute updates as to the ETA of their car’s arrival.

Airbnb | For space.

Yes, we love our families, but sometimes extended stays can be a bit stressful. This year, why not book an Airbnb in the area? It’s better than a hotel, will give you space after hours with Uncle Arty, and will keep the family in a happy, stress-free state.

Seamless | For the inevitable hangry moments.

Seamless is your personal delivery food site and as the name would suggests - takes the headache out of online orders. With Seamless you can search for restaurants in your area, create an order, pay online, and get updates on your delivery status by the minute.

YogaStudioApp | For your health.

If there’s any time you shouldn’t skip out on health - it’s the holidays. Make time to take care of yourself. YogaStudioApp is a great resource with custom workouts based on the # of minutes you’ve got, your experience level, and the space you’re in. There’s no excuse, take time to refocus and keep yourself healthy!

What's your favorite app for the holidays? We'd love to hear from you!

Categories
Tags
Author

Orion_Service_Module

At 7:05am EST today, the world watched as NASA released its unmanned spacecraft, Orion, into the ether. With Captain Kirk (in doll form) at the helm, the massive capsule soared from Cape Canaveral with countless hopes attached. This new spaceship was built with one goal in mind: deep space exploration.

Orion’s 4.5 hour flight test was a critical step toward eventual near-Earth asteroid excursions, trips around the moon, and--most significantly--manned missions to Mars. That's right: with the success of Orion's launch would come “the beginning of the Mars era,” as NASA Administrator, Charles Bolden, remarked before blastoff.

And succeed it did! Completing two orbits and going farther than all rockets designed to carry astronauts have in the past four decades, Orion passed with flying colors, and landed in the Pacific Ocean at 11:29 this morning. Our biggest congratulations to NASA on an incredibly successful flight test! Mobomo is proud to be part of the team supporting NASA.gov.

Categories
Author

Insert picture of happy little trees
We all know Bob Ross (above) and his fascination with "Happy Little Trees" (or maybe that's my fascination). In any case, we can all take some advice from Bob and keep our Git trees happy.

If you took a look at your current project's git tree (through a tool maybe like Gitx) I bet it would look something like this:

Now I don't know about you, but that is really hard for me to follow. I can get the general picture, that there are some branches that merged back into master, but then there's some branching off of branches and merges between branches; I can't really tell what story the git tree is trying to tell.

Why do we care about "Happy Trees"?

It is my view of git that all of your commits should tell a story of new functionality. This lets your coworkers or, as in the case of Intridea, clients see very plainly the set of commits that led to a feature being added to the application. It potentially lets less experienced developers see what steps you took to add a feature, and shows your train of thought on how you went from 0 to feature (hero).

Essentially, keeping a happy and clean git tree is about communication.

What does a Happy (git) tree look like?

Great question! Everyone has their own opinions on what a happy and clean git tree looks like (well, some have no opinion at all); this is my opinion on what a happy and clean tree might look like:

I created this by just initializing a blank git repository and making some commits on master and two different branches. As you can see, it's clearly communicated what work occured on the branch and what commits happened directly on master. This makes it very easy to track down a potential bug, and which commit or merge may have caused it. Also, this makes it very easy to revert an entire feature should it no longer be wanted or if it's causing bugs. All you need to do is revert the merge commit and like magic, the feature is gone.

How do 'I' make my own Happy (git) trees?

Another AMAZING question! You too can have happy little (git) trees, just follow the guidelines below:

  • If you are working on a feature that takes more than one commit to implement (you are keeping small commits, right?), make a git branch git checkout -b <branch_name> and do all of your coding on there.
  • Before you are ready to merge it back into master, rebase it off of master git fetch && git rebase origin/master.
    • Essentially what a rebase does (if you do not already know) is rewrite your branch's git history by removing your commits, updating your branch so that it's the same as master then replaying your commits one at a time on top of the new HEAD. Note: For a more complete visual explanation of git rebase please see this.
  • When you are ready to merge your feature branch back into master, you can check out master git checkout master, update master via git rebase origin/master, and merge your branch using the "no fast forward" option git merge --no-ff <branch_name>
    • What the --no-ff option does is not replay your commits back onto master, but instead makes a merge commit of all the changes in your branch (It should be noted that this is what a GitHub Pull-request does)

A bit more about git rebase and conflicts

Often, if you are working on a team, you will have conflicts when you rebase your branch from master. This is fine--do not panic--it just means that another person updated the same code you did and merged it in before you. What will happen then is the rebase will stop in the middle and you will have to manually fix the conflicts. If you type in git status it will show you which files have conflicts, you can simply open them and see which changes you made that are in conflict with the changes on master (which will be denoted with <<<<<< and ========), then add or delete the code necessary to incorporate both changes in the code (if necessary). Once you are done fixing the conflicts (this includes deleting the <<<< and ==== mark up), go back to the command line and type git add . then git rebase --continue to continue along your path to having a happy (git) tree.

It is for this reason that if I am working on a feature branch, I rebase it after almost every commit, just to make sure I am kept up to date with master. I don't like fixing conflicts, and this ensures that I have as few of them as possible at one time.

Now, a bit more about GitHub and rebasing

If you are like me, you use GitHub to store your code, and you use pull requests on GitHub to have someone review your code (you are having someone review your code, right?). In order to take advantage of GitHub and git rebase you will need to rebase your branches before you make a pull request, otherwise you could end up with a spaghetti like tree as shown above.

Keep the conversation going! We'd love to hear from you!

Categories
Tags
Author

dear maggie
Dear Maggie,

When it comes to the winter season, I am all the more grateful I get to skip the commute and work from home! Nothing beats waking up to snow, and knowing you don’t have to leave your cozy house to get work done.

There’s just one thing - it seems there’s a direct correlation with the increase in my waistline and the decrease in my commute. Incorporating exercise into my daily routine has become increasingly difficult as the reasons for leaving the house have continued to diminish. Help! Do you have any tips for staying fit while working from home?

Frustrated in Frisco


Howdy Frustrated,

You are not alone! It’s so easy to develop an unhealthy routine while working from home. Not only do you move less in a day, but you’re also within easy reach of that lovely space called kitchen.

In my experience, the key to staying fit is pinpointing the problem areas and identifying a solution that works for you. Here are a few tactics I’ve found successful throughout the years. Granted, while there’s no “one size fits all” these tips are definitely a step in the right direction.

Track your activity.

There are lots of options out there, the Fitbit Flex, the Misfit Shine, “UP” by Jawbone, and even most newer phones have some sort of built-in step tracker. Utilize these programs to track exactly how much activity you’re getting in a day. You can set daily goals for yourself, and even compete against friends and family in order to increase your accountability.

Track your calories.

You shouldn’t have any trouble finding a calorie tracking app on your favorite smartphone. Weight Watchers has an application to match their very successful program, MyFitness Pal can track your activity as well, and MyPlate by livestrong.com makes entries on the go a breeze. And if all else fails, go old fashion and keep a food diary. Simply forcing yourself to write down what you eat has an amazing effect on your calorie consumption and eating habits.

Get a workout buddy.

Anyone can create a gym schedule, but its all too easy to skip out when no one is expecting you there. Find a friend looking to go to the gym, and coordinate your schedules. Not only does this open up the possibility of ride-sharing, but at many gyms “couple” memberships offer a great discount vs. individual memberships, and you usually don’t have to kiss the other person to get the discount.

Put your desk to work.

This one might seem silly, but you’d be surprised how much exercise you can fit in WHILE you work. Keep a set of free-weights near your desk and take calls while doing a few lunges. Got a yoga mat? Set your computer to alert you every few hours for a 5-10 minute yoga break. Or why not invest in a standing desk? Whatever it is - you’ve got time - you’ve got the resources - you just have to get creative and discipline yourself to do it.

Pre-plan your hanger.

When it comes time to feed yourself, the easiest worm always gets eaten first and it’s usually disguised in sugary-salty goodness. My best advice for overcoming hanger snacking: buying healthy foods and planning your meals and snacks in advance. Apps like ShopWell allow you to scan the barcodes of items you’re considering for purchase so you can see healthier (and sometimes cheaper!) alternatives. If your problem is inspiration, Su Chef provides healthy recipes (by ingredient), or Blue Apron will deliver all the ingredients and recipe you need for an exciting meal. When you spend a little extra time pre-portioning and preparing - you give yourself a fighting chance for picking the healthier option.

Breaking old habits and getting into a healthy routine can be done! Just remember, working from home should enable a healthy work-life balance, not be an excuse for ignoring it. Take advantage of your work situation, find creative ways to stay active, and have fun doing it!


Got an idea for our next post? Send your burning questions, feedback, or suggestions over to dearmaggie@intridea.com! We'd love to hear from you.

Also, check out Dear Maggie's advice on relationships and working remotely.

Categories
Author

EzraZygmuntowicz
Ezra Zygmuntowicz passed away last week. A titan in the Rails community, Ezra co-founded Engine Yard and was the original developer of the Merb framework.

Ezra was crazy smart, scary smart and spoke with equal felicity about code, 3D printing, and glass blowing. In an industry where intelligence positively correlates with arrogance, Ezra was unfailingly humble and always made time to share his knowledge.

It's no secret that Ezra's personal and health issues limited his career after an early promise. After winding down his 3D printing company Trinity Labs, Ezra served as CTO at Intridea before going on to co-found Stuffstr. If he was unable to complete the projects he started late in life, he was still smart enough to ask the right questions at the right time.

Ezra was questing to the end. I last heard from him two weeks ago when he reached out to me with an idea for a mobile app. His death is a loss to the community, and I will miss him.

Categories
Author

javascript
Coding JS is an art. As we continue in our JS Fundamentals series we’ll unlock layer by layer the basics to working confidently in the land of Javascript.

Mastering the concept of “scope” is an important step towards mastering writing efficient, easily maintained JavaScript. In this installation of the JS Fundamentals series, we’ll take a high-level look at how JavaScript’s scope works.

There are two levels of scope - “global” and “block.” Of the two, the easiest to explain is global. Global scope variables and functions can be accessed by any portion of the environment in which it’s been created. Examples of this are the window Object or the setTimeout Function in the browser, and Node.js’s require Function.

“Block” scope is a little more tricky - a block scope gets created when you write a Function or a try/catch statement. Unlike other C-based languages, blocks that are defined by braces ({ and }) do not create a new scope; at least, not until ECMAScript 6 is adopted.

Let’s look at a few practical examples.

var foo = “Global variable”;  function globalFunc() {   var bar = “Scoped variable”;    function scopedFunc() {     console.log(‘foo is: ‘ + foo);     console.log(‘bar is: ‘ + bar);   };   scopedFunc(); } globalFunc(); scopedFunc(); 

If you run this, the output that you’ll receive from the two function calls would be “foo is: Global variable”, “bar is: scoped variable”, and a ReferenceError because scopedFunc doesn’t exist in the global scope.

This enables us to write functions (and store variables) that are only relevant to the execution of that scope, which is useful for when you’re running repeated tasks that only need to happen when a specific function is being called, but you don’t necessarily need it to be available anywhere else. Additionally, leveraging scope in this way means that any large variables (if you’re doing some intense data processing like manipulating the points of a GeoJSON map, for example) they can be picked up by the garbage collector when the function’s task is complete, and not continue to sit in memory.

Since we have access to global variables inside of a scope, those variables can get reassigned. Take this example:

var reassignable = ‘I am in the global scope’;  function reassigner() {   reassignable = ‘I am in the block scope’; }   console.log(reassignable); // >> “I am in the global scope”  reassigner(); console.log(reassignable); // >> “I am in the block scope” 

This will return the string “I am in the block scope” in the JavaScript console. Of course, the caveat to this is that you need to be careful with naming your variables. If you reuse a global variable, you’ll overwrite it.

Why is this important?

Scope is crucial to understand when working in JavaScript. The examples we’ve seen so far have been simplistic. One of the most challenging aspects of JavaScript for new developers - the this keyword - is heavily impacted by the scope in which it is being called.

However, it’s bigger than just keeping your this keyword context straight (and that particular subject will be focused on in an upcoming post).

If you’re working on a project of any substantial size (more than some basic prototyping, or a very simple static site with a bit of JavaScript thrown in for some enhancements), chances are good that you’re working with external libraries like jQuery or Modernizr (or, perhaps, both). How do you keep these libraries from colliding with one another? If you have both jQuery and Underscore, for example, how do you ensure that their various each methods don’t get overwritten by the other library?

Technically, these libraries use what’s known as “namespacing,” which is the practice of applying a scope to the library that prevents that library’s methods and variables from overriding the methods and variables of the environment to which they’ve been added.

jQuery uses two - one as an alias of the other - names: jQuery and the more familiar $. Underscore, as you might expect, uses the _ character. Therefore the respective each methods would be $.each and _.each. This is possible through what we’ve been discussing with scoping.

If you look at the code for either library, you’ll see that both are wrapped in something that looks like this:

(function (args…) { })() 

This is what’s known as a closure (and, more specifically, an “Immediately Invoked Function Expression” or IIFE). Though we won’t dig in to closures right now, what we know from what’s been covered so far is that any variables defined within a function is constrained to that function. Therefore, the authors of Underscore can implement their each method inside of that closure and not have to worry about whether or not what they write will interfere with a similarly named function that other libraries may introduce.

Finally, it’s important to note that scope and context aren’t the same thing. Context is all about the specific situation where something is being run, and is most easily explored through the use of the this keyword. Scope is what helps define context.

Variables

As we covered already, variables defined in a scope belong to the scope in which they’re defined. If a new scope is defined within the parent scope, they’ll be accessible the same way that global variables are accessible inside of block scopes.

There’s a caveat to this: if you forget to include the var keyword, any variable you create in a scope is automatically added to the global scope:

var globalVar = 1;  function someFunc() {   var scopeVar = 2;   otherVar = globalVar + scopeVar; }  someFunc();  console.log(‘globalVar: ‘ + globalVar); // >> 1 console.log(‘otherVar: ‘ + otherVar); // >> 3 console.log(‘scopeVar: ‘ + scopeVar); // >> ReferenceError: scopeVar is not defined 

Wait, what was that try/catch thing you mentioned earlier?

The one other place that creates scope, at least in ECMAScript 5, is the try/catch block. This is typically used when you know that a portion of your program has potential to throw an error and you want to make sure that it’s properly handled. The catch portion of this is essentially a function that accepts one argument - the error Object - and it allows you to work with that:

try {   undefFunc(); } catch (err) {   console.log('Error:');   console.log(err);   console.log('--------------------'); }  console.log('Error:'); console.log(err); 

This will print out:

“Error:” [object Error] { ... } // depending on your logging environment, this will either be an object you can interact with or just an indication that an object was logged “--------------------" “Error:” ReferenceError: err is not defined 

Note that the err variable isn’t available in the global scope, while it is available in the catch block’s scope.

In Conclusion

Scope is an important concept to grasp when working with JavaScript (really, with any programming language). As Kyle Simpson points out in his You Don’t Know JavaScript: Scopes & Closures, it’s important to remember that JavaScript is actually not a dynamic language - it’s compiled. When a browser loads a JavaScript file, it offloads the contents of that file to its JavaScript engine, where it’s read, compiled, and executed. Because of this process, it’s important to understand how scopes function, Otherwise you’ll likely run in to unexpected behaviors and errors, especially once your application grows in complexity.

A few good resources (in addition to Kyle Simpson’s book) that help with understanding JavaScript Scope are:

Categories
Author

Web App

This article originally appeared on the Divshot blog. Divshot is a developer-focused platform for deploying modern front-end applications. It was also started with Intridea's support by Michael Bleigh!

A few months ago I came across "Rails' Degenerate Front-End Support" taking Rails to task for slipping behind the state of the art when it comes to front-end dependency management. The pain is real, but the problem isn't that Rails does a bad job with front-end dependencies. The problem is that the front-end has no business living in a Rails app at all.

Web applications in 2005 were engines of request and response. You were always interacting with a snapshot of the data, never the data itself. In this era, it made perfect sense for the server to generate the HTML that landed in the browser. After all, browsers were terrible! IE7 had just come out, Firefox had only a tiny sliver of the market, and anything you could do to keep things server-side was a win.

Sure, AJAX existed and could be cajoled into helping out here and there. By and large, however, the server was running the show. What a difference a decade makes.

What has happened since 2005 came about so gradually that many haven't even realized it happened. Browsers got better. Not just a little better. Browsers got magnificently, insanely, unbelievably better. They became faster, they supported more standards and newer standards, they landed on whole new device categories without giving up (too much) ground. Even better, they started releasing much more frequently ensuring they would continue to get better. In the past decade, the browser has evolved from a document viewer that could be hacked into a basic application provider to a full-blown operating system in its own right.

Backbone, Mind Blown

It all started with Backbone, first of the mainstream modern JavaScript frameworks. Like Rails before it and with just around 1,000 lines of code, it had the audacity to challenge the whole way we built applications. Sure, I'd wired up some pretty complex jQuery code by 2010, even dabbled with real-time, but Backbone was the first library that shouted in your face "your front-end is an application." It showed us a map that showed just how far we had come since 2005, and gave us a glimpse of what was to come.

The seed of the idea had been planted. Your front-end is an application. It makes perfect sense and has now for some time. We test the limits. We move from pull to push. We build more and better interaction away from the request/response mindset. And our users love it. The web is more alive today than it's ever been.

So where does that leave Rails?

We're Gonna Need a Bigger Toolbox

Rails has an inherently monolithic mindset. It's a web app in a box. It's a fantastic framework for tying application code to a data store and preparing that data for rendering to the browser. It has thousands of libraries written by thousands of developers to do thousands of things extremely well. You can still build a great modern web application using Rails. You just can't build one only using Rails.

Yes, Rails has the asset pipeline. There are gems that let you use Bower, Angular, Ember, and everything else you might imagine inside your Rails app. But front-ends aren't kids anymore, and it's starting to get embarrassing when they're still living in their parents' basement.

By now, most Rails developers have picked one or more of the modern JS frameworks to use when building apps. Whether it's Backbone, Angular, Ember, or something else. It's not a change in tools that's needed, it's a change in perspective.

The back-end is for persisting and retrieving data. The front-end is for user interaction. And the two should not live in the same repo anymore.

A Modern Approach

I still love Ruby. JavaScript runs in the browser and has better conventions around concurrency, but if I have a choice of language I'm still picking Ruby. But today when I start building a new application, I make two folders: myapp-api and myapp-web.

In my front-end app, I use a variety of tools suited to the purpose like Yeoman, Broccoli, Grunt, or even just bare HTML. In my back-end app, I create a JSON interface to my application logic and data stores using Grape or Sinatra. I deploy the two applications to two separate servers: api.myapp.com for the back-end, and myapp.com or www.myapp.com for the front-end.

The result is a change both much smaller and much bigger than you'd expect. Before I had completely cut the cord between front and back-end, I was already effectively building two separate applications. I just had the artificial constraint that they needed to live in the same repository for some reason.

After the separation, I find that my architecture is much cleaner. I can focus on a single purpose. When I'm working on the back-end, I worry only about a clear and logical representation of application data. When I'm working on the front-end, my only concern is user experience. More than that, it feels right. It feels like I'm building things correctly.

I'm not saying Rails is dead. While I've moved on to lighter frameworks (admittedly, one I designed myself), Rails is still an extremely capable way to implement your web app's back-end. I just think we should stop pretending that there can or should be a single framework that encompasses all the needs of a modern web application.

Keeping front-end and back-end tightly coupled is condemning them to run a three-legged race, awkwardly tied together when both would benefit from separation. Cut the tie. You'll be glad you did.


Categories
Author

 

angular-ruby-on-rails-logo
In Part 2 of Angular with Rails series, we covered creating a Rails API with tests in RSpec and setting up an AngularJS app that reads from the API.

In Part 3 we will cover:

  • Adding CSS
  • Adding editing functionality
  • Creating a custom filter
  • Setting up JavaScript tests

Setup Bootstrap Sass

In order to support editing functionality we'll add some CSS to the UI. Following the README at bootstrap-sass, we'll add to our Gemfile:

gem 'bootstrap-sass', '~> 3.3.0' 

Add to our app/assets/javascripts/application.js:

//= require bootstrap-sprockets 

Rename app/assets/stylesheets/application.css to application.css.sass (if you prefer sass over scss) and add:

@import "bootstrap-sprockets" @import "bootstrap" 

Then we'll add some classes to our employee index in app/views/employees/index.html.erb:

<div ng-app='app.employeeApp' ng-controller='EmployeeListCtrl' class="col-xs-8">   <h1>Employees List</h1>   <table ng-if="employees" class="table table-hover table-striped">     <thead>       <th>Name</th>       <th>Email</th>       <th>SSN</th>       <th>Salary</th>     </thead>     <tbody>       <tr ng-repeat="employee in employees">         <td>{{employee.name}}</td>         <td>{{employee.email}}</td>         <td>{{employee.ssn}}</td>         <td>{{employee.salary | currency}}</td>       </tr>     </tbody>   </table> </div> 

Add the Edit API

Next, we'll update our Rails API to support editing of employee records. Let's start by adding some tests for the update method in spec/controllers/api/employees_controller_spec.rb:

require 'spec_helper'  describe Api::EmployeesController do   before(:each) do     @employee = create(:employee, name: 'Calhoun Tubbs', salary: 50000)   end    describe '#index' do     it 'should return a json array of users' do       get :index       result = JSON.parse(response.body)        expect(result[0]['name']).to eq('Calhoun Tubbs')     end   end    describe "#update" do     it 'should successfully respond to edits' do       put :update, id: @employee.id, employee: {             id: @employee.id,             salary: 60000           }        expect(response).to be_success     end      it "should change the employee's salary" do       @employee.update_attribute(:salary, 50000)        put :update, id: @employee.id, employee: {             id: @employee.id,             salary: 60000           }        expect(@employee.reload.salary).to eq(60000)     end   end end 

At this point, these tests will fail. We need to create an update method in our API controller, but first we must specify what params are allowed on update (Rails' strong parameters) at app/controllers/api/employees_controller.rb:

class Api::EmployeesController < ApplicationController # section omitted   private    def employee_params     attributes = [       :salary,       :name,       :email,       :ssn     ]      params.require(:employee).permit(attributes)   end end 

Now, our update action is simple. The code will try to update the employee object and render the errors if there's a problem.

class Api::EmployeesController < ApplicationController   # section omitted   def update     employee = Employee.find(params[:id])      if employee.update(employee_params)       render json: employee     else       render json: employee.errors.messages, status: :bad_request     end   end   # section omitted end 

Next run the tests to make sure they pass. For a more complex application there would be more tests, but in our example these will suffice.

Add the Edit Front End

For our employee edit functionality we'll define a modal dialog containing a form and a submit button. The Angular-bootstrap package contains a modal dialog tool. We'll add the angular-bootstrap package to our bower.json:

{   "lib": {     "name": "bower-rails generated lib assets",     "dependencies": {       "angular": "v1.2.25",       "restangular": "v1.4.0",       "angular-bootstrap": "v0.11.0"     }   },   "vendor": {     "name": "bower-rails generated vendor assets",     "dependencies": {     }   } } 

And bundle exec rake bower:install

Next, require angular-bootstrap in app/assets/javascripts/application.js:

//= require jquery //= require jquery_ujs //= require angular //= require angular-rails-templates //= require lodash //= require restangular  //= require angular-bootstrap //= require bootstrap-sprockets  //= require angular-app/app // rest of the file omitted 

Finally, we'll add it as a dependency to our angular app in app/assets/javascripts/angular-app/modules/employee.js.coffee.erb:

@employeeApp = angular   .module('app.employeeApp', [     'restangular',     'ui.bootstrap'     'templates'   ])   .run(->     console.log 'employeeApp running'   ) 

Now, we need to add a modal controller to our AngularJS app. This controller will handle popping up a modal editable form and submitting it to our Rails API. In app/assets/javascripts/angular-app/controllers/employee/EmployeeListCtrl.js.coffee:

angular.module('app.employeeApp').controller("EmployeeListCtrl", [   '$scope', 'EmployeeService', '$modal',   ($scope, EmployeeService, $modal)->      $scope.editEmployee = (employee) ->       modalInstance = $modal.open({         templateUrl: 'employee/edit.html',         controller: 'EmployeeEditModalCtrl'         size: 'lg'         resolve:           employee: ->             employee       })        modalInstance.result.then(->         console.log 'edit closed'       ) # rest of file omitted 

Next, we'll create the modal controller at app/assets/javascripts/angular-app/controllers/employee/EmployeeEditModalCtrl.js.coffee:

angular.module('app.employeeApp').controller('EmployeeEditModalCtrl', [   '$scope', '$modalInstance', 'employee', ($scope, $modalInstance, employee)->     $scope.submitEmployee = ->       console.log 'submit employee'      $scope.cancel = ->       $modalInstance.dismiss('cancel')  ]) 

In the above code we're defining a modal controller that receives the employee object we passed in the prior controller, along with function stubs for the save / cancel buttons in the dialog. Next, we need to define the edit form template in app/assets/javascripts/angular-app/templates/employee/edit.html.erb:

<div class="modal-header">   <h4 class="modal-title">Edit Employee</h4> </div>  <div class="modal-body">   <form name="" ng-submit="submitEmployee(employee)">     <div class="form-group">       <div class="row">         <div class="col-xs-12">           <label>Name<span class="required"> required</span></label>           <input class="form-control" ng-model="employee.name">         </div>       </div>     </div>      <div class="form-group">       <div class="row">         <div class="col-xs-12">           <label>Email<span class="required"> required</span></label>           <input class="form-control" ng-model="employee.email">         </div>       </div>     </div>      <div class="form-group">       <div class="row">         <div class="col-xs-12">           <label>SSN<span class="required"> required</span></label>           <input class="form-control" ng-model="employee.ssn">         </div>       </div>     </div>      <div class="form-group">       <div class="row">         <div class="col-xs-12">           <label>Salary<span class="required"> required</span></label>           <input class="form-control" ng-model="employee.salary">         </div>       </div>     </div>      <div class="modal-footer">       <button type="button" class="btn btn-default" data-dismiss="modal" ng-click="cancel()">Cancel</button>       <button type="submit" class="btn btn-primary">Save Changes</button>     </div>   </form> </div> 

Finally, if you run the code now you'll get an error loading the template. We need to tell our angular-rails-templates gem what the base path for our templates is. This is necessary because we are using the rails asset pipeline to combine our javascript files. In config/application.rb:

require File.expand_path('../boot', __FILE__) require 'rails/all'  Bundler.require(*Rails.groups)  module AngularExample   class Application < Rails::Application     # the path relative to app/assets/javascripts     config.angular_templates.ignore_prefix  = 'angular-app/templates/'   end end 

Next, we can return to our employees index and provide an ng-click attribute to launch the edit form. We'll add this link on the employee name cell in the table in app/views/employees/index.html.erb:

# rest of the file omitted <td><a ng-click="editEmployee(employee)">{{employee.name}}</a></td> 

Now, clicking an employee's name will launch a modal form with fields containing that employee's data. However, we still need to hook up the Save Changes button to our Rails API. In app/assets/javascripts/angular-app/controllers/employee/EmployeeEditModalCtrl.js.coffee:

angular.module('app.employeeApp').controller('EmployeeEditModalCtrl', [   '$scope', '$modalInstance', 'employee', ($scope, $modalInstance, employee)->     $scope.employee = employee      $scope.submitEmployee = (employee)->       employee.put().then(         $modalInstance.close('saved')       )     # section omitted ]) 

We can simply say put() above because we are using Restangular to manage that object.

One problem with the above code is that while the server is applying validation to the object, the client side AngularJS portion is not. One downside of using client-side MVC is the fact that validation from the server must often be duplicated on the client. The subject of AngularJS form validation could be a blog post on its own. I'll leave that as an exercise for the reader.

Secure the UI

Though the editing functionality now works, notice the page still shows each employee's social security number. In order to mask the SSN and only reveal it in edit form, we can create an AngularJS filter. In app/assets/javascripts/angular-app/filters/employee/empMaskNumber.js.coffee

angular.module('app.employeeApp').filter('empMaskNumber', ()->   (value, nonMaskLength) ->     if value?       maskLength = value.length - nonMaskLength - 1       if maskLength > 0         v = value.split("")         for i in [0..maskLength] by 1           if v[i] != '-'             v[i] = '*'         v = v.join('')       else         value     else       value ) 

It's recommended that you name filters with an app prefix to avoid colliding with AngularJS's built-in filters. Let's edit our employee table to make use of the filter in app/views/employees/index.html.erb:

# section omitted <tr ng-repeat="employee in employees">   <td><a ng-click="editEmployee(employee)">{{employee.name}}</a></td>   <td>{{employee.email}}</td>   <td>{{employee.ssn | empMaskNumber: 4 }}</td>   <td>{{employee.salary | currency}}</td> </tr> # section omitted 

Viewing the employee table again you should see the SSN field being masked with asterisks except the last 4 digits. While this filter is simple and apparently works, when building more complex filters we will need javascript tests.

Setup JavaScript Tests

Next, we'll setup jasmine-rails. Add jasmine-rails to the Gemfile:

group :test, :development do   gem 'jasmine-rails' end 

And add angular-mocks to the bower.json:

{   "lib": {     "name": "bower-rails generated lib assets",     "dependencies": {       "angular": "v1.2.25",       "restangular": "v1.4.0",       "angular-bootstrap": "v0.11.0",       "angular-mocks": "v1.3.2"     }   },   "vendor": {     "name": "bower-rails generated vendor assets",     "dependencies": {     }   } } 

Then run:

bundle install rails generate jasmine_rails:install bundle exec rake bower:install 

Next, we need to setup our spec_helper. In app/assets/javascripts/spec_helper.coffee:

#= require application #= require angular-mocks #= require sinon  beforeEach(module('app', 'app.employeeApp'))  beforeEach inject (_$httpBackend_, _$compile_, $rootScope, $controller, $location, $injector, $timeout, $filter) ->   @scope = $rootScope.$new()   @compile = _$compile_   @location = $location   @controller = $controller   @injector = $injector   @http = @injector.get('$httpBackend')   @timeout = $timeout   @model = (name) =>     @injector.get(name)   @filter = $filter   @eventLoop =     flush: =>       @scope.$digest()  afterEach ->   @http.resetExpectations()   @http.verifyNoOutstandingExpectation() 

Note the beforeEach line above. As your application grows and more angular apps are added, they must also be added here to be available for testing. The above spec helper also requires SinonJS. Download the file and place it at vendor/assets/javascripts/sinon.js .

Now you should be able to run an empty test suite:

RAILS_ENV=test bundle exec rake spec:javascript 

Which should return 0 specs and 0 failures. We are now ready to write our test for our filter defined above. In spec/javascripts/filters/emp_mask_number_spec.js.coffee:

#= require spec_helper  describe 'Filters', ->   describe 'EmpMaskNumber', ->     beforeEach ->       @maskFilter = @filter('empMaskNumber')      it 'should mask the value with asterisks', ->       @value = "123"       @nonMaskLength = 0        expect(@maskFilter(@value, @nonMaskLength)).toEqual "***" 

In a real application expect to have more specs than just the above. At this point though, you have everything you need to continue building your application and expanding. You can find the code for this example on Github.

Need to catch up on the Angular with Rails series? Check these out:

  • Angular with Rails Part I
  • Angular with Rails, Part II

Resources:

Categories
Author
Subscribe to General