Mostly PJAX site with some AngularJS
Asked Answered
G

3

6

I have a site using PJAX all over the place and I have a few pages that are using AngularJS. For the AngularJS pages I would like to continue to use PJAX to get all the benefits associated with not reloading the entire HTML page, assets etc. Unfortunately, PJAX just loads some HTML into the page and doesn't fire any javascript. This is okay, because I can fire the javascript manually on pjax success, but I can't quite figure out what makes AngularJS initialize.

For a simple scenario, lets say I AJAX the following HTML into a page. Also assume, the page already had Angular.js included. What could I call to have the following behave like an Angular App?

<div>
  <label>Name:</label>
  <input type="text" ng-model="yourName" placeholder="Enter a name here">
  <hr>
  <h1>Hello {{yourName}}!</h1>
</div>

Thanks

Gramnegative answered 14/10, 2012 at 21:39 Comment(2)
$compile service may be used.Overripe
Manual Initialize is proving quite usefull, docs.angularjs.org/guide/bootstrap ... its working with the exception that something is converting my mustaches }} in hrefs to %20%7D%7D ... I'm now trying to figure what is doing that or what can be done about it. AngularJS is working where the }} aren't being encoded.Gramnegative
D
14

angular.bootstrap(...) didn't quit work for me, it was throwing an

Already existing App Error.

So I went for the (re)$compile solution, but the docs are sparse when it comes to using angular from outside the framework (e.g how do you call $compile). After a lot of trial & error this works for me although there might be some overhead :

  // outside of angular...
  var ngRefresh = function() {
    var scope = angular.element("body").scope();
    var compile = angular.element("body").injector().get('$compile');

    compile($("body").contents())(scope);
    scope.$apply();
  }

  // PJAX
  $('#page').on('pjax:success', function(e){
    ngRefresh();
  });
Delphine answered 18/9, 2013 at 14:8 Comment(1)
Doesn't this leave remaining elements in the scope? When trying this locally my scope is getting bigger and bigger.Silicious
G
2

Manual Initialize worked just fine

  angular.element(document).ready(function() {
     angular.bootstrap(document);
   });

I ended up having some issues with the gem rack-pjax because it was escaping the mustaches in my urls, so I just got rid of the middle ware and decided to have the web server detect the pjax in the request and opt-out of a rendering the layout.

Gramnegative answered 14/10, 2012 at 23:13 Comment(0)
S
-1

Starting from the solution provided by @charlysisto, I added a hook in pjax:beforeReplace to clean up the old scope:

var cleanupRemovedScopes = function() {
  var $scope = angular.element("body").scope();

    var q = [$scope], scope, first = true;
    while (q.length > 0) {
        scope = q.pop();

        if (!first) {
            scope.$destroy();
        }

        first = false;

        if (scope.$$childHead) {
            q.push(scope.$$childHead);
        }

        if (scope.$$nextSibling) {
            q.push(scope.$$nextSibling);
        }
    }
}

$('#page').on('pjax:beforeReplace', function() {
  var $timeout = angular.element("body").injector().get("$timeout");
  $timeout(function() {
    cleanupRemovedScopes();
  }
}

However, the limitation of this cleanup script is, that your PJAX call replaces the whole body.

Silicious answered 23/3, 2016 at 10:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.