Create a gist now

Instantly share code, notes, and snippets.

Embed
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
]
@hooptie45

This comment has been minimized.

Show comment
Hide comment
@hooptie45

hooptie45 Oct 17, 2014

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]

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]
@Spadavecchia

This comment has been minimized.

Show comment
Hide comment
@Spadavecchia

Spadavecchia Nov 22, 2014

@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]

@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

This comment has been minimized.

Show comment
Hide comment
@kmaraz

kmaraz Dec 14, 2014

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

kmaraz commented Dec 14, 2014

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

@bra1n

This comment has been minimized.

Show comment
Hide comment
@bra1n

bra1n 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. :/

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. :/

@bhollis

This comment has been minimized.

Show comment
Hide comment
@bhollis

bhollis May 9, 2015

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

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