Menu Navigation Menu

In my time with Angular.js, I’ve run into two common annoyances that often happen during the initial load of a page containing an Angular app: an empty view and an API data delay. These annoyances are particularly likely on a slow mobile connection. But I’m here to show you what they are, and how to fix them!

Annoyance 1: Empty View

The first action a typical single-page Angular app takes during initialization is to send a request to the server for its first view template. This extra request adds a very annoying delay, particularly on non-optimal connections such as mobile. Even worse is the fact that the app’s view container is completely empty until the template is fetched.

We can avoid this by including the template as a script tag of type text/ng-template with id matching the templateUrl set in the $routeProvider configuration. Angular automatically loads these into the $templateCache during app initialization. It’s important to note that the script tag must be inside the ng-app element, not in the page’s <head>.

Taking it a step further, we can also improve the responsiveness of page transitions by including all of the app’s templates rather than just the initial one.

See the Pen jWpKjZ by Cody Sims (@codysims) on CodePen.

For my Angular on Rails apps, I use the following helper to insert all of an app’s templates from app/assets/javascripts/<app_name>/templates within the ng-app element:

def angular_templates(app_name, prefix = nil)
  script_tags = ''
  templates_pathname = Pathname.new(Rails.root.join('app', 'assets', 'javascripts', app_name, 'templates'))

  file_paths = Dir[File.join(templates_pathname, '**', '*')]
  file_paths.each do |file_path|
    pathname = Pathname.new(file_path)
    next if pathname.directory?

    # remove the template folder parts from the front of the array of path parts
    template_id_parts = pathname.each_filename.to_a.last(pathname.each_filename.count - templates_pathname.each_filename.count)
    # replace filename extensions (.erb, .html.erb, .haml, .html.haml) with .html
    template_id_parts.last.sub!(/(\.erb|\.html\.erb|\.haml|\.html\.haml)$/, '.html')
    template_id = template_id_parts.join('/')

    if prefix.present?
      template_id = "#{prefix}/#{template_id}"
    end

    script_tags += content_tag(:script, render(file: pathname), id: template_id, type: 'text/ng-template')
  end

  script_tags.html_safe
end
<div ng-app="demoApp" ng-controller="demoAppController">
  <%= angular_templates('demo_app') %>
  <div class="ng-view"></div>
</div>

Annoyance 2: API Data Delay

Another common action after an app has initialized is for the page’s controller to send an API request for data that the page needs to display immediately. Once again we run into an annoying delay here, and a situation that leaves the page looking broken until the request finishes and the template’s bindings get filled in.

For data that we know the app will need right away, we can avoid yet another request by including the data in the app module as a value service which can then be injected into the controller.

angular.module('demoApp', [])
.controller('demoAppController', function($scope, $injector) {
  if ($injector.has('customer')) {
    $scope.customer = $injector.get('customer');
  } else {
    $scope.customer = getCustomer(); // only make an API request if the value service isn't present
  }
});
<script type="text/javascript">
  angular.module('demoApp').value('customer', {name: 'Jane Smith'});
</script>

<div ng-app="demoApp" ng-controller="demoAppController">
  Customer Name: {{customer.name}}
</div>

For Angular on Rails, it is helpful to put a method in the model that returns JSON for only the subset of attributes that you want to expose to the client:

class Customer < ActiveRecord::Base
  def client_attributes_json
    attributes.slice(:id, :name).to_json
  end
end
<script type="text/javascript">
  angular.module('demoApp').value('customer', <%= customer.client_attributes_json %>);
</script>

Using these two techniques, we can make the initial page load of an Angular app feel nearly as responsive as server-generated pages.


Contact us for a complimentary 30 minute consultation.

get in touch