[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-0003.html>
More information about the Chat
mailing list