$q.all not working as expected - javascript

It's like there is a loading div that I want to show if two API calls are yet not ready with the results.
SO that the results all of sudden do not jump into the view causing it to flicker.
My view looks something like this :
<div ng-show="vm.loading" class="table-overlay table-overlay-loading">
<div class="table-overlay-content">
<div class="table-overlay-message">Loading…</div>
</div>
</div>
<div ng-show="vm.loadError" class="table-overlay table-overlay-error">
<div class="table-overlay-content">
<div class="table-overlay-message"><i class="icon-error-indicator"></i>
Error encountered. Please try again.
</div>
</div>
</div>
<div class="inner" ng-show="!vm.loading && !vm.loadError">
<div class="info-panel">
<h3>Current Pricing</h3>
<p>
Billing Period:
<br>
<em>{{vm.invoiceCoverageStartDate}} to {{vm.invoiceCoverageEndDate}}</em>
<br>
<big><b>${{vm.invoiceTotal}}</b>/month</big>
<br>
<small>(See Details)</small>
</p>
</div>
And the methods to populate the interpolated values look like :
vm.getCurrentPricingDetails = function(){
HttpWrapper.send(url1, {"operation":'GET'})
.then(function(result){
console.log("Current Pricing Response: ", result);
vm.invoiceCoverageStartDate = $filter('date')(result.invoice.coverageStartDate, "dd/MM/yyyy");
vm.invoiceCoverageEndDate = $filter('date')(result.invoice.coverageEndDate, "dd/MM/yyyy");
vm.invoiceTotal = result.invoice.invoiceTotal;
}, function(error) {
console.log("Error: Could not fetch current pricing details", error);
});
}
vm.getProjectedPricing = function(){
$timeout(function(){
var selectedPricingMappingObj = dataStoreService.getItems('server');
selectedPricingMappingObj.forEach(function(item){
vm.totalProjectedPricingSum += parseFloat(item.selectedMapping.cost);
});
vm.totalProjectedPricingSum = vm.totalProjectedPricingSum.toFixed(2);
},1000);
}
But in my components $onInit method I tried to resolve the same using promise.
vm.$onInit = function() {
vm.loading = true;
vm.loadError = false;
var currentPricingDetails = vm.getCurrentPricingDetails();
var projectedPricingDetails = vm.getProjectedPricing();
$q.all([currentPricingDetails,projectedPricingDetails]).then(function(results) {
debugger;
vm.loading = false;
}, function(error){
debugger;
vm.loading = false;
vm.loadError = true;
});
But still the screen flickers and the loading div does not show .
I want the loading div to show until and unless the two method calls are not done with the results.`
How to achieve that ?
What am I doing wrong ?

vm.getCurrentPricingDetails() and vm.getProjectedPricing() does not return anything. So your line
$q.all([currentPricingDetails,projectedPricingDetails])
is really just
$q.all([undefined, undefined])
$q.all expects an array of promises. $timeout returns a promise when it's done. And it seems like your HttpWrapper also returns a promise. So I think that all you need to do is to add returns to your code:
return HttpWrapper.send(url1, {"operation":'GET'})
and
return $timeout(function(){

vm.getCurrentPricingDetails and vm.getProjectedPricing are not returning promises and hence the $q.all has no chance knowing when they are finished
m.getCurrentPricingDetails = function(){
var defer = $q.defer();
HttpWrapper.send(url1, {"operation":'GET'})
.then(function(result){
console.log("Current Pricing Response: ", result);
vm.invoiceCoverageStartDate = $filter('date')(result.invoice.coverageStartDate, "dd/MM/yyyy");
vm.invoiceCoverageEndDate = $filter('date')(result.invoice.coverageEndDate, "dd/MM/yyyy");
vm.invoiceTotal = result.invoice.invoiceTotal;
defer.resolve();
}, function(error) {
console.log("Error: Could not fetch current pricing details", error);
defer.reject();
});
return defer.promise;
}
vm.getProjectedPricing = function(){
var defer = $q.defer();
$timeout(function(){
var selectedPricingMappingObj = dataStoreService.getItems('server');
selectedPricingMappingObj.forEach(function(item){
vm.totalProjectedPricingSum += parseFloat(item.selectedMapping.cost);
});
vm.totalProjectedPricingSum = vm.totalProjectedPricingSum.toFixed(2);
defer.resolve();
},1000);
return defer.promise;
}

Related

Pause function to wait for ajax request inside of a websocket

I have a WebSocket that adds people to a messaged list when they receive a message. I use a fetch to get information from the server to build the messaged list, but I cannot find an easy way to get the code to pause until the fetch completes. I cannot add await because the funciton is inside a websocket that isn't async. Does anyone have any suggestions on this?
socket.on('receive_message', function(data) {
let messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
console.log('messaged_user_li: '+messaged_user_li)
if (messaged_user_li == null) {
console.log('if fired')
//THIS is the fetch I want to pause the function until complete. Note that the await here does not work since the funciton isn't async.
await fetch('/get_messaged_user/'+from_user).then((response) => {
response.json().then((data2) => {
loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen);
});
});
messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
}
console.log('messaged_user_li: '+messaged_user_li)
let message_user = document.getElementById('message_user'+data['from_user']);
let message_target = document.getElementById("message_target"+data['from_user']);
if (messaged_user_li.classList.contains('active') == false) {
messaged_user_li.classList.add('flash-message');
}
if (message_user != null) {
data = `
<li class="clearfix">
<div class="message-data text-right">
<span class="message-data-time">just now</span>
</div>
<div class="message other-message float-right">`+data['message']+`</div>
</li>`;
message_target.innerHTML += data;
//Move scroller to bottom when message received
myDiv = message_user.querySelector(".chat-history");
myDiv.scrollTop = myDiv.scrollHeight;
}
});
Actually You can add async function in socket.on
socket.on('receive_message',async function(data) {

Displaying data on the page from the data gotten from parse

I have offers table and users table on parse server. I did a query for he offers table and it worked great (both console log and html - I had issues with async and the Q.promise helped). Now I'm trying to add two elements that are in the users table. I get it on the console, but not on the page. Here is what I have on the offers.service:
this.getAllOffers = function () {
var Q = $q.defer();
console.log('getAllOffers called');
//all offers filter is selected
this.allOffersFilter = false;
var offers = Parse.Object.extend("Offer");
var exchanges = Parse.Object.extend("Exchanges");
var users = Parse.Object.extend("User");
var query = new Parse.Query(offers);
var userQuery = new Parse.Query(users);
var results = [];
query.descending("createdAt");
query.limit(4);
userQuery.find().then(function(users) {
for (i = 0; i < users.length; i++) {
foundUsers = users[i];
query.find().then( function(offers){
for(i = 0; i < offers.length; i++){
found = offers[i];
var result = {};
result.date = found.get("createdAt");
result.price = found.get("price");
result.status = found.get("accepted");
result.lastName = foundUsers.get("lastName");
result.companyName = foundUsers.get("companyName");
console.log(result.companyName);
console.log(result.price);
}
});
results.push(result);
}
Q.resolve(results);
});
return Q.promise;
};
Then my HTML:
<!--List of offers-->
<div class="col-md-3">
<h4>List of offers</h4>
<div ng-if="offersList">
<div ng-repeat="offer in offersList">
<div class="offer card">
<div>{{offer.username}}</div>
<div>{{offer.companyName}}</div>
<div>{{offer.date}}</div>
<div>{{offer.price}}</div>
<div>{{offer.status}}</div>
</div>
</div>
</div>
<div ng-if="!(offersList)">There are no offers</div>
</div>
Then my component:
angular.module('offersPage')
.component('offersPage', {
templateUrl: 'pages/offers-page/offers-page.template.html',
controller: function(AuthService, PageService, OffersService,
$scope) {
// Functions for offers-page
// Check if user is logged in and verified on page load
AuthService.userLoggedin(function(loggedIn, verified) {
if(!verified) {
PageService.redirect('login');
}
});
this.$onInit = function() {
OffersService.getAllOffers().then(function(offersList) {
$scope.offersList = offersList;
});
}
}
});
THANKS IN ADVANCE !
You are resolving $q before results is populated, so, you list is empty.
I don't know about Parse server, but if userQuery.find().then is async, then need to move Q.resolve(results); inside it, or probably inside query.find().then.
When you do an ng-if in angularjs it literally takes out the element and when it puts it in it is as a child scope. To fix this you need to make sure and put $parent on any child element inside an ng-if. See below. Make sure to use track by $index to when you are doing repeats its good practice. Also notice you dont need to $parent anything in the repeat since it is referencing offerwhich is defined.
Code:
<div ng-if="offersList">
<div ng-repeat="offer in $parent.offersList track by $index">
<div class="offer card">
<div>{{offer.username}}</div>
<div>{{offer.companyName}}</div>
<div>{{offer.date}}</div>
<div>{{offer.price}}</div>
<div>{{offer.status}}</div>
</div>
</div>
</div>

Ng-src not calling function from scope

I have an ngRepeat that iterates through a list of objects and displays them. These objects do not contain any images, but the ID can be used to fetch the image. In the contents of the ngRepeat, I want to show the image that corresponds to the object, and so I call a function within an ngSource to grab the image corresponding with the current object. However, the ngSource fails to load on the actual page.
HTML:
<div class="list-group">
<a ng-click="courseSelected(course.course_id)" class="list-group-item" ng-repeat="course in searchResultData.courseList" id="searchResults">
<div>
<img ng-src="{{getLogoByCourse(course)}}"/>
<h4 class="list-group-item-heading">{{course.name}}, {{course.city}}, {{course.state}}</h4>
<p class="list-group-item-text miscData" ng-show="course.price"><span>Price: {{course.price}}</span></p>
<p class="list-group-item-text miscData">
<span>Phone number: {{course.phone}}</span><br/>
<span>Address: {{course.address}}</span><br/>
<span>Website: {{course.website}}</span>
</p>
</div>
</a>
</div>
Directive Snippet:
$scope.getLogoByCourse = function(course) {
console.log("Getting logo");
courseFactory.getLogoByID(course.course_id)
.then(function(result){
console.log("logo: ", result);
result = "app/images/directory/" + result.data[0];
return result;
}
);
}
$scope.getImageByCourse = function(course) {
courseFactory.getImagesByID(course.course_id)
.then(function(result){
console.log("Image: ", result);
result = "app/images/directory/" + result.data[0];
return result;
}
);
}
Contents of image tag on loaded page:
<img class="logo-img">
The problem is that $scope.getLogoByCourse doesn't actually return anything. It looks like you're trying return a value from a promise, but since that returns asynchronously, you'll always just get undefined as the return value from $scope.getLogoByCourse.
You may want to consider getting all of the URLs at once and loading them into an array. Then you can attach each URL in the ng-repeat using the $index variable.
$scope.logos = [];
for (var i = 0; i < searchResultData.courseList.length; i++) {
(function (j) {
var course = searchResultData.courseList[j];
courseFactory.getImagesByID(course.course_id)
.then(function (result) {
console.log("Image: ", result);
result = "app/images/directory/" + result.data[0];
$scope.logos[j] = result;
$scope.$apply();
}, function (error) {
$scope.logos[j] = null;
});
)(i);
}
Then in your template:
<img ng-src="{{ logos[$index] }}" />

Images in JSON AngularJS

I'm new to AngularJS, so sometimes when I do some mistake that is obvious, I still can't figure out what is going wrong with my code. So saying, here is my doubt:
HTML code:
<body ng-controller = "Ctrl">
<script id="Page6.html" type="text/ng-template">
<div class="list card" style="background-color: beige">
<div class="item item-icon-left">
<i class="icon ion-home"></i>
<input type="text" placeholder = "Enter display name" ng-model="user.nam">
</div>
<a ng-click = "saveedit(user)"<button class="button button-clear">SAVE DETAILS</button></a>
</div>
</script>
</body>
CONTROLLER.JS
.controller('Ctrl',function($scope,$rootScope,ContactService){
$rootScope.saveedit=function(user) {
ContactService.save({names: user.nam, image:"images.jpg"},ContactService.getid("Donkey"));
}
});
THIS IS THE SERVICE:
.service('ContactService', function () {
var items = [
{ id: 1, names: 'Dolphin', image: 'dolphin.jpg',}, { id: 2, names: 'Donkey', image: 'donkey.jpg'}, { id: 3, empid: 'FG2043', image: 'penguin.jpg'}];
var im = [{image: ''}];
var ctr=0;
var uid=3;
this.save = function (contact,id) {
ctr=0;
for (i=0;i<items.length;i++) {
if(items[i].id == id)
{
im[0].image= items[i].image;
ctr=100;
break;
}
}
uid = (uid+1);
contact.id = uid;
items.push(contact);
if (ctr==100 ) {
alert("in save putting the image");
items[contact.id].image = im[0].image; //doubt
alert("finished putting image");
}
}
//simply search items list for given id
//and returns the object if found
this.getid = function (name) {
for (i=0;i<items.length;i++) {
if (items[i].names == name) {
return (i+1);
}
}
}
//simply returns the items list
this.list = function () {
return items;
}
});
The problem I am facing is this: Everything works, except one thing. In ContactService, push() function, the line I have commented as //doubt is not getting executed.
The alert before it "in save putting the image" runs, but the alert "finished putting image" doesn't. What is the mistake there??
The problem here is that you're using the id's, which start at 1, to navigate in an array whose indexes start at 0.
To access the most recently pushed element, you should rather do :
items[contact.id - 1].image = im[0].image;
But you actually don't need to access the array : items[contact.id - 1] will return the object that you just pushed, and which is already referenced by variable contact, so you could just do :
contact.image = im[0].image;

How do I make json get to work in angularJS?

I followed information on this answer
But it doesn't work in my situation.
Chrome Inspector console says "ReferenceError: dataResponse is not defined"
maybe that is the problem?
I am trying to GET this JSON from url:
[{"app_id":1,"app_name":"oh yeeah","app_description":"desc","app_price":111,"is_activated":false,"video":"videolink"},{"app_id":2,"app_name":"oh yeaaaeah","app_description":"ewaewq","app_price":222,"is_activated":false,"video":"fuck off"},{"app_id":3,"app_name":"oh yeaaaeah","app_description":"ewaewq","app_price":333,"is_activated":false,"video":"fuck off"}]
This is my javascript code
var appstore = angular.module('appstore', []);
appstore.service('dataService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.getData = function(callbackFunc) {
$http({
method: 'GET',
url: '/administrator/components/com_apps/loadAppsJson.php'
}).success(function(data){
callbackFunc(data);
}).error(function(){
alert("error");
});
}
});
appstore.controller('app_Ctrl', function($scope, dataService) {
$scope.apps = [
{app_id:1, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
{app_id:2, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
{app_id:3, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
];
//$scope.apps = null;
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
});
console.log(dataResponse);
console.log($scope.apps);
//get images thumbs
for(app = 0; app <= $scope.apps.length-1; app++) {
$scope.apps[app].thumb = ("000" + $scope.apps[app].app_id).slice(-3);
}
//separate apps to columns
var columns = [];
for (var i = 0; i < $scope.apps.length; i++ ) {
if (i % 3 == 0) columns.push([]);
columns[columns.length-1].push($scope.apps[i]);
}
$scope.columns = columns;
});
My HTML view
<div ng-controller="app_Ctrl">
<div class="row"></div>
<div class="row">
<div class="row" ng-repeat="apps in columns">
<div id="app_id_{{ app.app_id }}" class="col-lg-4" ng-repeat="app in apps | filter:search">
<div class="thumbnail" ng-class="app.is_activated ? 'activated' : ''">
<!-- -->
<img ng-src="/images/apps/app_images/{{ app.thumb }}_thumb.jpg" alt="{{ app.app_name }}" title="{{ app.app_name }}">
<div class="caption">
<h3>{{ app.app_name }}</h3>
<p class="app_price">{{ app.app_price }} €</p>
<div style="clear:both;"></div>
<p class="app_card_description">{{ app.app_description | limitTo:100 }}...</p>
Info
Video <span class="glyphicon glyphicon-facetime-video"></span>
{{ app.is_activated ? 'Aktivované' : 'Aktivovať' }}
</div>
</div>
</div>
</div>
To elaborate on what #Mritunjay said in the comments; review this code with comments:
dataService.getData(
// this is your callback function which has an argument for dataResponse
// the dataResponse variable will only be defined within the Call back function
function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
// The Curly Brackets that follow mark the end of the callback handler method
});
// This log statement is not in the callback handler and there is no defined dataResponse variable which is probably why you got an error in the console
console.log(dataResponse);
You can fix this by moving the dataResponse log into the callback method, like this:
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
console.log(dataResponse);
});
There appear to be other problems with your code, in that you are trying to access $scope.apps before the data is returned; which will hinder your processing. Easiest approach would be to move that processing into the result handler:
// define $scope.columns outside of the result handler
$scope.columns = [];
// call to method in service
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
console.log(dataResponse);
// inside the result handler; you run this code after $scope.apps is defined:
for(app = 0; app <= $scope.apps.length-1; app++) {
$scope.apps[app].thumb = ("000" + $scope.apps[app].app_id).slice(-3);
}
//separate apps to columns
var columns = [];
for (var i = 0; i < $scope.apps.length; i++ ) {
if (i % 3 == 0) columns.push([]);
columns[columns.length-1].push($scope.apps[i]);
}
$scope.columns = columns;
});
That's what promises and asynchronous calls are all about.
console.log(dataResponse);
console.log($scope.apps);
The first one won't work because dataResource is a private variable and not part of the same scope you're trying to print.
The second one won't work either because that get's populated at future time (after X seconds), after the $http request is finished so it will only be availableat that point.
One way to do something after the object is populated is to use
$scope.$watch("apps", function (){
// do stuff
});

Categories