Why is AngularFire updating the database just once ?
I'm getting the user as this:
var firebaseURL = "https://..";
angularFire(new Firebase(firebaseURL), $scope, "database").then(function() {
$scope.user = $scope.database.users[0];
});
And in the view:
<input ng-model="user.name" />
When I'm changing the input, it only updates once then never does.
Also, if I change something by using the firebase ui, the model does not update.
Since you are using the angularFire function in a promise context, the continuation (the then clause) is run only once and for the initial data. From the docs:
Data from Firebase is loaded asynchronously, you can use the promise to be notified when initial data from the server has loaded.
I suspect you want something of this kind:
angularFire(new Firebase(firebaseURL), $scope, "database");
$scope.$watch("database[0]", function (newVal) {
$scope.user = newVal;
});
Related
I'm using FireBase and trying to do some queries, the results are logging in but are not visible in the HTML $scope.
var shopRef = firebaseDataService.intro;
$scope.shops = [];
var taskRef = shopRef.orderByChild("cat").equalTo("Accomodation");
taskRef.on("value", function(snapshot) {
var snapData = snapshot.val();
console.log(snapData);
$scope.shops.push(snapData);
});
When I use $scope.$apply(), I manage to get the data updated to shops, but it's still not passing anything to my directive .
<search-card shops="shops"> </search-card>
<p> Shops are {{shops}}</p>
I got it working somehow with $firebaseArray
$scope.shops = $firebaseArray(taskRef);
but I`d still like to know what I'm doing wrong and why it's not working with the snapshot.
From the angularfire docs:
// read data from the database into a local scope variable
ref.on("value", function(snapshot) {
// Since this event will occur outside Angular's $apply scope, we need to notify Angular
// each time there is an update. This can be done using $scope.$apply or $timeout. We
// prefer to use $timeout as it a) does not throw errors and b) ensures all levels of the
// scope hierarchy are refreshed (necessary for some directives to see the changes)
$timeout(function() {
$scope.data = snapshot.val();
});
});
It seems that using $scope.apply() will not refresh the entire hierarchy (and hence the directive). Try using $timeout as prescribed instead
That being said, I think you should go with the $firebaseArray() option as that strikes me as the most "angular" solution
I discovered that when I call a service method within my controller and pass to it an object as a parameter, any changes that are done to that object (inside service method) are also made to the original object from my controller.
I always thought that controller data should stay unchanged until I changed it inside promise win/error event and only if I need to.
JS sample:
// Code goes here
var app = angular.module('App', []);
app.controller('AppCtrl', function($scope, simpleService){
$scope.data = { d: 1, c: 10};
$scope.clickMe = function(){
simpleService.clickMe($scope.data).then(function(res){
alert($scope.data.d);
})
.catch(function(err){
alert($scope.data.d);
});
}
});
app.factory('simpleService', function($q){
var simpleServiceMethods = {};
simpleServiceMethods.clickMe = function(data){
var deffered = $q.defer();
//data = JSON.parse(JSON.stringify(data)); - solution: clone data without references
data.d = 1111;
deffered.reject();
return deffered.promise;
}
return simpleServiceMethods;
});
Plunker demo: http://plnkr.co/edit/nHz2T7D2mJ0zXWjZZKP3?p=preview
I believe this is the nature of angular's databinding. If you want to pass the details of a $scope variable you could make use of angular's cloning capability with copy or update your services to work slightly differently by creating a copy on the service side. Normal CRUD style applications you'd normally be passing the id of an entity, receiving a new entity or posting changes which may in most cases already be present client side.
Another Question here,
I am using firebase and angular js, and trying to return data from my database to the console log using this code :
function userCtrl($scope){
$scope.userName="";
$scope.myData = new Firebase ("https://yjyc-signup.firebaseio.com/Entries");
$scope.users={};
$scope.saveUser = function(){
$scope.myData.push({userName: $scope.userName});
$scope.userName="RESET";
};
$scope.myData.on('value', function(snapshot) {
$scope.users = snapshot.val();
console.log("Author: " + $scope.users.name);
});
but the console return "Author: Undefined" although I have a value in my database of a name.
is anybody can help me that would be amazing
When using AngularFire you need to sync the reference before you can get any data from it. Also you're trying to use a Firebase function that doesn't exist for AngularFire as far as I'm aware. Instead try to register a $watch function in your controller and each time that $watch executes you grab the information from the reference. Something like this:
myApp.controller('UserCtrl', ['$scope', function($scope) {
$scope.$watch('watchedExpression', function() {
var ref = new Firebase ("https://yjyc-signup.firebaseio.com/Entries");
var syncedRef = $firebase(ref);
console.log('Author:' + syncedRef.name); //You need to change this path to work with your Firebase tree structure
});
}]);
If you don't want to register a $watch function you can look at the threeway data-binding, you can look at this here in the AngularFire documentation.
I am writing a small Angular web application and have run into problems when it comes to loading the data. I am using Firebase as datasource and found the AngularFire project which sounded nice. However, I am having trouble controlling the way the data is being displayed.
At first I tried using the regular implicit synchronization by doing:
angularFire(ref, $scope, 'items');
It worked fine and all the data was displayed when I used the model $items in my view. However, when the data is arriving from the Firebase data source it is not formatted in a way that the view supports, so I need to do some additional structural changes to the data before it is displayed. Problem is, I won't know when the data has been fully loaded. I tried assigning a $watch to the $items, but it was called too early.
So, I moved on and tried to use the angularfireCollection instead:
$scope.items = angularFireCollection(new Firebase(url), optionalCallbackOnInitialLoad);
The documentation isn't quite clear what the "optionalCallbackOnInitialLoad" does and when it is called, but trying to access the first item in the $items collection will throw an error ("Uncaught TypeError: Cannot read property '0' of undefined").
I tried adding a button and in the button's click handler I logged the content of the first item in the $items, and it worked:
console.log($scope.items[0]);
There it was! The first object from my Firebase was displayed without any errors ... only problem is that I had to click a button to get there.
So, does anyone know how I can know when all the data has been loaded and then assign it to a $scope variable to be displayed in my view? Or is there another way?
My controller:
app.controller('MyController', ['$scope', 'angularFireCollection',
function MyController($scope, angularFireCollection) {
$scope.start = function()
{
var ref = new Firebase('https://url.firebaseio.com/days');
console.log("start");
console.log("before load?");
$scope.items = angularFireCollection(ref, function()
{
console.log("loaded?");
console.log($scope.items[0]); //undefined
});
console.log("start() out");
};
$scope.start();
//wait for changes
$scope.$watch('items', function() {
console.log("items watch");
console.log($scope.items[0]); //undefined
});
$scope.testData = function()
{
console.log($scope.items[0].properties); //not undefined
};
}
]);
My view:
<button ng-click="testData()">Is the data loaded yet?</button>
Thanks in advance!
So, does anyone know how I can know when all the data has been loaded
and then assign it to a $scope variable to be displayed in my view? Or
is there another way?
Remember that all Firebase calls are asynchronous. Many of your problems are occurring because you're trying to access elements that don't exist yet. The reason the button click worked for you is because you clicked the button (and accessed the elements) after they had been successfully loaded.
In the case of the optionalCallbackOnInitialLoad, this is a function that will be executed once the initial load of the angularFireCollection is finished. As the name implies, it's optional, meaning that you don't have to provide a callback function if you don't want to.
You can either use this and specify a function to be executed after it's loaded, or you can use $q promises or another promise library of your liking. I'm partial to kriskowal's Q myself. I'd suggest reading up a bit on asynchronous JavaScript so you get a deeper understanding of some of these issues.
Be wary that this:
$scope.items = angularFireCollection(ref, function()
{
console.log("loaded?");
console.log($scope.items[0]); //undefined
});
does correctly specify a callback function, but $scope.items doesn't get assigned until after you've ran the callback. So, it still won't exist.
If you just want to see when $scope.items has been loaded, you could try something like this:
$scope.$watch('items', function (items) {
console.log(items)
});
In my project I needed to know too when the data has been loaded. I used the following approach (implicit bindings):
$scope.auctionsDiscoveryPromise = angularFire(firebaseReference.getInstance() + "/auctionlist", $scope, 'auctionlist', []);
$scope.auctionsDiscoveryPromise.then(function() {
console.log("AuctionsDiscoverController auctionsDiscoveryPromise resolved");
$timeout(function() {
$scope.$broadcast("AUCTION_INIT");
}, 500);
}, function() {
console.error("AuctionsDiscoverController auctionsDiscoveryPromise rejected");
});
When the $scope.auctionsDiscoveryPromise promise has been resolved I'm broadcasting an event AUCTION_INIT which is being listened in my directives. I use a short timeout just in case some services or directives haven't been initialized yet.
I'm using this if it would help anyone:
function getAll(items) {
var deferred = $q.defer();
var dataRef = new Firebase(baseUrl + items);
var returnData = angularFireCollection(dataRef, function(data){
deferred.resolve(data.val());
});
return deferred.promise;
}
I have a service for storing some application common data. This service can also modify this data and react to events. However I am having trouble with propagating the changes to controllers that are using this service.
I've seen this question and this question, but the solutions don't work for me.
I created a plunker that shows what is my core problem. There is a service object storing common data in sharedObject. It has a function updateObject, which can update the data (in my real app it sends a request to a server). The service is used by main controller. However, when updateObject is called in the service, data are not updated in the controller.
Why is that? Also is this a best practice or am I doing somethign wrong?
Here's the code:
app = angular.module('app', []);
app.factory('object', ['$timeout', function($timeout){
var sharedObject = {attr1: '', attr2: ''};
var updateObject = function() {
$timeout(function(){
sharedObject = {attr1: 'new value', attr2: 'update value'};
console.log('updated');
}, 1000);
}
updateObject();
return sharedObject;
}]);
app.controller('main', [
'$scope', 'object', function($scope, object) {
$scope.$watch(function(){return object}, function(obj){
console.log(obj);
$scope.object1 = obj;
}, true);
}
]);
The problem is that the timeout callback doesn't update the object you're watching. It replaces it by another object. So the watcher still sees the old object, unmodified.
Replace the code, as in this updated plunker, by
sharedObject.attr1 ='new value';
sharedObject.attr2 = 'update value';
and you'll see the values change in the page.
The problem is that you are replacing the object that sharedObject points initially with another object altogether in update method.
You controller would be still holding the reference to the older object.
What you need to do is update the sharedObject in place. See my updated plunkr
Basically you need to do something like this in update method
sharedObject.attr1= 'new value';
sharedObject.attr2= 'update value';