Reputation: 2310
See update below
I have an angular application that has a controller and a directive on the page of that controller that both reference the same firebase data(I have a good reason for keeping them separate). When I make changes to one reference the other references don't reflect the change unless I save the data. What is the best way to update all local firebase references without having to save the data. I would like to allow the user to see what his/her changes will look like before saving.
I have a plunker that illustrates the problem i'm having. Type into one input and none of the other inputs will update until you save. I would like them all to stay in sync as the user types.
http://jsfiddle.net/dberringer/F7MVB/2/
function MyCtrl($scope, $firebase) {
var ref1 = $firebase(new Firebase("https://fiddler.firebaseio.com/test1"));
ref1.$on('value', function () {
$scope.foo = ref1;
});
$scope.save1 = function () {
ref1.$save();
};
var ref2 = $firebase(new Firebase("https://fiddler.firebaseio.com/test1"));
ref2.$on('value', function () {
$scope.bar = ref2;
});
$scope.save2 = function () {
ref2.$save();
};
$scope.bas = $firebase(new Firebase("https://fiddler.firebaseio.com/test1"));
$scope.saveBas = function () {
$scope.bas.$save();
};
$scope.biz = $firebase(new Firebase("https://fiddler.firebaseio.com/test1"));
$scope.saveBiz = function () {
$scope.biz.$save();
};
}
Thanks!!
Update: Sorry. I should have been more clear. The fiddle I posted was just a quick and dirty way to illustrate the problem. Here is a bit more accurate representation of the situation and why being able to propagate changes without saving would be helpful. I'm trying to figure out if there is a nice out of the box way to propagate changes on one firebase reference to another firebase reference without requiring the user save the data to firebase. Perhaps there is a method or an event that I need to fire to tell all the references to update to match a . That would be sweet.
As you can see all my model logic is in the myModel factory. My controllers are nice and thin. The my-things directive may or may not exist on any given page so the directive accesses the model directly rather than via scope from the controller. My controllers don't need to inject a bunch of models for directives that they may or may not need. Going this route has felt very clean and natural when working with firebase. If this is not possible do folks have another way of rigging up a model, interacting with a firebase resource, that would facilitate this? Thanks again!
http://jsfiddle.net/dberringer/F7MVB/5/
angular.module('myApp',['firebase'])
.factory('myModel', function ($q, $firebase) {
var fbRef = $firebase(new Firebase("https://fiddler.firebaseio.com/"));
return {
getThings: function (id) {
var deferred = $q.defer(),
ref1 = fbRef.$child(id);
// Resolves the promise once the data is loaded from firebase
ref1.$on('loaded', function (data) {
deferred.resolve(ref1);
});
return deferred.promise;
}
}
})
.directive('myThings', function (myModel) {
return {
template: '<div>Hello, {{thingsListB.thing1}}!</div>',
restrict: 'A',
scope: {
elemId: '@myThings'
},
link: function postLink(scope, iElement, iAttr) {
myModel.getThings(scope.elemId).then(function (things) {
scope.thingsListB = things;
});
}
}
})
.controller('myCtrl1', function($scope, myModel) {
myModel.getThings('test1').then(function (things) {
$scope.thingsListA = things;
});
// Something like this might do the trick
// $scope.$watch('thingsListA', function () {
// $scope.thingsListA.$heyIChanged();
// });
})
.controller('myCtrl2', function($scope) {
$scope.foo = 'foo';
});
Upvotes: 3
Views: 1611
Reputation: 32604
A couple of things here:
Calling $firebase(ref)
automatically loads the data
When you call the AngularFire binding, $firebase
, with a reference it automatically loads the data. In this case the same data is being loaded four times.
EDIT: As of AngularFire 0.8 this is no longer true. To load data call $asObject()
or $asArray()
.
Sync locally with ng-model
, then save off to Firebase when ready
To get the data to sync locally, we don't need to use multiple AngularFire bindings. Just by using ng-model
and the same save function we can sync things locally. Once the data is ready to be saved to Firebase then we can process the operation.
We can simplify it by using one AngularFire binding and listening on changes for it. To get the changes to sync locally we can just share the same ng-model
as well as the same save
function.
var myApp = angular.module('myApp',['firebase']);
function MyCtrl($scope, $firebase) {
var ref1 = $firebase(new Firebase('https://fiddler.firebaseio.com/test1'));
$scope.thing1 = '';
ref1.$on('value', function(data) {
$scope.thing1 = data.snapshot.value.thing1;
});
$scope.save = function () {
ref1.$child('thing1').$set($scope.thing1);
};
}
View
<div ng-controller="MyCtrl">
<div>Hello, {{thing1}}!</div>
<input type="text" ng-model="thing1"/>
<button ng-click="save()">Save 1</button>
<div>Hello, {{thing1}}!</div>
<input type="text" ng-model="thing1"/>
<button ng-click="save()">Save 2</button>
<div>Hello, {{thing1}}!</div>
<input type="text" ng-model="thing1"/>
<button ng-click="save()">Save 3</button>
<div>Hello, {{thing1}}!</div>
<input type="text" ng-model="thing1"/>
<button ng-click="save()">Save 4</button>
</div>
From your update
What I mentioned above still applies. What you're trying to do is "sync" data across Firebase instances without saving. It doesn't work that way. The $firebase
bindings immediately load all of the data from Firebase at that location. Therefore, you can't "locally" sync them.
However, that's okay because we can still use Angular to do syncing locally and then submit the data when ready.
In your directive we can actually get rid of the model and linking function. This directive essentially becomes ng-bind
now though.
.directive('myThings', function (myModel) {
return {
template: '<div>Hello, {{myThings}}!</div>',
restrict: 'A',
scope: {
myThings: '@'
},
link: function postLink(scope, iElement, iAttr) {
}
}
})
Then in the view change the attribute my-things to use a binding.
<div ng-controller="myCtrl1">
<div>This is page 1</div>
<div>Hello, {{thingsListA.thing1}}!</div>
<input type="text" ng-model="thingsListA.thing1"/>
<button ng-click="thingsListA.$save()">Save</button>
<div my-things="{{thingsListA.thing1}}"></div>
</div>
This way is much cleaner because we're sharing the property between then directive and the controller rather than creating it in two places.
Upvotes: 5