"creeping scope" into coffeescript

135 views
Skip to first unread message

Nate Kidwell

unread,
Mar 18, 2012, 3:47:04 PM3/18/12
to AngularJS
tldr; I love coffeescript and angular. Looking for feedback on a way
of I'm putting them together using 1.0.rc (with some help from
underscore).

Because my posts always get misformatted I put the code at:
https://gist.github.com/2079970

-------------------------
Okay, so imagine I want to do this:

<div ng:controller="MyController">{{message}}</div>

It looks like it the most recent version of angular, the controller
classes need to explicitly 'scope' their fields to an injected
$scope. So where I used to be able to get away with

class MyController
constructor:->
@message = "Hello World"

Now my class becames:

class MyController
constructor:($scope)->
$scope.message = "Hello World"

I hate typing, so I wrote a function "adaptForAngular" (at bottom)
which takes a class, instantiates it and basically wraps its
properties around the $scope. This function writes a new constructor
into global scope (of the same name as the class) so if you do

adaptForAngular(MyController)

you can then write the controller without the $scope reference, and
use the classname in the ng-controller attribute. Also I can pass in
injections as additional arguments (also a $scope injection
automatically gets pushed in to the injections). If you do

adaptForAngular EditorController, '$http'

it will then call an initializeInjections function on your original
class (I use this instead of a constructor) and pass in to there any
injected arguments (except for scope, which is back to "this" again).

By no longer explictly using scope, TDD is simpler in some cases, plus
your controllers can then be used outside of angular projects.

Thoughts?

Main function below
--------------------

adaptForAngular = (clazz, injections...)->
injections.push "$scope"
@[clazz.name] = ->
args = _.toArray(arguments)
scope = args.pop()
controller = new clazz()
_.extend scope, controller
scope.initializeInjections? args...
@[clazz.name].$inject = injections

Nate Kidwell

unread,
Mar 18, 2012, 3:59:15 PM3/18/12
to AngularJS
oh, and w/o underscore thanks to angular.extend :)

adaptForAngular = (clazz, injections...)->
injections.push "$scope"
@[clazz.name] = ->
args = [].splice.call(arguments,0)
scope = args.pop()
controller = new clazz()
angular.extend scope, controller

Peter Bacon Darwin

unread,
Mar 18, 2012, 4:24:11 PM3/18/12
to ang...@googlegroups.com
I also am loving using Coffee script with Angular.  One of the nice things about separating the scope from the controller is that we can control what is allowed into the scope.  For instance, not all the helper methods and services that the controller needs should be published to the scope.

For many of my Controllers, I am starting to lean toward the simple functional style and eschewing classes altogether, since I am finding the extra overhead of maintaining all the references to @ (this) and also wiring up the instance methods to the scope a pain.



MyController = ($scope, $otherService)->
  someInternalField = "secret"

  someHiddenHelper = ()->
    #  I can use $scope and $otherService in here
    return $otherService.doStuff($scope.someField)

  someScopeHelper = ()->
    $scope.fieldToUpdate = someHiddenHelper()

  # I can initialize $scope here
  $scope.someField = "myValue"


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/angular?hl=en.


Peter Bacon Darwin

unread,
Mar 18, 2012, 4:35:07 PM3/18/12
to ang...@googlegroups.com
Oops, I meant: 

  $scope.someScopeHelper = ()->
    $scope.fieldToUpdate = someHiddenHelper()

Nate Kidwell

unread,
Mar 18, 2012, 5:12:25 PM3/18/12
to AngularJS
Yeah, never sure which is the right approach. Don't want to give up
classes (not than I'm stuck on them, but does make things more legible/
concise). However, if the scope is "implied", like I'm doing it some
wierd things *can happen*.

for example I had this code inside a repeater:
<img "ng-src":"{{ image }}", "ng-click":"imageClick(image)",
width:"80"/>

and in my controller had as a listener function:
imageClick:(@currentImage)->

However the @currentImage wasn't being set. Why? Because it was
being set in the scope for the repeater item, even though it was being
read from the main controller.

So I had to wind up injecting the scope here anyway...

On Mar 18, 4:35 pm, Peter Bacon Darwin <p...@bacondarwin.com> wrote:
> Oops, I meant:
>
>   $scope.someScopeHelper = ()->
>     $scope.fieldToUpdate = someHiddenHelper()
>
> On 18 March 2012 20:24, Peter Bacon Darwin <p...@bacondarwin.com> wrote:
>
>
>
>
>
>
>
> > I also am loving using Coffee script with Angular.  One of the nice things
> > about separating the scope from the controller is that we can control what
> > is allowed into the scope.  For instance, not all the helper methods and
> > services that the controller needs should be published to the scope.
>
> > For many of my Controllers, I am starting to lean toward the simple
> > functional style and eschewing classes altogether, since I am finding the
> > extra overhead of maintaining all the references to @ (this) and also
> > wiring up the instance methods to the scope a pain.
>
> > For example (seehttps://gist.github.com/2079970#comments):
>
> > MyController = ($scope, $otherService)->
> >   someInternalField = "secret"
>
> >   someHiddenHelper = ()->
> >     #  I can use $scope and $otherService in here
> >     return $otherService.doStuff($scope.someField)
>
> >   someScopeHelper = ()->
> >     $scope.fieldToUpdate = someHiddenHelper()
>
> >   # I can initialize $scope here
> >   $scope.someField = "myValue"
>

Misko Hevery

unread,
Mar 19, 2012, 12:20:08 AM3/19/12
to ang...@googlegroups.com
Let me summarize:

  • Prior to 1.0 'this' is scope is in controller
  • In 1.0 the scope is injected explicitly into controller
What are the advantages and disadvantages?

For small projects it looks like 'this' is scope is better since there is less typing and as a result the code is bit more readable. However this suffers from the issue that controller can never have private state, as well as that the controller unintentionally inherits from parent scope.

By making the scope explicit the controller no longer unintentionally inherits from parent scope and it has private state. This may seem like a small difference, but we found that on large projects the unintentional inheritance has caused a lot of pain. 

It really comes down to your own preference and how big your project is.

Vojta Jína

unread,
Mar 19, 2012, 3:16:36 AM3/19/12
to ang...@googlegroups.com

Nate Kidwell

unread,
Mar 19, 2012, 11:26:05 AM3/19/12
to AngularJS
Thanks Misko and Volta - good to get insight from your experiences
with angular-in-the-large. I have a huge Flex (R.I.P.) application
I'm replicating in angular, so docs like that are pretty awesome.

I usually pass my controller into helpers anyway so this winds up not
being too big an architectural deal (can pass $scope instead of this/
@). In vanilla coffeescript classes, $scope imho tends to exhibit a
degree of http://sourcemaking.com/refactoring/feature-envy (which
might simply be a sign, like Peter above argues, that classes aren't
the best way to do angular).

And the more I play with it, the more I think a degree of
metaprogramming is useful to add some delegation/decoration to the mix
(like the awesome Draper gem in ruby).

gaetan....@gmail.com

unread,
Jul 10, 2012, 5:44:51 PM7/10/12
to ang...@googlegroups.com
I like that, but what if I need to intentionally inherit from the parent scope ?

Also, is there is some way to access the scope of the previous page, after being routed to a new page ?

I have been looking for weeks on how to do this, without success. Maybe because I am new to js. Please help!

Thanks,

Gaetan Sheridan
Reply all
Reply to author
Forward
0 new messages