Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
A simple, made-up example of the code for a simple AngularJS blog viewer as a more detailed exploration of http://benhollis.net/blog/2014/01/17/cleanly-declaring-angularjs-services-with-coffeescript/ . Yes, I know about `$resource`, but I prefer not to use it.
app = angular.module 'BlogExample', []
# Simple controller that loads all blog posts
app.controller 'BlogCtrl', ['$scope', 'Blog', ($scope, Blog) ->
# Get all the blog posts
Blog.all().then (posts) ->
$scope.posts = posts
# Extend the $scope with our own properties, all in one big block
# I like this because it looks like declaring a class.
angular.extend $scope,
posts: []
# A new, prototype post that will be bound to the "new post" form.
newPost: Blog.newPost()
# When the "save post" button is clicked:
addPost: ->
$scope.newPost.save().then ->
@posts.push $scope.newPost
$scope.newPost = Blog.newPost()
]
# The Blog service provides access to Posts. We immediately "new" the class
# to provide a single instance of the Blog class to the injector.
app.factory 'Blog', ['$http', 'Post', ($http, Post) ->
new class Blog
# Get all blog posts
all: ->
# Assume a response like:
# { "posts": [ { "title": "Hello World", "author": "Ben Hollis", "body": "This is an example." } ] }
$http.get('/posts').then (result) ->
new Post(post.title, post.author, post.body, true) for post in result.data.posts
# Create a new, empty blog post
newPost: ->
new Post('', '', '')
]
# This makes the Post class available for injection, rather than an instance of Post. Thus, it doesn't call "new".
#
# Post represents a single blog post.
app.factory 'Post', [ '$http', ($http) ->
class Post
# Title, Author, and Body are self-explanatory
# Persisted tells whether this instance has been saved.
constructor: (@title, @author, @body, @persisted = false) ->
# Save this post to the server.
save: ->
$http.post('/posts', title: @title, author: @author, body: @body).then =>
@persisted = true
]

This works nice for controllers

 class BlogCtrl
    constructor: (@scope, @Blog) ->
      @Blog.all().then (posts) =>
        @scope.posts = posts
      angular.extend @scope, @

      newPost: => @Blog.newPost()
app.controller 'DemoCtrl', ['$scope', 'Blog', DemoCtrl]

@hooptie45 that pollute your global namespace, but you can do:

 class app.BlogCtrl
    constructor: (@scope, @Blog) ->
      @Blog.all().then (posts) =>
        @scope.posts = posts
      angular.extend @scope, @

      newPost: => @Blog.newPost()
app.controller 'DemoCtrl', ['$scope', 'Blog', app.BlogCtrl]

kmaraz commented Dec 14, 2014

How will you Unit test your file blog.js.coffee? With Jasmine for example.

bra1n commented Jan 16, 2015

I really like the idea of using Coffee classes in Services, however the way you propose it:

app.factory 'Blog', ['$http', 'Post', ($http, Post) ->
  new class Blog
    # Get all blog posts
    all: ->
      # Assume a response like:
      # { "posts": [ { "title": "Hello World", "author": "Ben Hollis", "body": "This is an example." } ] }
      $http.get('/posts').then (result) ->
        new Post(post.title, post.author, post.body, true) for post in result.data.posts

    # Create a new, empty blog post
    newPost: ->
      new Post('', '', '')
]

would be no different than if you simply left out line 27:

app.factory 'Blog', ['$http', 'Post', ($http, Post) ->
  # Get all blog posts
  all: ->
    # Assume a response like:
    # { "posts": [ { "title": "Hello World", "author": "Ben Hollis", "body": "This is an example." } ] }
    $http.get('/posts').then (result) ->
      new Post(post.title, post.author, post.body, true) for post in result.data.posts

  # Create a new, empty blog post
  newPost: ->
    new Post('', '', '')
]

Now if I am able to leave out a line without it affecting the code at all, I can't really see why I would use a class there in the first place. "Just for the sake of using a class" isn't really a good enough reason for me. :/

Owner

bhollis commented May 9, 2015

@bra1n I wrote a whole blog post about my reasoning for this pattern. It's linked from this gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment