loop through firebase database object and ng-repeat the result - angularjs

I am ng-repeating the objects stored in my $firebaseArrayobject.
mycontroller:
var projectquery = DatabaseRef.ref("projects").orderByKey().equalTo("-KUTPrs6ARZXjbjtNr7O");
$scope.projectslist = $firebaseArray(projectquery);
html view with ng-repeat:
<tr ng-repeat="obj in projectslist">
<td>{{ obj.field1}}</td>
<td>{{ obj.field2 }}</td>
<td>{{ obj.field3 }}</td>
<td>{{ obj.field4 }}</td>
</tr>
this is displaying only one project because I am passing in the project key manually to equalTo and thus filtering to show one.
From the other hand, I am getting a set of project keys from the below function by doing a forEach on another database where I get my project keys:
var keyquery = DatabaseRef.ref('/users/' + userId).orderByKey().once("value")
.then(function onSuccess(snapshot) {
snapshot.child("savedprojects").forEach(function(childSnapshot) {
var userprojects = childSnapshot.val();
console.log("my list is :", userprojects );
});
});
the output of the console is like:
my list is : -KUTLAZENGlxtzCtEfaZ
my list is : -KUTLM8r_kmVSTaxzLW5
my list is : -KUTPrs6ARZXjbjtNr7O
Question:
how is it possible to pass on the above list of project keys obtained from forEach into the first function which shows the projects and filters them by key?
Could somebody please give a relevant answer and help me solve this, without referring to other answers which are not directly related to my question?
thanks in advance!
edit:
my DatabaseRef refers to this factory, which is initializing the firebase database:
app.factory("DatabaseRef", function() {
return firebase.database();
});
update:
After implementing Gourav's answer, my console shows undefined in place of the project IDs:
as you can see, the user has six projects under his profile, but all of them are empty and undefined.
update 2:
here is console output after Gourav's edit:
and the error for ng-repeat:

I don't know much about DatabaseRef but this might help.
$scope.projectslist = [];
DatabaseRef.ref('/users/' + userId).orderByKey().once("value")
.then(function onSuccess(snapshot) {
snapshot.child("savedprojects").forEach(function (childSnapshot) {
var userprojects = childSnapshot.val();
console.log("my list is :", userprojects);
var projectquery = DatabaseRef.ref("projects").orderByKey().equalTo(userprojects);
$scope.projectslist.push(($firebaseArray(projectquery))[0]);
});
});

Related

ServiceNow spUtil

I'm trying to make a slight improvement to an existing widget that our team created, but can't seem to get it to work correctly. We have a widget that does a RowCount of tasks and groups them by state. I want the RowCount to auto update once a task is complete without having the user press the refresh button. I've read some documentation on $rootscope, $broadcast, and $on, but can't seem to get it to work.
Below is snippet of our HTML:
<table class="table table-sm table-responsive">
<tbody>
<tr class="h3">
<td colspan=2>Complete</td>
</tr>
<tr class="h2 bg-success" ng-repeat="x in data.values track by $index">
<td><span class="glyphicon glyphicon-check"></span></td>
<td>{{x.completedCount}}</td>
</tr>
</tbody>
</table>
A snippet of our Server Script:
var values = [];
var _completedCount;
var gsCompleted = new GlideRecordSecure('sn_hr_core_task');
//CLOSED COMPLETE, CLOSED INCOMPLETE,
gsCompleted.addQuery('state', 'IN', '3,4,7');
gsCompleted.addQuery('assigned_to', gs.getUserID());
gsCompleted.addQuery("parent.state", 'NOT IN', '1,800,900');
gsCompleted.query();
if(gsCompleted){
_completedCount = gsCompleted.getRowCount();
}
else{
_completedCount = 0;
}
values.push(
{
completedCount: _completedCount
});
data.values = values;
How do I get this widget to auto update the Completed row count without refreshing the page? I've been playing around with spUtil recordWatch, but cannot get it to work correctly:
function($scope, $sce, spUtil) {
var c = this;
c.data.loading = true;
//After page initially loads re-call server script to load data
c.server.get({
action: 'retrieve_data'
}).then(function(response) {
c.data.loading = false;
console.log('Response');
console.log(response);
c.data.values = response.data.values;
spUtil.recordWatch($scope, 'sn_hr_core_task', "", function(name,data) {
spUtil.update($scope);
})
});
}
Take a look at the widget Simple List, it has an example of one that may help a bit.
You should be able to change your recordWatch to this
var filter = "stateIN3,4,7^parent.stateNOT IN1,800,900^assigned_to=" + window.NOW.user_id;
spUtil.recordWatch($scope, 'sn_hr_core_task', filter);
You generally won't need a callback function unless there is some specific action you're triggering.

Foods.filter is not a function- Angular 2

i am trying to a multiple deletion by selecting checkboxes but i get the error Foods.filter is not a function error in my console when i try to make a multiple deletion. When i introduced the pipe in the html table, that was when the error was raised. But without the pipe, it works fine. Could it be that the filter is not receiving the array?
html
<tbody>
<tr *ngFor="let key of Foods | keys; let i = index" >
<td>{{i + 1}}</td>
<td><input #{{Foods[key].id}} [(ngModel)]="Foods[key].selected" type="checkbox"></td>
</tbody>
component
delete() {
this.RemoveFood = this.Foods.filter(Foods => Foods.selected);
for (var food in this.RemoveFood) {
this.httpService.deleteFood(this.RemoveFood[food].id)
}
}
Food seems to be an Object and not an array, so there is no filter method implemented.
delete() {
Object.keys(this.Foods) //get an array of the object keys
.filter(key => this.Foods[key].selected) //filter selected foods
.forEach(key=>{ //for each food, delete
let id= this.Foods[key].id;
this.httpService.deleteFood(id);
});
}
Note that it might be long to remove several foods one by one, maybe you should think to a batch delete method on the server.

Angular js fetch data from multiple table at the same time

This is not a question about something where I am getting stuck. It's a question about good practice and performance. My code is working properly. But I want to know the correct way of doing this task. Maybe I am already right. Just look at the scenario and the code and please suggest me the right way.
Requirement
I have near about 20 tables in the database, when a user logs in and go to the dashboard page I need to show the data from all the tables. It is not required to show all the data so I am fetching 25 rows from each table using Angular $http and displaying in the dashboard page.
Code
My Angular js code is:
$scope.$on('$viewContentLoaded', function (event) {
$scope.loadDashboardQue(event, '/route1/getDashboardData?user_id=123', 'table1');
$scope.loadDashboardQue(event, '/route2/getDashboardData?user_id=123', 'table2');
$scope.loadDashboardQue(event, '/route3/getDashboardData?user_id=123', 'table3');
$scope.loadDashboardQue(event, '/route4/getDashboardData?user_id=123', 'table4');
$scope.loadDashboardQue(event, '/route5/getDashboardData?user_id=123', 'table5');
$scope.loadDashboardQue(event, '/routeN/getDashboardData?user_id=123', 'tableN');
});
Now loadDashboardQue function defination
$scope.loadDashboardQue = function (event, url, tableName) {
$http.get(url)
.success(function (response) {
if (response.status === 'success') {
//Assigning data to HTML table
if (tableName === 'table1') {
$scope.table1Data = response.data;
}
if (tableName === 'table2') {
$scope.table2Data = response.data;
}
if (tableName === 'table3') {
$scope.table3Data = response.data;
}
if (tableName === 'table4') {
$scope.table4Data = response.data;
}
if (tableName === 'table5') {
$scope.table5Data = response.data;
}
if (tableName === 'tableN') {
$scope.tableNData = response.data;
}
} else {
console.log("Something wrong while fetching data from table ", tableName);
}
})
.error(function (error) {
console.log("The error is :", err);
});
});
HTML table
<table style="width:100%">
<tr>
<th>Nme</th>
<th>Id</th>
<th>Contact No</th>
</tr>
<!--Table1 Data-->
<tr ng-repeat="data in table1Data">
<td>{{ data.user_name }}</td>
<td>{{ data.user_id }}</td>
<td>{{ data.mobile }}</td>
</tr>
<!--Table2 Data-->
<tr ng-repeat="data in table2Data">
<td>{{ data.customerName }}</td>
<td>{{ data.customerId }}</td>
<td>{{ data.phone }}</td>
</tr>
<!--Table3 Data-->
<tr ng-repeat="data in table3Data">
<td>{{ data.student_name }}</td>
<td>{{ data.roll_no }}</td>
<td>{{ data.mobile }}</td>
.
.
.
<!--TableN Data-->
<tr ng-repeat="data in tableNData">
<td>{{ data.developer_name }}</td>
<td>{{ data.id }}</td>
<td>{{ data.mobile }}</td>
</tr>
</table>
You can see in each table the column name is different so I am not able to show all the data in a single ng-repeat. So I have written separate ng-repeat for each and every table.
This code is working fine but when the page starts loading it takes more than 7 seconds so this is my worry(performance wise). So please suggest me if any better way is available to achieve this.
Thanks for your valued time.
Merge Requests
Create a new end-point on your API which will get data from all tables and return it in one request. This will definitely save you some time especially if your request are cross-domain.
The following will not speed your application up, but they you asked about best practices so here goes.
Abstract API Calls to a Service
Try to avoid using $http in your controller. The controller should not be concerned with how to get the data, only that it needs to get it and then what to do with it.
Map Results
If you want to use single ng-repeat in your template, map every result (or part of result if you merged all the requests) so the object structure is the same for every table. The following is a simplified example for the table1.
$scope.tableData = [];
result.data.forEach(row => {
$scope.tableData.push({
id: row.user_id,
mobile: row.mobile,
name: user_id
});
});
This way you can loop through all table data using one ng-repeat.
Use $q.all()
This applies only if you cannot merge you requests on the API level. In you example you are calling the function 25-times manually. It would make sense to make a for loop from 1 to 25 and pu each request into an array:
const promises = [];
for (let i = 1; i <= 25; i++) {
promises.push(loadDashboardQue(YOUR_ARGS_HERE));
}
After that you can wait for all of them to resolve and access the results in the response which will by an array in the same order in which you pushed your requests into the promises variable.
$q.all(promises).then(response => {
// response[0] is table1data etc...
});
I hope this help you somehow. Let me know if you have some questions.

Angular JS, bind scope property to function

I'm new to Angular JS. I am creating an app that stores data in the web storage. WHen an enquiry form is submitted, it gets the current arrays from the web storage, strigifies the new form data, adds it to an array, and posts it back to web storage. Works great and all gets saved correctly.
$scope.submit = function () {
// get object from form data
var formData = { firstName: $scope.firstName, lastName: $scope.lastName, date: getDateFromatted(), posted: false };
addStoredData(formData, ENQUIRY_STORE);
}
function getStoredData(storeName) {
// get or create enquiry store
var storedData = (typeof localStorage.getItem(storeName) !== 'undefined' && localStorage.getItem(storeName)) || "[]";
// parse store into object
storedData = JSON.parse(storedData);
return storedData;
}
function addStoredData(data, storeName) {
var storedData = getStoredData(storeName);
var count = storedData.length;
// form data into next submission slot
storedData[count] = data;
// turn back into JSON
storedData = JSON.stringify(storedData);
// slap it back in web storage
localStorage.setItem(storeName, storedData);
}
On my HTML page I have this;
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Date</th>
</tr>
<tr ng-repeat="e in enquiries">
<td>{{ e.firstName }}</td>
<td>{{ e.lastName }}</td>
<td>{{ e.date }}</td>
</tr>
</table>
So I want to be able to do this...
// list of stored enquiries
$scope.enquiries = function () {
return getStoredData(ENQUIRY_STORE);
}
But it doesn't bind and there are no rows. If I do this...
$scope.enquiries = getStoredData(ENQUIRY_STORE);
it works, but then I have to keep setting it when a new enquiry is submitted. Maybe Anulgar doesn't let you have use functions to return data for binding, but I thought it did.
Any help much appreciated. Thanks.
In the first way ($scope.enquiries = function() ...) it doesn't bind because you are making an ngRepeat over a function (it doesn't throw an error because a function has actually a length).
However, even if you did <tr ng-repeat="e in enquiries()"> you would have a infinite digest error because enquiries() is returning a different object each time and angular is waiting to have the same result twice to stop the digests cycle.
The best way would be:
$scope.submit = function() {
...
setEnquiries();
}
...
function setEnquiries() {
$scope.enquiries = getStoredData(ENQUIRY_STORE);
}
And keep looping through enquiries:
<tr ng-repeat="e in enquiries">
$scope.selectedENQUIRY_STORE = null;
$scope.getStoredData= function (e) {
$scope.selectedENQUIRY_STORE = e;
};
it binds when you clicked on that enquiry form on name
You simply have to retrieve the data again, upon submitting, so move your $scope.enquiries() function into your $scope.submit() function.
$scope.submit = function () {
// get object from form data
var formData = { firstName: $scope.firstName, lastName: $scope.lastName, date: getDateFromatted(), posted: false };
addStoredData(formData, ENQUIRY_STORE);
// FIRE UP THE FUNCTION
$scope.enquiries();
}
Note that you should put getStoredData() and other data related function into an angular service.
Luckily, there's already a good module for dealing with LocalStorage operations :
https://github.com/tymondesigns/angular-locker

angular js ng repeat asynchronously

I want to show a list of elements on view asynchronously.
For example i have a method which returns promise and result will look like this.
var result = {
books: [
{name: 'The Kite Runner', author: 'Khaled Hosseini'},
{name: 'The Book Thief', author: 'Markus Zusak'},
{name: 'A Thousand Splendid Suns', author: 'Khaled Hosseini'},
]
}
Here is a method:
function getBooks(userId) {
return BookProduct.getBooksByUser.query({ id: userId }).$promise;
};
And then i have a method which invokes getBooks.
$scope.showBooks = function(userId) {
//some users id
var users_id = [1,2,3,4,5];
$scope.tables = [];
for (i = 0; i < users_id.length; i++) {
getBooks(i).then(function(result){
$scope.table = {
books = result;
}
$scope.tables.push($scope.table);
})
}
}
Thereafter i want to show it to user on a view using angular ng-repeat.
<div data-ng-repeat="table in tables">
<table>
<thead>
<tr>
<th>Name</th>
<th>Author</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="book in table.books">
<td>{{ name }}</td>
<td>{{ author }}</td>
</tr>
</tbody>
</table>
</div>
It works good but ng-repeat will work when function $scope.showBooks is executed and $scope.tables is already full. I am looking for more nice solution. I want to load books for user piecemeal on each iteration when method getBooks() invokes.
So in this part for example when the first iteration is processing and i get result of 2 elements, then i pass them to the view and user can see the first part. Next iteration i get for example other 3 elements, so i add them to previous result and pass to the view, so user can see 5 elements now, and so on. I know it will be almost insensibly for user but if my server responds slowly it will help me to be more user friendly.
for (i = 0; i < users_id.length; i++) {
getBooks(i).then(function(result){
$scope.table = {
books = result;
}
$scope.tables.push($scope.table);
})
}
I am not asking you HOW to implement it. I just want to know is it possible or not and where should i dig to know more, cause i tried some googling but without success.
Angular has a cycle in which it performs all scope functionality and after each cycle of "watches" it renders changes to the view. By default, Angular performs this automatically - for example simple change of variable in scope also rerenders view. But in some cases you may want to intercept because your operation didn't trigger the rerender.
For these cases, $apply and $digest are useful - more in documentation https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

Resources