Hope some of you can provide me with some suggestions on how to Query Data From Firebase into my HTML Page.
Controller
.controller('cloudCtrl', ['$scope', '$stateParams', '$firebaseArray' ,// $stateParams.parameterName
function ($scope, $stateParams, $firebaseArray) {
var ref2 = firebase.database().ref().child("info");
var sync = $firebaseArray(ref2);
console.log(sync.toString());
sync.orderBychild('name').endAt(3).on('child_added', function(snap){
console.log('added', snap.val())
})
}])
HTML5 File
<div ng-controller="cloudCtrl">
<button ng-click="Load()">Load User</button>
</div>
Since you are using angularfire, you can do it this way:
$scope.load = function() {
$scope.result = firebase.database().ref().child("info").orderByChild('name').endAt(3);
}
Be careful concerning the camelCase on orderByChild.
You only need to watch the data if you want an event to occur when a child is added. For example, send a notification when a new message is available. Otherwise the database works in realtime, so if you modify a the name in info (directly in the firebase console) it will be available and updated in your scope.
There is one other way to do this which might come in handy while you're preparing your app:
$firebaseArray(yourRef).$loaded().then(function(result) {
$scope.result = result;
})
This will handle the promise while you're fetching the data. That might be useful if you want to show a loader while fetching the data which you would show before that function and hide once it is done.
Hope it helps !
Related
I'm new on Angularjs and I'm trying to build my first application. Let's say I have to routes that loads two different views:
127.0.0.1:8080/site
127.0.0.1:8080/site_details
Maybe having two different routes is not the right procedure but that it is another problem.
I have two controllers:
Controller 1:
app.controller('controller_1', function($scope, $http, user) {
user.set('Test Example')
});
and Controller 2
app.controller('controller_2', function($scope, $http, user) {
var xxx = user.get()
});
What I want to do is to share data between these two controllers. To do that I did a service in this way:
app.factory('user', function($rootScope) {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
By looking around it seems that having a service built like this should solve the problem. However, what I obtain with the function get() in controller 2 is always an empty return.
By setting breakpoints I can see that both set() and get() functions enters in their respective function in the service.
Is this a correct procedure to share data between controllers belonging of different routes?
EDIT1
The two views are built in the same ways and the are loaded inside ng-view
<html ng-app="app" ng-controller='controller_1'>
CONTROLLER 1
</html>
First, sharing data between a service is a correct approach.
In your case, you need to ensure the order of getting data is after setting data.
Using a $timeout is not a good approach, i think there should be another way, it depend on your detail code.
If your data is set after some event, you just need to pay attention to the order sequence like 'get after data has been set'
If you have to set data in initialization of controller_1, and controller_2 is sibling of controller_1, you can put the initialization logic of user data before bother controller_1 and controller_2 is entered.
I think you had giving factory reference to both html where first and
second controller you given have. in that case you have to give factory referee to main single page where your also loading sub pages(where you kept ng-view)
The problem occurs because, controller_1 was not created before the creation of controller_2. You can modify the controller_2 to introduce some delay using $timeout:
app.controller('controller_2', function($scope, $timeout, $http, user) {
// The time out is added to check your code working,
// You can replace the code or can use, its up to your requirement
$timeout(function(){
var xxx = user.get();
console.log(xxx);
}, 500);
});
Using $timeout will allow some time for creation of controller_1.
Also instantiate the controller_2:
<html ng-app="app">
<body>
........
<div ng-controller='controller_1'>
<div ng-controller='controller_2'>
</div>
</div>
</body>
</html>
You can use rootscope like below.
app.controller('controller_1', function($scope, $http, $rootScope) {
$rootScope.UserInfo ="Test Example";
});
app.controller('controller_2', function($scope, $http, $rootScope) {
var xxx = $rootScope.UserInfo;
console.log(xxx)
});
I have the same issue with this post Pass Angular scope variable to Javascript . But I can't achive my solution with their answers.
My Angular Controller
angular.module('App').controller('HomeController', [
'$rootScope', '$scope', '$state', '$timeout', 'ReportService', 'MsgService',
function($rootScope, $scope, $state, $timeout, ReportService, MsgService) {
$scope.$on('$viewContentLoaded', function() {
console.log('HomeController');
$scope.get_locations();
});
// get locations
$scope.get_locations = function() {
var data = {};
// call http get to my api
MsgService.get_all_locations(data, function(response) {
if (response.code == 1) { // success
$scope.locations_array = response.data; // data that I want to access to script
} else {
alert(response.message);
}
});
}
}
]);
My Html
<div id="map" ng-controller="HomeController">{{locations_array}}</div> // {{locations_array}} scope have the result that I want
<script type="text/javascript">
$(document).ready(function() {
var data = $('[ng-controller="HomeController"]').scope().$parent.locations_array;
console.log(data); // underfined
//var $element = $('#map');
// var scope = angular.element($element).scope();
// console.dir(scope.$parent.locations_array); // underfined
});
</script>
I tried access from browser develop tool then It can access scope. But My code can't access this.
How to solve this?
The immediate problem here is a timing issue - you are trying to read the locations_array value off the scope long before the value is populated.
The sequence of events is something like this:
ready event for document triggers, and before Angular has even thought about starting, your inline JS code runs, trying to read the value from the scope, which doesn't exist yet.
Angular bootstraps your Angular application in response to the document's ready event (this may be before #1, depending on the order of scripts on the page). This will call the HomeController constructor, that only sets up a listener for the $viewContentLoaded event.
The $viewContentLoaded event gets broadcast, and you initiate an asynchronous request for the locations.
When that returns with the locations some time later, it populates them on the scope.
Don't rely on .scope()
In addition to the timing issues, there is another major problem with your solution - it relies on the debug information being included by AngularJS. Obviously, it is by default, but it is possible to disable this debug information for significant performance gains in production.
If someone else comes along, possibly after you have left, and tries to disable debug information to improve performance or for some other reason (it is a recommended practice in production), it will stop .scope() from working.
So by relying on .scope(), you are making it so that disabling debug info, a best practice and performance booster, is not possible now or in the future for your app, because it will break things. And it won't be at all obvious to that developer that it would break anything.
So relying on .scope() for anything other than debugging should always be a very last resort.
So what do I do instead?
Like I mentioned, this is a timing problem - you need to wait until the locations are eventually loaded before running code that relies on them.
Luckily, we have many options in JS to deal with asynchronous values - callbacks, promises, RxJS observables, etc. Pick your favourite.
Example: using a global promise
In your controller, create a promise on the global scope (icky, but it needs to be outside Angular somewhere), and resolve that promise with the location data when it is loaded.
var resolveLocations;
window.locationsPromise = new Promise(function (resolve) {
resolveLocations = resolve;
});
angular.module('App').controller('HomeController', [
'$rootScope', '$scope', '$state', '$timeout', 'ReportService', 'MsgService',
function($rootScope, $scope, $state, $timeout, ReportService, MsgService) {
$scope.$on('$viewContentLoaded', function() {
console.log('HomeController');
$scope.get_locations();
});
// get locations
$scope.get_locations = function() {
var data = {};
// call http get to my api
MsgService.get_all_locations(data, function(response) {
if (response.code == 1) { // success
resolveLocations(response.data); // resolve the promise
$scope.locations_array = response.data; // data that I want to access to script
} else {
alert(response.message);
}
});
}
}
]);
Then, your normal (non-angular) javascript (which needs to run after your Angular javascript file is loaded) could use that promise to do something with the data when available:
<script type="text/javascript">
$(document).ready(function() {
window.locationsPromise.then(function (locations_array) {
console.dir(locations_array);
// do something with the data
});
});
</script>
There is probably a better way
Without knowing why you think you need access to this data outside of Angular, it's hard to say for sure, but there are likely other better ways of handling the interplay between Angular code and other Javascript code that depends on it.
Maybe you create a directive to integrate a jQuery plugin, or another service, or whatever, but since AngularJS code is just normal JS, there is no need to think of them as separate from each other. You just have to get the timing right so you have the data available. Good luck!
I am working on displaying collection that I got from DB in angular with firebase DB. I have those controller and service setup. in the html, I use search.users expecting it will hold all the data that I got from the DB but it won't show up. I can't figure out why. I tried few things like angular.copy or $broadcast with no luck. Can anyone help advise on this? Appreciated in advance.
.controller('SearchController', function ($scope, SearchService, logout, $location){
var search = this;
search.users = SearchService.users;
//$scope.$on('evtputUsers', function () {
// search.users = SearchService.users;
//});
})
//service for SearchService
.factory('SearchService', function ($http, $rootScope){
var userRef = new Firebase("app url");
var broadcastUsers = function () {
$rootScope.$broadcast('evtputUsers');
};
//get the user info
//insert the data to the db.
//retrieving the data
var dbUsers;
userRef.child('users').on('value', function(snapshot){
dbUsers = snapshot.val();
// angular.copy(snapshot.val(), dbUsers);
console.log('usersinDB:',dbUsers);
broadcastUsers();
}, function(err){
console.error('an error occured>>>', err);
});
return {
users: dbUsers
};
})
Rather than using $broadcast() and $on() you should use the AngularFire module.
AngularFire provides you with a set of bindings to synchronizing data in Angular.
angular.module('app', ['firebase']) // 1
.controller('SearchCtrl', SearchCtrl);
function SearchCtrl($scope, $firebaseArray) {
var userRef = new Firebase("app url")
$scope.users = $firebaseArray(userRef); // 2
console.log($scope.users.length); // 3
}
There are three important things to take note of:
You need to include AngularFire as firebase in the dependency array.
The $firebaseArray() function will automagically synchronize your user ref data into an array. When the array is updated remotely it will trigger the $digest() loop for you and keep the page refreshed.
This array is asynchronous. It won't log anything until data has populated it. So if you're logs don't show anything initially, this is because the data is still downloading over the network.
I'm currently learning AngularJS and similar stuff, and today I've encountered a problem (probably with async).
What I'm trying to do, is to use an Angular factory to get some data from Firebase and then use the data in a controller.
App.factory('Jobs', ['$firebaseObject', function($firebaseObject) {
var ref = new Firebase('https://myapp.firebaseio.com/Jobs');
return $firebaseObject(ref);
}]);
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
Jobs.$bindTo($scope, 'allJobs');
console.log($scope.allJobs);
}]);
This is working pretty OK. When I put {{ allJobs | json }} in a template- it is updated after few seconds. The problem is that in the controller $scope.allJobs is returning undefined (probably because the response from Firebase arrived later than the code has been executed.
My question is, how to write it, so I can access $scope.allJobs directly in the controller?
You could do something like this:
App.factory('Jobs', ["$firebaseObject",
function($firebaseObject) {
// create a reference to the Firebase where we will store our data
return function(url){
var ref = new Firebase(url);
// this uses AngularFire to create the synchronized array
return $firebaseObject(ref);
};
}
]);
Then in your controller:
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
$scope.allJobs = Jobs('https://myapp.firebaseio.com/Jobs');
$scope.allJobs.$loaded().then();
}]);
This is showing the $loaded method as opposed to $bindTo. As the other answers/comments mention, $bindTo may be the better way to go.
Referencing to this Firebase documentation: https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebaseobject-bindtoscope-varname
I can just do it very very simple:
App.controller('JobsController', ['$scope', 'Jobs', function($scope, Jobs) {
Jobs.$bindTo($scope, 'allJobs').then(function() {
// now I have access to $scope.allJobs when everything is downloaded from Firebase
});
}]);
I have an issue when I try to display data.
I send my form and update my database (it works great, backend is ok)
I use a button on the page to return to homepage
The view of the homepage is not updated, always the same values.
(It seems that there is only the template that loads, not with queries)
I need to click on the button refresh of the browser to see new values.
After the 'hard' refresh, the view is updated.
Why do I need to completely refresh the page ?
Here is my code
JS :
My service GenericService
(I created this service because I use this in several controllers)
myApp.factory('GenericService', function ($http, $q, MyFunctions) {
var data = {};
function getDataIfNeeded(action) {
action = action || 'default';
if (data[action] !== undefined) {
return $q.when(data[action]);
}
return $http({
url: "php/functions.php",
method: "GET",
params: {
action: action,
dummy: MyFunctions.randomString(7)
}
}).then(function(response) {
data[action] = response.data;
return data[action];
});
}
return {
getData: getDataIfNeeded
};
});
The call of the service
myApp.controller('listCtrl', ['$scope', '$http', '$rootScope', '$location', 'GenericService',
function ($scope, $http, $rootScope, $location, GenericService) {
GenericService.getData("get_projects").then(function (data) {
$scope.projects = data;
});
GenericService.getData("get_projects_draft").then(function (data) {
$scope.projects_in_draft = data;
});
}]);
HTML :
<div ng-controller="listCtrl">
<div ng-repeat="project in projects">
<span>{{ project.nom }}</span>
</div>
<div ng-repeat="project_draft in projects_in_draft">
<span>{{ project_draft.nom }}</span>
</div>
</div>
Your service GenericService fetch the data from the server only "if needed", which means if the local data variable isn't empty. Naturally, without reloading the service and after a form submission, the data will be out-of-sync! So you have two choices:
If the server is creating additional data, i.e. possesses data that AngularJS don't have after the form submission, you need to do another request in order to fetch the new data. Just empty the local data variable.
Else, if the server is just saving the data in a database for instance, and doesn't perform any other operation with an impact on what is shown on your page, you don't have to do an other request, since you already know what you sent to the server! Just update the local data variable.