URL parameters are an essential part of single page applications. They hold the current state of an application and enable users to open the application in a certain state. Here’s an example:
http://localhost:8080/index.html#/page?showAll=true&filterByName=Thomas
| |
+--------- for on the client ---------+
(anchor part)
In this example, showAll=true
and filterByName=Thomas
are URL parameters.
The hash (#
) divides the URL into two parts:
a part which goes to the server (before #
) and a part which stays on the client (behind #
).
This is the so called anchor part which holds our URL parameters.
This part of the URL is purely for our AngularJS client!
Two-way binding
Two-way binding means that we bind a parameter of our URL (e.g. filterByName
) to a parameter of the controller (let’s say it has the same name: myController.filterByName
).
To achieve this, we need to update our controller if the URL parameter changes and we need to update our URL parameter if the controller changes vice versa.
That’s what two-way mean - controller to URL, URL to controller.
Controller » URL
Let’s start with the first way: controller to URL.
Everytime a property of the controller changes, we want to update the corresponding property of the URL.
To do so, we use Angular’s $watch
method (see here).
The method $watch
allows us to watch a value and to do something if it changes.
Exactly what we want!
The code is pretty straight forward:
angular
.module('my.module')
.controller('MyController', function($scope, $location) {
var self = this;
var filterByName = null; // optional declaration!
// Controller to URL
$scope.$watch(function() { return self["filterByName"] }, function (newVal) {
console.log("Property changed!");
$location.search("filterByName", newVal);
});
});
This code will do the following:
It will create a watcher which listens on the property filterByName
of our controller.
If the property changes, we get the new value and set it to the URL (by $location.search("filterByName", newVal);
).
To use the URL parameters we use a service called $location
provided by Angular.
That’s the default way to access the URL.
URL » controller
Now we only need the way back: URL to controller.
We can use another watcher for that.
But in this case we use Angular’s $on
.
This looks like that:
angular
.module('my.module')
.controller('MyController', function($scope, $location) {
var self = this;
var filterByName = null; // optional declaration!
// Controller to URL
$scope.$watch(function() { return self["filterByName"] }, function (newVal) {
console.log("Property changed!");
$location.search("filterByName", newVal);
});
// URL to controller
$scope.$on('$locationChangeSuccess', function(event) {
console.log("URL changed!");
self["filterByName"] = $location.search()["filterByName"];
});
});
What $on
does is simple:
It listens for events, e.g. the $locationChangeSuccess
event, which is thrown after the URL has changed.
If this happens, we use the $location
service to read the value from the URL and assign it to our controller.
Put it to a method!
We already have achieved what we wanted: we have a two-way binding between our controller and the URL. One last thing we might do right now, is to put this into a nice method we can reuse:
angular
.module('my.module')
.controller('MyController', function($scope, $location) {
var self = this;
var filterByName = null; // optional declaration!
bind("filterByName");
function bind(valueName) {
// Controller to URL
$scope.$watch(function() { return self[valueName] }, function (newVal) {
console.log("Property changed!");
$location.search(valueName, newVal);
});
// URL to controller
$scope.$on('$locationChangeSuccess', function(event) {
console.log("URL changed!");
self[valueName] = $location.search()[valueName];
});
}
});
If the property of our controller changes (e.g. because the user enters data) the URL will change. And if the URL changes (e.g. because the user clicked on a link) the controller will change. We have a two-way binding between controller and URL!
More
Best regards, Thomas.