[LRUG] [JOBS] Help with AngularJS + Rails

Mark Weston mark at markweston.me.uk
Fri Jan 17 02:39:19 PST 2014


As a spectator to this conversation, I just want to say thank you.  That
was an awesome post.


On 16 January 2014 18:41, Jasim A Basheer <jasim.ab at gmail.com> wrote:

> Hi Diego,
>
> We're working on an Angular-Rails project and have made some mistakes and
> learned a few things along the way. I cannot offer to do a paid and/or
> onsite gig, but I'd be happy to help with any specific questions you might
> have (thru LRUG, if it isn't terribly off-topic, or through email).
>
> To start with, here are a bunch of pointers in no particular order.
> Apologies if you're already familiar with them. I'm also curious to hear
> everybody's thoughts on this and hopefully learn something in the process.
> General
>
>    -
>
>    Use Coffeescript.
>    -
>
>    Start with Egghead.io. Read AngularJS by Brad Green, Shyam Seshadri (
>    http://shop.oreilly.com/product/0636920028055.do). Spend a few hours
>    and skim it cover-to-cover at least once. Angular documentation on the web
>    is quite fragmented; a book is the best way to begin learning Angular.
>    -
>
>    If you aren't familiar with Underscore.js already, go through it. Add
>    it to the project as soon as you begin. You'll need it.
>    -
>
>    Drop gem 'ng-rails-csrf' into your Gemfile to make Angular AJAX
>    requests play well with Rails CSRF protection.
>
> Directives
>
>    -
>
>    Some material on the web might lead you to think that a deep
>    understanding of Directives is crucial to ‘get’ Angular. But for a
>    beginner, the important thing is to just have fun doing things the Angular
>    way (it is loads of fun, let me assure you). The basic directives Angular
>    provides (eg: ng-repeat and ng-show) can go a surprisingly long way. Worry
>    about transclusion, $compile, $digest and other arcane incantations only
>    after you get comfortable with the basics.
>    -
>
>    One reason for you to write a directive is when you need to use
>    existing widgets. A lot of popular widgets already have Angular wrappers.
>    For example, we use the select2 wrapper
>    https://github.com/angular-ui/ui-select2 for multi-select.
>
>    There is also AngularStrap (http://mgcrea.github.io/angular-strap/)
>    with a bunch of nice components that wraps over Twitter Bootstrap. The v1 (
>    http://mgcrea.github.io/angular-strap/1.0/) version has Datepicker and
>    Timepicker which isn't available in the new version yet.
>    -
>
>    You would hit a point when off-the-shelf directives won’t be enough
>    and you'd want to do something that requires DOM manipulation. That would
>    be a good time to start digging into Directives. You can also start
>    exploring Directives when you start seeing patterns in your controllers
>    that can be extracted out into reusable components.
>
> Serialization
>
>    -
>
>    We use ActiveModel::Serializer. It is a safe choice.
>    -
>
>    Pay close attention to what you include in each serializer. Here is
>    the obvious thing: you can put a bunch of has_many in your User model,
>    and if you keep doing that for other objects, arender json: User.all will
>    end up sending your entire db graph to the client.
>
> We implement a base serializer for most entities with some basic fields
> and no associations. When we need more data/associations, we sub-class them
> for that specific use case. Discourse follows a similar style -
> https://github.com/discourse/discourse/tree/master/app/serializers
>
>    - Try not to do any decoration on the server side; it is tempting with
>    all the view helper goodness Rails provides, but then you are splitting
>    logic between client and server and it causes all sorts of headaches. You
>    can use Angular filters as client-side helpers on some occasions. eg: {{
>    start_time | date }} prints out nicely formatted dates.
>
> Forms, validations and POJO form objects
>
>    -
>
>    Angular gives you validation attributes like ng-required,
>    ng-minlength, ng-maxlength etc. on all form INPUTs. For simple use cases,
>    this is the best way to go. But for a complex form, we ended up building an
>    ActiveModel-like POJO Form Object to hold validation information, and for
>    doing some slight transformations on the data before submission. We bound
>    this object to the scope and removed a lot of clutter from our controllers
>    that had begun to get bloated. We have to see how this pans out in the long
>    run; I can give code snippets if you wish.
>    -
>
>    When I introduced Angular to our Rails project that was already under
>    development, I tried to minimize the amount of change required by trying to
>    keep form POSTs as they were.
>
>    <input type='text' ng-model='post.title' name='post[title]'>
>    <input type='submit' />
>
>
> It worked for the most part, but we ran into some edge cases that wasted a
> lot of time. That was when I learned to let go and fully embrace the
> Angular way. We switched over to something like this:
>
> <input type='text' ng-model='post.title' name='post[title]'>
> <button ng-click='submitForm()'>
>
> And the controller:
>
> angular.module('myApp').controller 'postCtrl', ['$log', '$scope', '$http', '$state', '$stateParams', '$window', 'flash', ($log, $scope, $http, $state, $stateParams, $window, flash) ->
>   $scope.submitForm = ->
>     $http.post(
>       Routes.posts_path,
>       $scope.post           # this is the ng-model hash we bound the input boxes to.
>     ).success((data, status) ->
>       $state.go("posts.show", {post_id: data.post.id})
>     ).error((data, status) ->
>       if data['message']
>         flash.error = data['message']
>       else
>         flash.error = "Could not create Post. #{data}"
>     )
>
>
> # `flash`  is from https://github.com/wmluke/angular-flash.
>
> # $state.go is ui-router. https://github.com/angular-ui/ui-router. More on that later.
>
> # we use JSRoutes to access Rails routes from JS. Quite useful. https://github.com/railsware/js-routes/
>
> The above code isn't ideal. You will be better off putting the $http.post inside
> an Angular service. This looks like a good post describing that:
> http://sravi-kiran.blogspot.in/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html
> .
> Rails+Angular
>
>    -
>
>    Go all the way in. Don’t try to keep a mix of Angular and non-Angular
>    pages. Except for Devise, all our pages are rendered client-side. We don't
>    have to worry about SEO since this is an internal app.
>    -
>
>    Use ui-router https://github.com/angular-ui/ui-router, not the one
>    that comes with Angular. The ui-router README could be confusing, but like
>    most of Angular documentaiton, it becomes obvious in hindsight if you
>    persevere.
>    -
>
>    We keep everything together in the same Rails project. Here is a rough
>    structure:
>
>    routes.rb:
>
>    root ‘base#angular’
>    resources :posts, only: [:index]
>
>    app/views/base/angular.html.erb:
>
>    <div ng-app='myApp'>
>      <div ui-view></div>
>    </div>
>
>    posts_controller.rb:
>
>      class PostsController < ApplicationController
>        respond_to :json
>
>        def index
>          render json: Post.all, root: false
>        end
>      end
>
>    app/assets/javascripts/routes.coffee.erb:
>
>    angular.module('myApp').run ($rootScope, $state, $stateParams) ->
>      $rootScope.$state = $state
>      $rootScope.$stateParams = $stateParams
>
>    angular.module('myApp').config ($stateProvider, $urlRouterProvider) ->
>
>      # The default route
>      $urlRouterProvider.when("", "/posts/list")
>
>      $stateProvider
>        .state("posts",
>          url: "/posts"
>          controller: "postsRootCtrl"
>          template: '<div ui-view></div>'
>          abstract: true
>        ).state("posts.index",
>          url: "/list"
>          controller: "postsIndexCtrl"
>          templateUrl: "<%= asset_path('posts/index.html.slim') %>"
>          resolve:
>            postsPromise: ($http) ->
>              $http.get(Routes.posts_path())
>        )
>
>    app/assets/javascripts/posts.js.coffee:
>
>    angular.module('myApp').controller 'postsCtrl', ['postsPromise', '$log', '$scope', '$http', '$state', '$stateParams', '$window', (postsPromise, $log, $scope, $http, $state, $stateParams, $window) ->
>      $scope.posts = postsPromise.data
>
>    app/assets/templates/index.html.slim
>
>    div ng-repeat='post in posts'
>      h1
>        | {{post.title}}
>      p
>        | {{post.body}}
>
>
> Quirks and bugs
>
>    -
>
>    It is worth keeping in mind that in JS {} == {} is false, since
>    comparisons are by object identity, not by object value. You can do explict
>    comparison using Angular.equals when you need it. But in some cases
>    Angular does an implicit comparison, for which it doesn't use
>    Angular.equals. For example, if you try loading a page with a bunch of
>    pre-selected radio-buttons, if their values are JS Objects (Hash), Angular
>    won't pre-select them since the comparison will fail. This is not an issue
>    if your values are strings/numbers.
>    -
>
>    JS treats the keys of all objects as strings.a={1: "Hello"}; _.each(a,
>    (value, key) -> console.log(typeof key)). If your server sends Hashes
>    whose keys are numeric ids, they'll end up in JS with string ids.
>    -
>
>    A quick and dirty way to inspect an Angular model is to drop this in
>    your Angular view template:
>
>    pre
>      | {{ post | json }}
>
>    -
>
>    There is also Batarang, a Chrome plugin which lets you inspect your
>    scope interactively. But I've found it to causes my pages to misbehave in
>    certain cases. I these days use angular-chrome-debug, (
>    https://gist.github.com/mzgol/7893061) which is quite light-weight. I
>    load this script in my app in development env so that I don't have to go
>    thru Chrome Snippets everytime.
>    -
>
>    Angular has Angular.copy for deep copy when you need it -
>    https://github.com/angular/angular.js/blob/master/src/Angular.js#L725.
>
> Well, that's mostly it. Please keep in mind that the structure I described
> is working well for our app, which is not meant for public consumption. You
> might want to look at keeping your Angular and Rails project separately and
> utilize JS build tools like Bower, Yeoman and Grunt if your app needs that.
> I'm generally liking building rich UIs with Angular and wouldn't go back
> to building for the web without using data-binding. Awesome sauce.
>
> --
> Jasim A Basheer — Nilenso Software — http://nilenso.com
> http://twitter.com/jasim_ab
>
>
>
>
> On 16-Jan-2014, at 6:20 am, Matthew Rudy Jacobs <
> matthewrudyjacobs at gmail.com> wrote:
>
> On 15 January 2014 14:38, Matt Spendlove <matt at cenatus.org> wrote:
>
>>
>> I’ve never used the service but these guys seem to be trying to solve
>> your kind of request:
>>
>> https://www.airpair.com
>>
>
> Also check out CodeMentor, who do something similar.
> https://www.codementor.io/directory/angularjs
>  _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>
>
>
> _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20140117/b1f51850/attachment.html>


More information about the Chat mailing list