Angular Carto: Custom Zoom Buttons
This is part of a series covering how to create reusable Carto map components. Read the introduction to learn why one would even want to do such a thing. Read the previous installment, Carto Map Factory, to learn about adding a Carto map to a webpage and registering the map with the Angular dependency injector.
In this post, we will discuss how to add custom zoom buttons to the map.
What are directives?
If directives are a new concept to you, I recommend Sandeep Panda’s “A Practical Guide to AngularJS Directives.”
The zoom buttons directive
Registering the directive
Directives are added to Angular modules using the
directive function. The first argument is the name under which we are registering the directive; the second argument is a factory function for creating instances of the directive. Note that the registered name of the directive,
zoomButtons, is camel-cased.
Note that in this sample code, the directive factory is an anonymous function, but you can (and perhaps should!) define a named function in a site-wide library and register the named function wherever zoom buttons are needed. Note also that the directive factory takes a parameter named
map. At run time, the Angular dependency injector will pass whatever is registered under the name “map” as an argument to the directive factory. For more information about registering a Carto map with the Angular dependency injector, please see my previous post “Map Factory.”
Restricting calling style
Angular provides several different ways of calling a directive from HTML. The two most common are element style (
<zoom-buttons></zoom-buttons>) and attribute style (
<div zoom-buttons></div>). All calling styles produce the same results, so the Angular team recommends choosing the calling style to signal how the directive is used.
When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template. The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.To allow those two calling styles, we specify
A(for attribute-style) and
E(for element-style) in the
restrictproperty of the directive. There are other directive calling styles, but it is not necessary to discuss them here.
Writing HTML to be interpolated
Next, we need to define an HTML template for the content that will be interpolated into the web page when the directive is invoked.
Each directive has its own
$scope. If you are not familiar with
$scope, for the purposes of this article it is best understood as the view model for an Angular component; in this case,
$scope is the view model for the zoom buttons directive. The
ng-click data binding and double-brace string interpolation are bound to methods and properties on this directive’s
This directive defines two buttons for zooming in and out. When clicked, the buttons call, respectively, the
zoomOut() functions on the
$scope. The labels on the buttons are likewise defined in the
zoomOutLabel properties on the
$scope. Finally, to ensure that the caller of the directive has control over the appearance of the buttons, their
style attributes are bound to the
buttonStyle properties of the
You can define your HTML template either using a string literal, or by pulling it into a separate file if it is too unwieldy to manage within a string literal. Beware: the sample code below shows both techniques, but you should pick one or the other.
Configuring the directive
When configuring a directive, we define a
link() function that configures the directive
link() function is takes information from the directive invocation in the web page and translates that into the
$scope required by the HTML template.
The first parameter of the
link() function is the
$scope to be configured. The second parameter is the HTML element on which the directive was invoked. The third parameter is the attributes of said element. In this particular
link() function, information from the attributes are used to set the label and styling properties on the
$scope. Note that the
$scope.zoom*() functions are set to be
map.zoom*.bind(map). Why not just set them to
map.zoom* instead? Because the
map.zoom* functions contain a variety of references to
this. If you use
$scope.zoom* = map.zoom*, those
this references will be treated as referring to
$scope rather than
$scope.zoom* = map.zoom*.bind(map) binds the zoom methods so that they will still treat their
thises as the map rather than the scope.
Bringing it all together
You can Fiddle around with a working Angular Carto map with custom buttons below.
Why would you do such a thing?
Carto/Leaflet maps come with a built-in zoom control. We had to explicitly turn it off with the option
zoomControl: false. What’s so great about writing our own control in an Angular directive instead of using the built-in control? The main reason is that it gives us control over the look-and-feel of the zoom controls. If the built in Leaflet controls clash with the styling we’re using in the rest of the site, we can create buttons with the same functionality but a more appropriate appearance. Still not convinced? That’s okay. You’ve learned a lot about Angular directives, and that will serve you well in the rest of the series.