building bulletproof views

176
* BUILDING BULLETPROOF VIEWS JOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial RAILS CONF 2011 BALTIMORE, MARYLAND

Upload: others

Post on 09-Feb-2022

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Building Bulletproof Views

* BUILDING BULLETPROOF VIEWSJOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial RAILS CONF 2011 BALTIMORE, MARYLAND

Page 2: Building Bulletproof Views

*

JOHNa designer(who also develops)

Page 3: Building Bulletproof Views

*

BRUCEa developer(who also designs)

Page 4: Building Bulletproof Views

WE WORK AT

Page 5: Building Bulletproof Views

WE ARE WRITING A BOOKfor

Page 6: Building Bulletproof Views

HOW MANY HERE ARE

DEVELOPERS??

Page 7: Building Bulletproof Views

HOW MANY HERE ARE

DESIGNERS??

Page 9: Building Bulletproof Views

AGENDA

HOUR 1

*

Intro, SurveysViews are ComplexMarkup RefresherBuilding a Layout

Questions

Page 10: Building Bulletproof Views

AGENDA

HOUR 2

*

The Art of Template WritingQuestions

Page 11: Building Bulletproof Views

AGENDA

HOUR 3

*

Nailing NavigationMaintainable FormsDon’t Fear the Object

Going MobilePackaging Assets

Page 12: Building Bulletproof Views

* 1.VIEWS ARECOMPLEX

Page 13: Building Bulletproof Views

Views are still...

THE WILD WEST

Page 14: Building Bulletproof Views

<ol class="narrow_playlist"><% playlist.listings.each_with_index do |listing, i| -%><% if i < show %>

<% composition = listing.composition %> <% position = i %>

<li class="playlist_item"> <table class="playlist_listing"> <tr> <td class="listing_rank_col"> <%= position+1 %> </td> <td class="listing_title_col"> <h6><%#= link_to awesome_truncate(composition.title,30), label_release_track_path(composition.label.slug, composition.release.slug), :title => composition.title %></h6> <h6><%= link_to awesome_truncate(composition.title,30), composition_path(composition), :title => composition.title %></h6> </td> <td class="listing_icon_col" rowspan="3"> <ul class="playlist_site_actions"> <li class="preview"> <a class="sm2_link" href="<%= composition.preview_url %>"> <span class="hide_me">Preview</span>&nbsp;&nbsp;&nbsp; </a> <%= link_to_function(image_tag("icon_speaker_preview.png", :alt => "Preview", :title => "Preview"), "") %> </li>

<% if !composition.member_contributed %> <li class="add_to_cart"> <% if composition.network? %> <%= link_to image_tag("button_playlist_download.gif", :alt => "Download"), downloads_path(:audio_item_id => composition.audio_items.first.id), :method => :post%> <% else %> <%= link_to(image_tag("cart.gif", :alt => "Add to cart", :title => "Add to cart"), selections_path(:salable_id => composition.id, :salable_type => composition.class.name), :method => :post) %> <% end -%> </li> <% end -%> </ul> </td> </tr> <tr> <td></td> <td><h6 class="artist"><%= linked_profiles(composition) %></h6></td> </tr> <tr> <td></td> <td> <ul class="playlist_social_actions"> <%= render :partial => "/ratings/rating_list", :locals => {:class_name => composition.class.superclass.name, :ratable => composition}%> <li> <%= link_to_function(image_tag("icon_load_in_player.png", :alt => "Add to Playlist",:title => "Add to Playlist"), "addCompositionToPlayerPlaylist(#{composition.id})") %> </li> </ul> </td> </tr> </table></li>

<% end %><% end -%></ol>

Page 15: Building Bulletproof Views

QUICKLY CLEANEDMuch simpler and could be better...

<ol class="latest_uploads">  <% playlist.listings.each_with_index do |listing, i| -%>    <% if i < show %>      <% composition = listing.composition %>      <% position = i %>      <li class="playlist_item">        <p class="track_title"><%= link_to awesome_truncate(composition.title,30), composition_path(composition), :title => composition.title %></p>        <p class="sm2_link"><a href="<%= composition.preview_url %>"><span class="hide_me">Preview</span>&nbsp;&nbsp;&nbsp;</a>        <% if composition.network? %>          <%= link_to image_tag("button_playlist_download.gif", :alt => "Download"), downloads_path(:audio_item_id => composition.audio_items.first.id), :method => :post%>        <% else %>          <%= link_to(image_tag("cart.gif", :alt => "Add to cart", :title => "Add to cart"), selections_path(:salable_id => composition.id, :salable_type => composition.class.name), :method => :post) %>        <% end -%></p>        <p class="artist_name"><%= linked_profiles(composition) %></p>          <ul class="playlist_social_actions">            <%= render :partial => "/ratings/rating_list", :locals => {:class_name => composition.class.superclass.name, :ratable => composition}%>            <li>              <%= link_to_function(image_tag("icon_load_in_player.png", :alt => "Add to Playlist",:title => "Add to Playlist"), "addCompositionToPlayerPlaylist(#{composition.id})") %>            </li>          </ul>      </li><% end %><% end -%></ol>

Page 16: Building Bulletproof Views

Views are still...

THE RED-HEADEDSTEPCHILD

Page 17: Building Bulletproof Views

MULTIPLE LANGUAGESMuch simpler and could be better...

➡ ERB/Ruby

➡ HTML

➡ CSS

➡ JavaScript/CoffeeScript

➡ Serialization formats

Page 18: Building Bulletproof Views

LACK OF FAMILIARITY & RULES

Page 19: Building Bulletproof Views

“This is really long. I should break it into partials.”

Page 20: Building Bulletproof Views

“This is really complex. I should make it a helper.”

Page 21: Building Bulletproof Views

http://www.flickr.com/photos/lastyearsgirl_/2882447041

DESIGNERISSUES

Page 22: Building Bulletproof Views

* 2.MARKUPREFRESHER

Page 23: Building Bulletproof Views

HOW MANY HERE

USE ANDUNDERSTANDHTML5?

?

Page 24: Building Bulletproof Views

WHICH VERSION?HTML5 vs HTML4 vs XHTML

Newest (under development) standard. More semantic elements and other technologies

HTML5

Pretty standard.HTML4

XML version of HTML4, most browsers didn’t respect itXHTML 1.0

Page 25: Building Bulletproof Views

DECLARE A DOCTYPEAnd then develop to it

HTML 4.01 StrictHTML 4.01 Transitional

HTML 4.01 FramesetXHTML 1.0 Strict

XHTML 1.0 TransitionalXHTML 1.0 Frameset

XHTML 1.1HTML (5)

www.alistapart.com/articles/doctype/

Page 26: Building Bulletproof Views

XHTML 4 & HTML 4HAVE THESE ELEMENTS

html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, acronym, address, big, cite, code,

del, dfn, em, font, img, ins, kbd, q, s, samp,small, strike, strong, sub, sup, tt, var,

dl, dt, dd, ol, ul, li,fieldset, form, label, legend,

table, caption, tbody, tfoot, thead, tr, th, td

Page 27: Building Bulletproof Views

HTML5HAS SOME NEW SEMANTIC TOYS

section, article, header, hgroup, nav,footer, aside, time, figure, figcaption

Legacy support requires Modernizr or a javascript shiv.

and removes things like frameset and consolidatessome other elements together (acronym/abbr)

Page 28: Building Bulletproof Views

HIERARCHYWhere it’s at.

Page 29: Building Bulletproof Views

<div class="headline">This is a page headline.</div><div class="subhead">This is a subhead</div><div class="body">This is body text and it goes on for miles and miles. I like cheese.</div><div class="list">This is going to be a list of items:<br />- Item 1<br />- Item 2<br />- Item 3<br /></div>

NO HIERARCHYNot semantic, everything is the same.

Page 30: Building Bulletproof Views

NO HIERARCHYNot semantic, everything is the same.

Page 31: Building Bulletproof Views

<hgroup> <h1>This is a page headline.</h1> <h2>This is a subhead</h2></hgroup><section id=”page”> <p>This is body text and it goes on for miles and miles. I like cheese.</p> <p>This is going to be a list of items:</p> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul></section>

“SEMANTIC”HTML Tags used for meaning

Page 32: Building Bulletproof Views

“SEMANTIC”HTML Tags used for meaning

Page 33: Building Bulletproof Views

USE THE ELEMENTS!Lists are lists. Not divs with bullets.

<% @unit.unit_client_scans.each do |client_scan| %>&nbsp;• <%= client_scan.value %><br /><% end %>

<ul class=”scans”><% @unit.unit_client_scans.each do |client_scan| %> <li><%= client_scan.value %></li><% end %></ul>

Page 34: Building Bulletproof Views

!TIP:

Mark up content for the meaning, NOT the layout. Use the semantic elements.

Page 36: Building Bulletproof Views

STYLECSS Overview and Legacy Hacks

http://www.flickr.com/photos/prettycooljewels/4351797285/

Page 37: Building Bulletproof Views

<div class="b"> <div class="l"> <div class="r"> <div class="bl"> <div class="br"> <div class="tl"> <div class="tr box"> <%= content %> </div> </div> </div> </div> </div> </div> </div>

ROUNDED CORNERSVintage like...

Page 38: Building Bulletproof Views

ROUNDED CORNERS<div class=”box-to-be-rounded”> <%= content %></div>

CSS3 Solution

.box-to-be-rounded { border: 1px solid #ccc; -webkit-border-radius: 5px; /* Safari, Chrome */ -moz-border-radius: 5px; /* Firefox */ border-radius: 5px; /* IE9, Opera 10.5 */}

Page 39: Building Bulletproof Views

REQUIRED FIELDS

<%= f.label :first_name %> <span class=”required”>*</span><br /><%= f.text_field :first_name %>

Love the form you’re with

span.required {color: red;}

Page 40: Building Bulletproof Views

REQUIRED FIELDS<%= f.label :first_name, :class => “required” %><%= f.text_field :first_name %>

Progressively enhance!

label { display: block; }

label.required { color: red; }

label.required:after { content: “*”;}

Not supported in MSIE 7 and below, 8 does not accept images for content

www.quirksmode.org/css/beforeafter.html

Page 41: Building Bulletproof Views

DON’T FIGHT THE BROWSEREmbrace it!

html { overflow-y: scroll;}

Page 42: Building Bulletproof Views

ID vs. CLASSThere should be a standard

#this_is_an_id

.this-is-a-class

Page 43: Building Bulletproof Views

!TIP:

Embrace the browser and its flaws. Design to the power of the medium. (SCROLLING IS NOT A FLAW)

Page 44: Building Bulletproof Views

SHORTENINGLearn to write concise css

http://www.flickr.com/photos/tomsaint/3456155628/

Page 45: Building Bulletproof Views

CSS SHORTHANDCombine pieces together

margin-top: 4px;margin-right: 2px;margin-bottom: 4px;margin-left: 8px;

becomes...

margin: 4px 2px 4px 8px;

application.css

Page 46: Building Bulletproof Views

CSS SHORTHANDCombine pieces together

background-color: #333;background-image: url(“../images/texture.png”)background-position: top left;background-repeat: repeat;

becomes...

background: #333 url(“../images/texture.png”) repeat-x top left;

application.css

Page 47: Building Bulletproof Views

CSS SHORTHANDCombine pieces together

fontbackgroundmarginborderpaddinglist

CAN BE USED FOR:

Reduce stylesheet size and improve readability.

AND WILL HELP YOU

Page 48: Building Bulletproof Views

HOW TO WRITE CSS.button-install { margin-left: 30px; font-family: "Helvetica Neue", helvetica, arial, sans-serif; display: block; width: 250px; margin-bottom: 15px; text-align: left; height: 48px; font-size: 20px; font-weight: bold; padding:4px 0 8px 0; background-color: #67c5e6; background-image: -webkit-gradient( linear, left top, left bottom, color-stop(0.23, rgb(103,197,230)), color-stop(0.81, rgb(94,149,167)) ); background-image: -moz-linear-gradient( center top, rgb(103,197,230) 23%, rgb(94,149,167) 81% ); border-radius: 8px; -webkit-border-radius: 8px; -moz-border-radius: 8px;}

Hard to read and find what you’re looking for...

Page 49: Building Bulletproof Views

HOW TO WRITE CSS.button-install { background-color: #67c5e6; background-image: -webkit-gradient( linear, left top, left bottom, color-stop(0.23, rgb(103,197,230)), color-stop(0.81, rgb(94,149,167)) ); background-image: -moz-linear-gradient( center top, rgb(103,197,230) 23%, rgb(94,149,167) 81% ); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; display: block; font: bold 20px "Helvetica Neue", helvetica, arial, sans-serif; height: 48px; margin: 0 0 15px 30px; padding:4px 0 8px 0; text-align: left; width: 250px;}

Alpha order your properties, use shorthand, one per line

Page 50: Building Bulletproof Views

!TIP:

Write effective and concise CSS. Properties in alpha order, be specific, browser experimental calls before standard calls, and don’t use browser hacks.

Page 51: Building Bulletproof Views

BUT GUYS,

WHY DOESTHIS MATTER?

?

Page 52: Building Bulletproof Views

WEB ACCESSIBILITYTake care of your users

Page 53: Building Bulletproof Views

GOOGLE IS A BLIND USER

Page 55: Building Bulletproof Views

START SMALLCover the basics all the time.

www.w3.org/TR/WCAG10/full-checklist.html

Page 56: Building Bulletproof Views

WEB ACCESSIBILITYERB Examples

link_to @product.name, product_path(@product), :title => “Take a look at #{@product.name}”

image_tag “product_12758.png”, :alt => “#{@product.name}”

link_to (image_tag “product_12758.png”, :alt => “#{@product.name}”), product_path(@product), :title => “Take a look at #{@product.name}”

Page 57: Building Bulletproof Views

WEB ACCESSIBILITY=Well Formed HTML

Not just an afterthought.

Page 58: Building Bulletproof Views

!TIP:

HTML is code. Don’t be afraid to write it. Well written HTML helps users of all types access and interact with our content or application.

Page 59: Building Bulletproof Views

* 3.BUILDINGA LAYOUT

Page 60: Building Bulletproof Views

A STORY OF A LAYOUTDefault

<!DOCTYPE html><html> <head> <title></title> <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </head> <body> <%= yield %> </body></html>

application.html.erb

Page 61: Building Bulletproof Views

CHARSETCombine pieces together

<!DOCTYPE html><html> <head> <meta charset=”utf-8”> <title></title> <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </head> <body> <%= yield %> </body></html>

application.html.erb

Page 62: Building Bulletproof Views

TARGET MSIEBy version

<!DOCTYPE html><!--[if lt IE 7 ]> <html lang="en" class="ie ie6"> <![endif]--><!--[if IE 7 ]> <html lang="en" class="ie ie7"> <![endif]--><!--[if IE 8 ]> <html lang="en" class="ie ie8"> <![endif]--><!--[if IE 9 ]> <html lang="en" class="ie ie9"> <![endif]--><!--[if (gt IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]-->

Extracted into a gem by Bruce

http://codefluency.com/post/1100393830/ie-conditional-tag-plugin

<%= ie_conditional_tag :html %>

application.html.erb

Page 63: Building Bulletproof Views

TARGET MSIEBy version

section#page { margin: 0 auto; width: 960px;}

.ie7 section#page { overflow: hidden;}

http://codefluency.com/post/1100393830/ie-conditional-tag-plugin

application.css

Page 64: Building Bulletproof Views

RECOGNIZE NEW ELEMENTSHTML5 Shiv (or Shim, same thing)

<!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->

Enables the elements and fixes printing on IE

code.google.com/p/html5shiv/

Page 67: Building Bulletproof Views

RESET.CSSGet your browser on the same page

html5doctor.com/html-5-reset-stylesheet/

tml, body, div, span, object, iframe,

h1, h2, h3, h4, h5, h6, p, blockquote, pre,

abbr, address, cite, code,

del, dfn, em, img, ins, kbd, q, samp,

small, strong, sub, sup, var,

b, i,

dl, dt, dd, ol, ul, li,

fieldset, form, label, legend,

table, caption, tbody, tfoot, thead, tr, th, td,

article, aside, canvas, details, figcaption, figure,

footer, header, hgroup, menu, nav, section, summary,

time, mark, audio, video {

    margin:0;

    padding:0;

    border:0;

    outline:0;

    font-size:100%;

    vertical-align:baseline;

    background:transparent;

}

....etc...

reset.css

Page 68: Building Bulletproof Views

CALLING STYLESHEETSPitfalls of :all

<%= stylesheet_link_tag :all %>

Can load things in alpha order, meaning reset happens after

Xapplication.html.erb

Page 69: Building Bulletproof Views

CALLING STYLESHEETSManually calling them

<%= stylesheet_link_tag ‘reset’, ‘application’, ‘buttons’ %>

application.html.erb

Page 70: Building Bulletproof Views

CALLING STYLESHEETSDefining :defaults

class Application < Rails::Application config.action_view.stylesheet_expansions[:defaults] = %w( nav tipsy iconic ui-lightness/jquery-ui-1.8.11.custom jquery.customSelect dashboard shadows dataTables/css/demo_table dataTables/css/demo_page application )end

config/application.rb

Page 71: Building Bulletproof Views

FRAMEWORKS/BOILERPLATECombine pieces together

html5boilerplate.com

Page 72: Building Bulletproof Views

Build Your Own

Framework

BUILD YOUROWN

FRAMEWORK

http

://www.flick

r.com/p

hoto

s/dysto

pos/3

15312

589/

Page 73: Building Bulletproof Views

HIERARCHY OF H TAGSYou can reuse the H1. Really.

html|-body| |-header| | |-h1| |-article| | |-h1| | |-section| | | |-header| | | | |-h1| | | |-p| | | |-h2

Page 74: Building Bulletproof Views

QUESTIONSAND ANSWERS

Page 75: Building Bulletproof Views

AGENDA

HOUR 2

*

The Art of Template WritingQuestions

Page 76: Building Bulletproof Views

*

WELCOMEBACK!

Page 77: Building Bulletproof Views

* 4.THE ART OF TEMPLATEWRITING

Page 78: Building Bulletproof Views

IMPROVINGREADABILITY

http://www.flickr.com/photos/bluefootedbooby/389279217/

Page 79: Building Bulletproof Views

WHY WE REFACTOR

➡ Templates can be verbose

➡ Display logic can be very complex

➡ More people need to read, understand, and modify them.

➡ They need to change more frequently, as they’re more subjective

Page 80: Building Bulletproof Views

FORMATTING

<html> <head> <title>...

Don’t indent with hard tabs

Page 81: Building Bulletproof Views

FORMATTING<% pets.each do |pet| %><li> <%= pet.name %></li><% end %>

Indent

Better:

<% pets.each do |pet| %> <li> <%= pet.name %> </li><% end %>

Page 82: Building Bulletproof Views

FORMATTING

<p> We believe that many dogs are unnecessarily abandoned because of easily solved, unwanted behavior. To combat this, we offer behavior classes to <strong>all<strong> adopters, and all individuals requiring help with their dogs.</p>

Clean up big blocks of text

Still try to keep line length <= 80 characters per line

Page 83: Building Bulletproof Views

TIDYBecause templating code gets messy.

http://www.flickr.com/photos/muehlinghaus/3564021462/

Page 84: Building Bulletproof Views

FORMATTING

$ gem install rack-tidy$ gem install rack-tidy-ffi

Output formatting isn’t your job

Use one of these (or similar) as middleware

Page 86: Building Bulletproof Views

Zen CodingUse a tool, not a generator

div#page>img.logo+ul#navigation>li*5>a

<div id="page"> <img src=”” class="logo" /> <ul id="navigation">        <li><a href=""></a></li>        <li><a href=""></a></li>        <li><a href=""></a></li>        <li><a href=""></a></li>        <li><a href=""></a></li>    </ul></div>

PROJECT FILES:code.google.com/p/zen-coding/

Page 87: Building Bulletproof Views

!TIP:

Know your team. YTMV

Page 88: Building Bulletproof Views

TOOLSWhat do we use for each situation?

http://www.flickr.com/photos/anotherphotograph/3571242832/

Page 89: Building Bulletproof Views

REFACTORING TOOLS

Extract part of a template (usually for reuse)PARTIALS

Extract complex logicHELPERS

Extract complex, wrap content, View DSLsBLOCK HELPERS

Reduce helper spaghetti code, encapsulate state, ease testingVIEW CLASSES

Page 90: Building Bulletproof Views

QUERYING, ORDERING, LIMITING

<% Pet.all.order(:adoption_date).each do |pet| %> <%# Use pet %><% end %>

Don’t do this.

X

Page 91: Building Bulletproof Views

QUERYING, ORDERING, LIMITING

def index @pets_by_adoption = Pet.all.order(:adoption_date)end

Instead, set an instance variable

pets_controller.rb

<% @pets_by_adoption.each do |pet| %> <%# Use pet %><% end %>

_adoption_list.html.erb

Page 92: Building Bulletproof Views

QUERYING, ORDERING, LIMITING

def pets_by_adoption @pets_by_adoption = Pet.all.order(:adoption_date)end

Or instead, use a helper

pets_helper.rb

<% pets_by_adoption.each do |pet| %> <%# Use pet %><% end %>

_adoption_list.html.erb

Page 93: Building Bulletproof Views

!TIP:

Avoid capital letters in your template code (configuration constants and model classes).

Page 94: Building Bulletproof Views

LOCAL ASSIGNMENT

<% number_available = Pet.available.count %><% available, unavailable = pets_by_status %><% next_month = Date.today >> 1 %>

Don’t do this, ever. Never. At no time...

Messy, noisy, undocumentable, easy to accidentally remove

X_availability.html.erb

Page 95: Building Bulletproof Views

LOCAL ASSIGNMENT

def index # ... other stuff @number_available = Pet.available.countend

Instead, use an instance varaible

pets_controller.rb

Page 96: Building Bulletproof Views

LOCAL ASSIGNMENT

def number_available @number_available ||= Pet.available.countend

Or instead, use a helper

pets_helper.rb

Page 97: Building Bulletproof Views

CONDITIONAL CONTENT

<% if @pet.at_risk? <%# Lots of content %><% else %> <%# Different lots of content %><% end %>

Avoid long if/else clauses

If lengthy, this makes it hard to get a quickfeeling of what the page contains.

X

Page 98: Building Bulletproof Views

CONDITIONAL CONTENT

def risk_assessment if @pet.at_risk? render partial: 'at_risk' else render partial: 'not_at_risk' endend

Better, split to files

pets_helper.rb

<%= risk_assesment %>

show.html.erb

Page 99: Building Bulletproof Views

CONDITIONAL CONTENT

<% if current_user.admin? || current_user.can?(:edit_pet) || pet.owner == current_user %> <%= render partial: 'form', locals: {pet: pet} %><% end %>

Avoid long and complex conditions

If lengthy, this makes it hard to get a quickfeeling of what the page contains.

X

Page 100: Building Bulletproof Views

CONDITIONAL CONTENT

<% if can_edit_pet?(pet) %> <%= render partial: 'form', locals: {pet: pet}<% end %>

Better, extract condition toa meaningful helper

_pet.html.erb

Page 101: Building Bulletproof Views

MANUAL LOOPS

<ul id='pets'> <% @pets.each do |pet| %> <li> <%= image_tag pet.image.url(:thumb) %> <%= pet.name %> <%= pet_stats(pet) %> <li> <% end %></ul>

Minimize theseindex.html.erb

X

Page 102: Building Bulletproof Views

MANUAL LOOPS

<ul id='pets'> <%= render @pets %></ul>

Break into partials

index.html.erb

<li> <%= image_tag pet.image.url(:thumb) %> <%= pet.name %> <%= pet_stats(pet) %><li>

_pet.html.erb

Page 103: Building Bulletproof Views

http://www.flickr.com

/photos/lambdacz/4

725437313

RENDERING(There are many ways to do it.)

Page 104: Building Bulletproof Views

RENDERING

render partial: 'pet'

Without an object

Renders #{controller_name}/_pet.html.erb

render partial: 'pets/pet'

Always renders pets/_pet.html.erb

Page 108: Building Bulletproof Views

RENDERING

<% @pets.each do |pet| %> <%= render pet %><% end %>

With a collection of objects

With a manual loop:

Page 109: Building Bulletproof Views

RENDERING

render partial: 'pet', collection: @pets

With a collection of objects

Using :collection

Renders _pet.html.erb for each object in @pets,assigning each as the pet local variable

Page 112: Building Bulletproof Views

!TIP:

Using implicit partial rendering means you don’t have to CARE which partial gets rendered.

Page 113: Building Bulletproof Views

SETTING THE PAGE TITLEThe Instance Variable Method

Xdogs_controller.rb

def show @dog = Dog.find(params[:id]) @page_title = @dog.nameend

<section id='content'> <header id=‘page_header’> <h1><%= @page_title %></h1> </header> <%= yield %></section>

application.html.erb

Page 114: Building Bulletproof Views

SETTING THE PAGE TITLE

<% content_for :page_header do %> <h1><%= @pet.name %></h1><% end %>

show.html.erb

<section id='content'> <% if content_for?(:page_header) %> <header id=‘page_header’><%= yield :page_header %></header> <% end %> <%= yield %></section>

application.html.erb

The content_for Method

Page 115: Building Bulletproof Views

SETTING THE PAGE TITLE

def page_title(title) content_for(:page_header, content_tag(:h1, title))end

pets_helper.rb

<% page_title @pet.name %>

show.html.erb

The content_for Method

Page 116: Building Bulletproof Views

!TIP:

Use content_for in your action templates to define content that is conceptually related to the action, but structurally located elsewhere in the layout.

Page 117: Building Bulletproof Views

BLOCK HELPERS

http://www.flickr.com

/photos/icco/5237319

440/

Page 118: Building Bulletproof Views

!TIP:

Don’t code generate before you have determined what your common design elements are.

Page 119: Building Bulletproof Views

QUESTIONSAND ANSWERS

Page 120: Building Bulletproof Views

AGENDA

HOUR 3

*

NavigationForming Forms

Don’t Fear the ObjectGoing Mobile

Packaging Assets

Page 121: Building Bulletproof Views

*

WELCOMEBACK!

Page 122: Building Bulletproof Views

* 5.NAILINGNAVIGATION

Page 123: Building Bulletproof Views

WHAT NAVIGATION DOES

➡ Tells users where they can go

➡ Reminds them where they are

Page 124: Building Bulletproof Views

WHERE ARE WE?The Instance Variable Method

Xdogs_controller.rb

def show @dog = Dog.find(params[:id]) @current_tab = :dogsend

<ul> <li class=’<%= nav_class(:dogs) %>’> <%= link_to ‘Dogs’, dogs_path %> </li> ...</ul>

def nav_class(tab) ‘active’ if tab == @current_tabend

application_helper.rb

_nav.html.erb

Page 125: Building Bulletproof Views

WHERE ARE WE?The Manual Method

X_nav.html.erb

<ul> <li> <%= link_to_unless_current("Home", action: ‘home’, controller: ‘pages’) %> </li> ...</ul>

Page 126: Building Bulletproof Views

WHERE ARE WE?The CSS Method

Xapplication.html.erb

<body class="<%= controller_name %> <%= action_name %>">

nav.css

body.dogs li#dogs_tab { background: #fff;}

body.dogs li#dogs_tab a { color: #000;}

_nav.html.erb<ul> <li id=‘dogs_tab’> <%= link_to("Dogs", dogs_path) %> </li> ...</ul>

nav.css

Page 127: Building Bulletproof Views

IT’S THE ACTION TEMPLATE’S

RESPONSIBILITY.

Page 129: Building Bulletproof Views

USING GOOSE

<% content_for :main_nav do %> <nav id='main-nav'> <ul> <%= nav_to 'Home', root_path %> <%= nav_to 'Dogs', dogs_path %> <%= nav_to 'Cats', dogs_path %> <%= nav_to 'Contact Us', about_path %> </ul> </nav><% end %>

nav/_main.html.erbHooking it in

<header> <%= yield :main_nav %></header>

application.html.erb

Page 130: Building Bulletproof Views

USING GOOSE

nav.css

Styling

nav#main_nav ul { height: 30px; overflow: hidden;}

nav#main_nav ul li { color: #999; float: left; margin-right: 3px; padding: 5px 10px;}

nav#main_nav ul li.active { color: #000; font-weight: bold;}

Page 131: Building Bulletproof Views

USING GOOSE

show.html.erb

In Action Templates

<% nav_at ‘Dogs’ %>

Page 132: Building Bulletproof Views

!TIP:

Driving the state of the page from the action template gives you flexibility and means you know exactly where to look to make changes.

Page 133: Building Bulletproof Views

* 6.MAINTAINABLEFORMS

Page 134: Building Bulletproof Views

FORM_FOR vs FORM_TAG

Page 135: Building Bulletproof Views

SHOW ME!

Page 137: Building Bulletproof Views

Search

HTML5 FORM ELEMENTS<input type=”search” name=”search”>

diveintohtml5.org/forms.html

Page 139: Building Bulletproof Views

FORMTASTIC

<%= form_for @adoption_application do |f| %> <fieldset> <legend>Your Name</legend> <ol> <li> <%= f.label :first_name, "First Name" %> <%= f.text_field :first_name %> </li> <li> <%= f.label :last_name, "Last Name" %> <%= f.text_field :last_name %> </li> </ol> </fieldset><% end %>

Before...

Page 140: Building Bulletproof Views

FORMTASTIC

<%= semantic_form_for @adoption_application do |f| %> <%= f.inputs “Your Name” do %> <%= f.input :first_name, label: ‘First Name’ %> <%= f.input :last_name, label: ‘Last Name’ %> <% end %> ...<% end %>

After

Page 141: Building Bulletproof Views

* 6.DON’T FEARTHE OBJECT

Page 142: Building Bulletproof Views

YOU MAKE CLASSES FOR THE MODEL

LAYER

Page 143: Building Bulletproof Views

YOU MAKE CLASSES FOR THE

CONTROLLER LAYER

Page 144: Building Bulletproof Views

WHAT ABOUT THE VIEW LAYER?

Page 145: Building Bulletproof Views

ENCAPSULATION

http://www.flickr.com

/photos/shuffle-art/28102475

09/

Page 146: Building Bulletproof Views

HELPER CLASSEncapsulates state, accesses template

class PetHistory

def initialize(template, pet) @template = template @pet = pet end

# ...

def to_s # @template.render, etc end

end

def history @history ||= PetHistory.new(self, @pet)end

lib/pet_history.rb

pets_helper.rb

Page 147: Building Bulletproof Views

HYBRIDController -> View

class PetSort delegate :params, to: :@controller delegate :each, to: :records

def initializer(controller) @controller = controller end

def field params[:field] || :name end

def order params[:order] || :desc end

private

def records # TODO: make sure field & order are on whitelist Pet.order("#{field} #{order}") end end

lib/pet_sort.rb

Page 148: Building Bulletproof Views

HYBRIDController -> View

def sort @sort ||= PetSort.new(self)endhelper_method :sort

pets_controller.rb

Page 149: Building Bulletproof Views

!TIP:

View classes allow you to more easily test complex logic (instantiate, stub out the controller, template, etc).

Page 150: Building Bulletproof Views

* 8.GOINGMOBILE

Page 151: Building Bulletproof Views

MOBILE USERINTERACTION

http://www.flickr.com

/photos/yourdon/359975

3183/

Page 152: Building Bulletproof Views

MOBILE USER INTERACTION

➡ Task focused

➡ What are they trying to do?

➡ Fat-fingering (easy targets)

➡ Sometimes low bandwidth

Page 153: Building Bulletproof Views

CSS3 MEDIA QUERIES

Page 154: Building Bulletproof Views

Client-side smart rendering via conditional CSS

RESPONSIVE DESIGN

www.alistapart.com/articles/responsive-web-design

Page 155: Building Bulletproof Views

Client-side smart rendering via conditional CSS

RESPONSIVE DESIGN

@media screen and (max-width: 800px) { }

@media screen and (max-width: 640px) { }

@media only screen and (min-device-width: 320px) and (max-device-width: 480px) { }

If lengthy, this makes it hard to get a quickfeeling of what the page contains.

application.css

Page 157: Building Bulletproof Views

@media QUERIES PITFALLS

➡ All content is loaded, even if it’s not shown

➡ Interaction is different on mobile

➡ Doesn’t get into the capabilities of the mobile device

Page 158: Building Bulletproof Views

MOBILE SPECIFIC

TEMPLATEShttp://www.flickr.com

/photos/goincase/49738

479

49/

Page 159: Building Bulletproof Views

Is it a mobile request?

MOBILE-SPECIFIC TEMPLATES

def mobile_request? request.user_agent =~ /iP(?:one|ad|od)/endhelper_method :mobile_request?

application_controller.rb

Page 160: Building Bulletproof Views

SET A MIME TYPE

Mime::Type.register_alias "text/html", :mobile

mime_types.rb

Page 161: Building Bulletproof Views

FILTER THE REQUEST

before_filter :prepare_mobile_request!

def prepare_mobile_request! if mobile_request? request.format = :mobile endend

application_controller.rb

Page 162: Building Bulletproof Views

Specific to mobile

LAYOUT FILE

<!DOCTYPE html> <html lang="en"> <head> <title>Dam App: Mobile Edition!</title> </head> <body> <%= yield %> <%= render partial: 'layouts/footer' %> </body> </html>

application.mobile.erb

Page 163: Building Bulletproof Views

LET USERS DECIDE

before_filter :set_preferred_view! before_filter :prepare_mobile_request!

def set_preferred_view! if mobile_request? case params[:prefer_view] when 'standard' session[:preferred_view] = :standard when 'mobile' session[:preferred_view] = :mobile end endend

application_controller.rb

Page 164: Building Bulletproof Views

LET USERS DECIDEdef prepare_mobile_request! if mobile_request? && preferred_view == :mobile request.format = :mobile endend

def preferred_view if mobile_request? session[:preferred_view] || :mobile else :standard endend

helper_method :preferred_view

application_controller.rb

Page 165: Building Bulletproof Views

LET USERS DECIDE

def link_to_prefer_view(name) link_to_unless(preferred_view == name, "#{name.capitalize} View", prefer_view: name)end

application_helper.rb

<% if mobile_request? %> <%= link_to_prefer_view :mobile %> | <%= link_to_prefer_view :standard %><% end %>

_footer.html.erb

Page 167: Building Bulletproof Views
Page 168: Building Bulletproof Views

!TIP:

Figure out what your users need and then build to that. The technology follows the use case.

Page 169: Building Bulletproof Views

* 9.PACKAGINGASSETS

Page 170: Building Bulletproof Views

WHY DOES IT LOAD SLOW?YSlow and PageSpeed

Page 171: Building Bulletproof Views

UNUSED SELECTORSUI side: Dust Me Selectors

sitepoint.com/dustmeselectors

Page 172: Building Bulletproof Views

UNUSED SELECTORSGem: Deadweight

# lib/tasks/deadweight.rake

require 'deadweight'

Deadweight::RakeTask.new do |dw| dw.mechanize = true

dw.root = 'http://staging.example.com'

dw.stylesheets = %w( /stylesheets/style.css )

dw.pages = %w( / /page/1 /about )

dw.pages << proc { fetch('/login') form = agent.page.forms.first form.username = 'username' form.password = 'password' agent.submit(form) fetch('/secret-page') }

dw.ignore_selectors = /hover|lightbox|superimposed_kittens/ end

www.github.com/aanand/deadweight

Page 173: Building Bulletproof Views

A MORE ROBUST METHODthat covers javascript too...

“Jammit is an industrial strength asset packaging library for Rails, providing both the CSS and JavaScript concatenation and compression that you'd expect,

as well as YUI Compressor and Closure Compiler compatibility, ahead-of-time gzipping, built-in JavaScript template support, and optional Data-URI /

MHTML image and font embedding.”

documentcloud.github.com/jammit

Page 175: Building Bulletproof Views

FINAL Q&AFIRE WHEN READY.