Not long ago I was selecting a javascript framework for a large client web project. The client’s existing applications made heavy use of Microsoft technologies so one factor in my choice was how would the framework “feel” coming from a background in Microsoft UI technologies. Having built Silverlight, WPF and Windows Phone apps myself, I felt particularly drawn to AngularJS. Below I’m calling out some core XAML concepts and their corresponding concepts in Angular. Along the way I’ll also note some key differences you should understand if you’re coming from the world of XAML development.
DataContext => $scope
The Model-View-ViewModel or MVVM pattern is the de facto standard for architecting applications using XAML technologies. XAML’s data-binding mechanisms facilitates keeping the “view”—a XAML document-in sync with the “view model”—some level of abstraction of the model that is constructed for the view. Typically the source of XAML data-bindings is the DataContext
of parent element in a XAML document:
Data context is a concept that allows elements to inherit information from their parent elements about the data source that is used for binding, as well as other characteristics of the binding, such as the path.
AngularJS provides a $scope
object whose function is very much like that of a DataContext (though the implementation is quite different).
- “$scope] is the glue between application controller and the view”; see the Angular guide for more information.
- Binding expressions are executed in the context of the
$scope
object $scope
s are inherited; you can reference properties of the scope in binding expressions on elements contained by the element where the$scope
is applied.
Unlike DataContext
inheritance, $scope
inheritance uses javascript’s prototypal inheritance. This is both more powerful (IMO) but also can introduce some unforeseen complexities. Read What are the nuances of scope prototypal / prototypical inheritance in AngularJS for more detail.
Data-binding & INotifyPropertyChanged => $watch
Alot of the grace and success of an “MV-whatever” application depends on the power of the data-binding between the view and the view model (or whatever you prefer…). In XAML, the Binding
class (typically instantiated via attribute syntax during XAML parsing) is the core of databinding. It gets the job done but I have to admit that, coming to XAML from the Adobe Flex framework, I felt the data-binding syntax was not as rich as I would’ve hoped (at least not without implementing an IValueConverter
or other fairly awkward workarounds):
- no logical expressions
- no math conversions
- no binding to method/function calls (when occasionally appropriate)
- no coallescing multiple model values into a single binding output
- etc.
With Angular expressions these limitations are removed and you can do something useful like this without a lot of extra headache:
<div><span ng-bind="Math.round(parentTask.completedTasks / parentTask.totalTasks * 100)"/>% complete</div>
Here are some quick points comparing XAML and Angular data-binding:
- Angular, like XAML, supports both one-way (
ng-bind
) and two-way bind (ng-model
) values in the “view” to data in the$scope
. - Angular expressions (as implied above) are more flexible and forgiving than XAML expresssions
- With Angular, your model DOESN’T need to implement any special interface (like
INotifyPropertyChanged
). This is actually one of my favorite feature of Angular with respect to other javascript frameworks like Backbone.js and Knockout. It doesn’t use change listeners opting for a dirty checking algorithm instead. - AngularJS binding expression in HTML are always evaluated in the context of the
$scope
object; there isn’t currently support for things like binding toStaticResource
s or other elements in the markup usingElementName
. However, the$watch
API does provide you some more flexibility for creating “bindings” in code.
IValueConverter => Filter & NgModelController
Before we move on from data-binding, let’s take a quick second look at IValueConverter
. When I wrote XAML apps I’d often have a list of a bunch of IValueConverter
s linked into App.xaml
that I could then reference throughout the app for common data-binding conversions:
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<InverseVisibilityConverter x:Key="InverseVisibilityConverter"/>
<VisibilityBoolConverter x:Key="VisibilityBoolConverter"/>
<CurrencyFormatConverter x:Key="CurrencyFormatConverter"/>
Angular has you covered here as well… In Angular, common conversion or formatting that should be applied to the output of the data-binding before it is rendered in the view are called filters. Angular provides several built-in filters: currency, date, etc and of course you can create your own.
IValueConverter
actually supports conversions in both directions when applied on two-way bindings. I found that most of the IValueConverter
s I used and wrote had an unimplemented or very simplistic ConvertBack()
method because they were only intended for use in one-way bindings. However, if you do need fine-grained control of conversions in both directions in Angular, you’ll want to add functions in the $parsers
and/or $formatters
arrays of the NgModelController
applied to that particular binding expression; NgModelController
is somewhat analogous to BindingExpression
.
Behaviors => Directives
In XAML a Behavior
is used apply functionality, frequently some kind of visual interactivity, to an element within a XAML document. The AssociatedObject
which is available from the OnAttached
method provides access to the XAML element on which the Behavior has been applied. You can listen for events on that element, update the properties of that element, modify the visual tree around that element, etc. Implementing a Behavior would make sense when you have a common user interaction you’d like to reuse in several places in your application. For example, if you have some contextual content that should be displayed when a TextBox
has focus, a behavior could be attached to the TextBox, listen for focus events and toggle the visibility state of another chunk of content.
In Angular, directives fill a function for HTML markup very similar to Behaviors for XAML markup.
angular.module('yourmodule', [])
.directive('showContentOnFocus', function() {
// Writing directives can range from a simple to a complex undertaking
// This simple approach is just highlight some similarities to XAML Behaviors
//
// Angular provides both
// - a scope ~= ((FrameworkElement)AssociatedObject).DataContext
// - an element ~= AssociateObject
return function(scope, element, attrs) {
// perform DOM manipulation, attach event handlers here...
// listen on DOM destroy (removal) event
element.on('$destroy', function() {
// This is where you'd perform logic to
// remove event listeners like you would
// in Behavior.OnDetaching()
});
}
});