29

What's the best way to pass a value from the backend to AngularJS in the frontend? I'm using Django and the templates are able to spit out values that I need, however I'm not sure what's the best practice on passing these values to AngularJS.

Think of a blog post and its comments, If I had an AngularJS service that retrieves all the comments of a certain blog post by passing the post ID to the service, and Django is rendering the HTML template and it does know what's the post ID, however I need to pass this post ID to AngularJS and subsequently to the service.

One idea is to have it in a hidden <input> and assign this input to a model. Not very appealing.

Another one is to have a directive and pass this value in an attribute to this directive, this way I would be able to access this attribute's value:

// Django (or any backend) is rendering {{ object.value }}
<div class="myDirective" data-object-id={{ object.value }}>
   ...
</div>

angular.module('myDirectives', []).
    directive('myDirective', function() {
        return {
            restrict: 'C',
            transclude: false,
            link: function postLink($scope, $element, $attrs) {
                // $attrs.objectId would have the value
            }
        }
    });

These two approaches look fine. But I'm wondering if there is a cleaner way of doing this? Any approach that follows AngularJS best practices?

3 Answers 3

22

If you need to initialize only a handful of model values from a view the ng-init directive might be what you are looking for: http://docs.angularjs.org/api/ng.directive:ngInit

Using it you could simply write:

<div ng-init="myModel=backend-generated-value-here">
   ...
</div>

Having said the above you might have a bit of hard-time finding best practices for your use-case as AngularJS is focused on Web2.0, full-client-side web applications. At the moment it doesn't play ideally with the server-side generate content. It might change in the future versions of AngularJS.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I would say around 90% of my application has been transformed to client-side while the backend (Django) is purely serving rest APIs.
As the documentation says, ng-init is only appropriate with ng-repeat, a better way would be to create a custom directive to pass data from html to controller.
I am curious about how both Django compiler and AngularJS should work with this solution... are they both suppose to read this same code area at a time?
9

If all you need is to setup initial values for a given "page" (meaning backend call to a view) then you can do as you demonstrated. I would only make it "cleaner" by setting up a global variable via javascript, like so:

// Django (or any backend) is rendering {{ object.value }}
<script>
  var backendVars = {
    {{object.name}} : {{object.value}}
    //and any other objects, manually parsed
  }
</script>

And read it on with angular on the bootstrapping process.

However, angularJS is a MVC of it's own, so you may want to be able to set/get data or backend controllers' processes, without necessarily reloading the entire page. The "cleanest" way is then to make a web service to return the data you need (i.e. a view that returns JSON, for instance) and retrieve it via $http or $resource service:

angular.module('myApp', []).
  controller('MainCtrl', function($scope, $html) {
    $html({
    method: 'GET',
    url: '/your/JSON/view.json'
      }).
      success(function(data, status){
        $scope.yourData = data;
      }).
      error(function(data, status){
        //whatever you need to do if the data is not available
      });
  }

Also, I should note that you shouldn't put data and logic directly on directives - they are meant for DOM manipulation - instead get your information on a controller, and then pass it on to a directive via scope inheritance, attributes or 2-way data binding. Hope it helps.

4 Comments

I cannot invoke a web service because it depends on the value the backend is passing. Think of a blog post and the view that shows this blog post, if I had an API that returns this blog post information I need to pass the post ID to the web service in order to retrieve its data.
Then the first part of the answer is what you were asking for. But it isn't "angulary" - you are using a big (and heavily structured) MVC framework to do the job of a smaller, toolbelt-like framework (like jQuery, or something more model-oriented). In angular, you would think of it this way: why call you.com/blog/123 and bootstrap an app with every different page, when you can call you.com/#/blog/123 and use $route and $html to get the blog content, never leaving the page (app) as you change views? Again, if it is for something like a blog, angular may not be the best choice.
You have got a point. I would rather not pollute the global namespace though, I'm trying to avoid global variables as much as possible. Thanks for your answer, I will either go for the two approaches I mentioned or use ng-init as @pkozlowski.opensource described.
There is a problem with Thiago's answer and AngularJS approach to this kind of thing IMHO. According to @pkozlowski.opensource, "AngularJS is focused on Web2.0, full-client-side web applications. At the moment it doesn't play ideally with the server-side generate content." But using "you.com/#/blog/123" and "$route and $html to get the blog content" falls into another set of client problems related with the dependency of a JavaScript Virtual Machine and hashbangs, they say it breaks the web (isolani.co.uk/blog/javascript/BreakingTheWebWithHashBangs). What to do then?
9

I believe the proper way to do this is for the server to render a script with a value provider, then dependency-inject it in your app. Like so:

index.php:

<html ng-app='myApp'>

<head>
    <title>AngularJS Back to Front</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
    <script src='index.js'></script>
    <?php
        $globals = array(
            'count' => 1, 
            'title' => 'Active users',
            'users' => array(
                'name' => 'john',
                'age'  => 21
            )
        );

        $globalsJSON = json_encode( $globals )
    ?>

    <script>
        myApp.value('globals', <?=$globalsJSON;?>);
    </script>
</head>

<body ng-controller="myController">
    <div>{{title}}</div>
</body>

</html>

index.js:

var myApp = angular.module( 'myApp', [] );

myApp.controller( 'myController', [ 'globals', '$scope', function( globals, $scope ) {
    // Copy globals to scope
    for ( var property in globals ) {
        $scope[ property ] = globals[ property ];
    }
}]);

1 Comment

Adding php to an already complicated situtation doesn't seem good to me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.