How to make three-way binding work for child objects in AngularFire?
-
21-12-2019 - |
Question
The following code is getting data from Firebase and binding it to $scope:
'use strict';
angular.module('costumeQueenApp')
.controller('ActorByUserIdActorIdCtrl', function ($scope,$routeParams,FireRef) {
var userId = $routeParams.user_id
var actorId = $routeParams.actor_id
var base = FireRef.actorById(userId, actorId)
$scope.base = base
$scope.base.$bind($scope, 'actor')
})
And uses the following template code to display the data (Note: uses AngularUI-Bootstrap):
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in actor.sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" />
</div>
</accordion-group>
The problem is that three-way data-binding is not working for the items inside the ng-repeat div. If I have an input field that uses the fully-qualified field in an input field, e.g., actor.sizes.waist, everything works as expected.
How do I make the data binding work properly?
Solution
If you replace value
then you have essentially done: $childScope.value = somethingNew
. Like any other JavaScript reference, this dose not modify the contents of the original object.
To put it another way, setting value
above is equivalent to this:
var items = [{label: 'a'}, {label: 'b'}, {label: 'c'}];
angular.forEach(items, function(v, k) {
v = {foo: bar}; // does nothing to items! Only modifies the local variable `v`
});
You can fix this when using angularFire by using $set
:
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" ng-change="actor.$child('sizes/'+key).$set(value)" />
</div>
</accordion-group>
Note that ng-change
feels a bit hackish here but investigating that didn't feel necessary to illustrate the solution.
Also, if you are using $bind
to sync data, or you want to control the timing of the sync event, you can just set the data on the parent object without a $set call:
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" ng-change="actor.sizes[key] = value" />
</div>
</accordion-group>