Question

I'm trying to integrate Rails with Angular to turn part of my app into a one-page-app using Angular. I have a main module with the following (coffeescript) code for routing:

MainApp.config ($routeProvider) ->
  $routeProvider
    .when '/', 
      {
        templateUrl: 'post_archive.html'
      }
    .when '/new', 
      {
        templateUrl: 'new_post.html'
      }
    .when '/:postSlug', 
      {
        templateUrl: 'show_post.html'
      }
    .when '/:postSlug/edit', 
      {
        templateUrl: 'edit_post.html'
      }
    .otherwise
      redirectTo: '/'

The main view for this section of the site starts with this haml

%div{ ng_controller: 'PostCtrl', ng_init: 'init()', ng_cloak: true }

And the PostCtrl has this init function:

$s.init = ->
  $s.getPost().then ->
    $s.getPostList() unless $s.postList
    $s.getPreviousPost()
    $s.getNextPost()

The idea is, the current post, as well as the next and previous, need to be recalculated, but the postList should remain the same, so it doesn't need to be re-fetched on every page load.

However, it is. It seems that the scope is getting dumped on every page load, which means that it's not really behaving at all like a one-page app, and there's a flicker while the postList reloads, whenever a link is followed within the app.

The links' rendered HTML looks like this, for example:

<a id="link_name" ng_href="#/post-name" class="ng-binding" href="#/post-name">
  Post Name
</a>

Any idea what I'm doing wrong here? (Does this have to do with all the pound signs that angular seems to be inserting before the final URL slash?)

Était-ce utile?

La solution

This is the expected behavior. If scope.init() is being called on init, it should be called on page load, as the scope will be bound (and initialized) each time its controller route will be accessed.

To avoid that behavior, simply call init() on demand, or — better yet — escalate postList to a higher level in the scope hierarchy (above ng-view, where the route change takes place and re-binds scopes to views), e.g. in the $rootScope. That way its initialization / evaluation won't be tied to the $s scope's init.

To illustrate this [1]:

  • You can define postList in the topmost scope, — it will be prototypically (is that even a word?) inherited:

    $rootScope.postList = [];
    
  • It's also sufficient to save it in a parent controller — so long as it's higher in the hierarchy than the router's scope (where ng-view resides), as same rules apply for inheritance with controller's scopes. Something along these lines [2]:

    // in the view
    %div { ng_contoller: 'ParentCtrl' }
      %div { ng_view }
        %div{ ng_controller: 'PostCtrl', ng_init: 'init()', ng_cloak: true }
    
    // in ParentCtrl
    $s.postList = [];
    

[1] Caution! not tested!
[2] Risk of bogus code — I don't really know HAML!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top