Skip to content

Instantly share code, notes, and snippets.

@alecperkins
Created August 8, 2012 21:12
Show Gist options
  • Save alecperkins/3298824 to your computer and use it in GitHub Desktop.
Save alecperkins/3298824 to your computer and use it in GitHub Desktop.
NamedView: Backbone View with automatic className
# Public: A Backbone View that automagically adds CSS classes based on its
# CoffeeScript class names, going right up the inheritence chain. This
# is cool because it's less crap to write, and plays well with CSS.
#
# Given:
#
# class Person extends NamedView
# class Wizard extends Person
# harry = new Wizard()
#
# `harry.el` will have a CSS class attribute of 'Person Wizard'.
#
# <div class="Person Wizard"></div>
#
# Note: a pattern like `class Person.Wizard extends NamedView` will result in
# only the CSS class 'Wizard' being added, since the classes are unaware of
# their namespacing.
#
# To opt-out of the naming (in case you want a subclass to not add the name),
# set the property `@mute_name` of the constructor to `true`. This opt-out will
# be carried through to any extending classes, so to opt back in to naming,
# set `@mute_name: false`.
#
# Continuing from above:
#
# class Voldemort extends Wizard
# @mute_name: true
# dark_lord = new Voldemort()
#
# `dark_lord.el`'s class will still only be 'Person Wizard':
#
# To 'solo' a class name, add the property `@solo_name:true` to the class.
#
# class Headmaster extends Wizard
# dumbledore = new Headmaster()
#
# `dumbledore.el`'s class will be 'Headmaster'. `@solo_name` propagates in the
# same way as `@mute_name`. To unsolo a descendant class, set `@solo_name: false`.
# The solo value can be set to a global default by setting:
#
# window.NamedView_solo_default = true;
#
# NamedViews work just like regular views in every other way, even supporting
# the normal className property.
class NamedView extends Backbone.View
constructor: (args...) ->
# Let the regular Backbone.View constructor do its thing.
super(args...)
# Use the @solo_name property of only this constructor to allow for
# overriding it in descendent classes.
solo_name = @constructor.solo_name
if window.NamedView_solo_default and not solo_name?
solo_name = true
# Recursively add the classes, going up the inheritence chain.
addParent = (parent_ctor) =>
# Stop if no parent or back at NamedView.
if parent_ctor? and parent_ctor.name isnt 'NamedView'
# Recurse all the way up the chain before adding the classes
# so that they are added in order, not that it really
# matters at all. (More of an OCD thing.)
if not solo_name
addParent(parent_ctor.__super__?.constructor)
if not parent_ctor.mute_name
@$el.addClass(parent_ctor.name)
# Start the recursion using this class's constructor.
addParent(@constructor)
class HerpDerp extends NamedView
className: 'some-class'
template: _.template """
This is some content.
<button class="foo-foo">Foo</button>
"""
render: ->
@$el.html(@template())
return @el
class BabyDerp extends HerpDerp
template: _.template """
<button class="bar-bar">Bar</button>
"""
class DivaDerp extends HerpDerp
@solo_name: true
template: _.template """
"""
class SilentDerp extends HerpDerp
@mute_name: true
template: _.template """
"""
my_view = new HerpDerp()
$('body').append(my_view.render())
other_view = new BabyDerp()
$('body').append(other_view.render())
diva_view = new DivaDerp()
$('body').append(diva_view.render())
unnamed_view = new SilentDerp()
$('body').append(unnamed_view.render())
# This results in the markup depicted in `resulting-els.html`.
<body>
<!-- The regular className attribute works like normal. -->
<div class="HerpDerp some-class">
This is some content.
<button class="foo-foo">Foo</button>
</div>
<!-- Descendant classes get the parent class names… -->
<div class="HerpDerp BabyDerp">
<button class="bar-bar">Bar</button>
</div>
<!-- …unless they are "soloed". -->
<div class="DivaDerp">
</div>
<!-- And descendants can exclude their own name. -->
<div class="HerpDerp">
</div>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment