Basically, you can replace JSF EL expressions by AngularJS mustaches almost everywhere in your code. If you do, the JSF bean is read and replicated on the client. It's part of the AngularJS model. So you can use the same value in a JSF bean and as a AngularJS model attribute. Even better, you can use both variables in a very similar way.
Let's have a look at the customer contact form we've been developing in the previous steps:
<prime:panel header="Contact information">
<prime:panelGrid columns="3">
<prime:inputText value="#{customer.lastName}" />
<prime:inputText value="#{customer.firstName}" />
<prime:inputText value="#{customer.dateOfBirth}" />
<prime:inputText value="#{customer.emailAddress}" />
<prime:selectBooleanCheckbox value="#{customer.IAgreeToTheTermsAndConditions}" />
</prime:panelGrid>
<prime:commandButton value="save" update="angular" action="#{customer.save}" />
</prime:panel>
This is a traditional JSF page that doesn't make use of the AngularJS model. There's a small main.js file, but it's only needed to provide the client side widgets of AngularFaces and the client side bean validation (see step 5).
Making this form's data available to AngularJS is as simple as can be: just replace the JSF EL terms #{...} by AngularJS style mustaches {{...}}:
<prime:panel header="Contact information">
<prime:panelGrid columns="3">
<prime:inputText value="{{customer.lastName}}" />
<prime:inputText value="{{customer.firstName}}" />
<prime:inputText value="{{customer.dateOfBirth}}" />
<prime:inputText value="{{customer.emailAddress}}" />
<prime:selectBooleanCheckbox value="{{customer.IAgreeToTheTermsAndConditions}}"/>
</prime:panelGrid>
<prime:commandButton value="save" update="angular" action="#{customer.save}" />
</prime:panel>
On the JSF side, the mustache expressions are converted to ordinary JSF expressions. It doesn't matter whether you write {{customer.lastName}} or #{customer.lastName}, until you look at the Javascript code.
On the AngularJS side, the bean attributes are added to the AngularJS scope. Hence there's a customer object, consisting of the first and last name, the date of birth and so on. For instance, you could use two AngularJS watches to suggest an email address:
function LabelDemoController($scope) {
// This initializes the Angular Model with the values of the JSF bean attributes
initJSFScope($scope);
$scope.$watch('customer.lastName', function(newValue, oldValue) {
if ($scope.customer.firstName!="" && $scope.customer.lastName!="") {
var name = "".concat($scope.customer.firstName).concat(".")
.concat($scope.customer.lastName);
$scope.customer.emailAddress = name + "@example.com";
}
});
$scope.$watch('customer.firstName', function(newValue, oldValue) {
if ($scope.customer.firstName!="" && $scope.customer.lastName!="") {
var name = "".concat($scope.customer.firstName).concat(".")
.concat($scope.customer.lastName);
$scope.customer.emailAddress = name + "@example.com";
}
});
(IMHO the Javascript code looks a bit odd. I'm sure there a better way of concatenating string, I just didn't figure out how
to do it when I wrote the demo).
Synchronizing values between AngularJS scope and JSF beans works in both ways. The values of the input fields are transmitted back to the server, no matter whether you do a regular HTML request, a JSF AJAX request or the optimized AngularFaces request.
AngularFaces provides an advanced kind of AJAX requests. Typically, they use a lot less network bandwidth, and they are faster than traditional JSF AJAX request. To activate AngularFaces AJAX, you have to do two simple preparations:
<prime:commandButton value="save" update="angular" action="#{customer.save}" />
<h:body ng-app="AngularFacesExamples" ng-controller="MyCtrl" id="angular">
Note that the id "angular" doesn't really mark an ordinary update region. It's a virtual id. If AngularFaces sees the id, it replaces the default update response generated by JSF by a highly-optimized response that updates only the scope values. However, there are also drawbacks to this approach. For instance, the <h:messages> tag isn't updated. Nor is <prime:growl>. I don't consider this a disadvantage: the idea of AngularFaces is to move such functionality to the client. Validation and presenting error messages in particular is Angular's job.