dc7a9163d9 dc7a9163d9 - 1 year ago 49
AngularJS Question

The parameters of functions magically match up in Angularjs?

I'm new to angualarjs and just start to build a page using angularjs 1.5 component. I'm trying to understand the following example from pluralsight. Does angularjs extended javascript with partial application/currying and others?


is passed with an object with property of

<span ng-repeat="entry in model.entries track by $index">
<span ng-click="model.setRating({ value: $index + 1})" class="glyphicon {{entry}}">

is bound using "&".

module.component("movieRating", {
templateUrl: "/ps-movies/movie-rating.component.html",
bindings: {
value: "<",
max: "<",
setRating: "&"

However, when using the component, the
is assigned a function with two paramters
model.setRating(movie, value)
? And movie is actually a value from
<tr ng-repeat="movie in model.movies">
? but

<tr ng-repeat="movie in model.movies">
<movie-rating value="movie.rating" max="5" set-rating="model.setRating(movie, value)">

And in the following code
model.setRating = function(movie, rating)
, there is no parameter that supposes to an object with property of

function controller($http) {
var model = this;
model.movies = [];

model.$onInit = function() {
fetchMovies($http).then(function(movies) { model.movies = movies; });

model.upRating = function(movie) { if(movie.rating < 5) { movie.rating += 1; } };
model.downRating = function(movie) { if(movie.rating > 1) { movie.rating -= 1; } };

model.setRating = function(movie, rating) { // rating is int, and movie is string
movie.rating = rating;

Answer Source

In AngularJS, the expressions bound by & are not executed in parameter order. Instead, when you execute a function bound like this, you have to execute it with an object. Let's take your example,

<movie-rating set-rating='model.setRating(movie, value)'></movie-rating>

Now, your movie-rating component will receive a setRating function and assign it to its controller. When you execute $ctrl.setRating() inside of the movieRating controller, Angular is going to expect you to pass it an object like

{ movie: 0, value: 0 }

it will then map these values to the parameter order specified in the string passed to set-rating; in other words, Angular will take model.setRating(movie, value) and work out that it actually needs to pass in the value of the movie key from the passed object to the first parameter and the value object to the second parameter.

So, when you click your span inside of movie-rating, it will call setRating({ value: $index }). It will leave movie undefined, so the parent function, movieList.setRating will receive the parameters (undefined, $index).

Note that all of this expression binding behaviour (where the names of movie and value inside of the set-rating expression are preserved) only applies to the template. This is why in JavaScript code you see that Angular does not fall over.

To basically tldr this, when you use value names in a function binding in the template, Angular will unpack an object passed to an invocation of the function and match up the parameters via name. It will only do this for & bindings, and only unpack js objects to & bindings (which have to be defined in HTML). It will not unpack JS objects to regular JS functions (which makes sense - the HTML bindings won't be mangled, but JS variable names will be mangled at runtime; this is why you need to use $inject or array syntax for dependency injection in Angular)

let me know if I can explain this any clearer