$scope doesn't get updated - javascript

The situation is like the code below. I want to change the value of $scope.pizzaList and $scope.selectedPizza when calling a function, but it seems not to change. I guess it is something with $scope's depth, but I'm not getting it. How to make these values get updated? Thanks in advance!
$scope.pizzaList = "some initial value";
$scope.selectedPizza = "some initial value";
$scope.setPizzaStatus = function (setStatus) {
$http.post('url1', { userID: $scope.pizzaioloID, pizzaID: $scope.selectedPizzaID, statusNum: setStatus }).
then(function(response) {
$http.get("url2")
.success(function (response) {
$scope.pizzaList = response;
});
$http.get("url3")
.success(function (response) {
$scope.selectedPizza = response[0];
});
}, function(response) {
console.log("Error");
});
}
$scope.pizzaList // doesn't get updated
$scope.selectedPizza // doesn't get updated

Try use $q.all():
$q.all([
$http.get("url2"),
$http.get("url3")
]).then(function(values){
$scope.pizzaList = values[0];
$scope.selectedPizza = values[1];//or values[1][0], It depends on data format;
}, function(error){
//error processing;
});
Don't forget add $q service to controller;

The way your code is written I would not expect the value of $scope.pizzaList to have changed between when you set it and when you evaluate it at the end. It will only be changed when you call the setPizzaStatus() method, and even then it will only change after the post to url1 and the get from url2 have returned from your server.

When you make a Get request, your Javascript code enters in "another task". So sometimes the current scope is not updated as expected. Try update the scope by calling "Apply", like this:
$http.get("url2")
.success(function (response) {
$scope.pizzaList = response;
$scope.$apply(); //or $scope.$applyAsync();
})

Related

Angular setting variable as object through http

I'm trying to set a variable as the data object returned from a http request in angular, but the variable never sets to even if it is in the $scope unless it is nested within the success function. For example, if I do this in the controller :
$scope.hello = [];
var getAppointmentsurl = './dbscripts/getAppointments.php';
$http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
$scope.hello = data;
});
console.log($scope.hello);
}
Hello is blank... so I set it up in services.js like this :
this.getCalendarData=function(){
var hello = [];
var getAppointmentsurl = './dbscripts/getAppointments.php';
$http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
hello = data;
});
return hello;
}
but still hello is blank. Am I missing something obvious?
edit --
this.getCalendarData=function(){
var getAppointmentsurl = './dbscripts/getAppointments.php';
return $http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
return data;
});
}
This is asynchronus call we have to return data like above.
To elaborate on Akash's correct answer, here's an example of how it should work.
In your view you should add logic to show the data only when hello exists. i.e. ng-if="hello"
controller:
ServiceName.getCalendarData().then(function(response) {
$scope.hello = response;
});
service:
this.getCalendarData = function() {
return $http.get('path/to/response/').success(function(data) {
return data;
});
}
As you put the api call as a method in the service, returning data from the service wont resolve yet, So in the controller the service return will only be a promise
serviceName.getCalendarData().then(function(data){
//Success data
},function(){});
Service code must return like the below code and here you will get the entire response object,
return $http({method: 'GET', url:getAppointmentsurl});
One other way to get the data directly resolved stripping of the other properties is returning from service like this,
return $http({method: 'GET', url:getAppointmentsurl}).success(function(data){
return data;
});

AngularJS scope.apply causes error

I have this function which fetches data from the DB and updates scope's values.
$scope.loadFromDB = function(){
console.log("loadFromDB function");
$http({
url: '/getData',
method: "GET",
})
.then(function(response) {
// success
var data = JSON.parse(response.data)
data = JSON.parse(data.data)
$scope.gridDataDayData = data.dayData;
$scope.gridDataKlasoveNames = data.klasoveNames;
$scope.gridDataNumOfRows = data.numOfRows;
$scope.$apply();
},
function(response) { // optional
// failed
console.log("get unsuccessful");
});
};
When I run this though I get
error: [$rootScope:inprog] http://errors.angularjs.org/1.4.3/$rootScope/inprog?p0=%24digest
I can not figure out how to fix this. Please help me.
Thank you
You don't need trigger the digest cycle because you are inside the angular life cycle... it is already running.
A manual triggering is needed when you are outside from the angular way: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Simply remove
var data = JSON.parse(response.data)
data = JSON.parse(data.data) and $scope.$apply()
and
$scope.$apply();

Correct way to callback function after all data have been retrieved from mongoDB in Express?

I'm writing a web application with a message feature.
A Conversation in my app is defined as between 2 distinct users. For example if Adam had just created a profile and sent 1 message to Jane and a 3 messages to Jack. He would have 2 Conversations but 4 Messages total.
In the following code on Express side, I'm attempting to retrieve all the Conversations for a given user in the database.
Once this is completed, send this data to the Angular controller side.
What's the correct way to do this in api.js below, taking into account that JS is asynchronous?
public/javascripts/main_controller.js
var mainApp = angular.module('myApp', ['ngRoute', 'btford.socket-io', 'xeditable']);
...
mainApp.controller('MessagesController', function($scope, $http, userSessionService, socket, focus){
console.log("MessagesController running");
$scope.messageDisplay = '';
$scope.username = userSessionService.getUserSession().username;
$http({
url: '/loadConversations',
// url: '/about',
method: "GET"
})
.success(function(response) {
console.log("success with loadConversations: ", response);
console.log(response[0].data);
});
....
})
routes/api.js:
....
router.route('/loadConversations')
.get(isAuthenticated, function(req, res) {
var result = [];
//Find Conversation with each and all distinct users
for(var i = 0; i < req.user.conversations.length; i++){
Conversation.findOne({'_id': req.user.conversations[i]}, function(err, conversation){
if(err){
console.log(err);
}
if(conversation){
var contactUsername = (conversation.initiatorUsername == req.user.username) ? conversation.responderUsername : conversation.initiatorUsername;
var lastMessage = conversation.messages[conversation.messages.length-1].content;
var dateOfMessage = conversation.messages[conversation.messages.length-1].date;
var resultJSON = {contactUsername: contactUsername,
lastMessage: lastMessage,
dateOfMessage: dateOfMessage};
result.push(resultJSON);
} else {
console.log("conversation not found!");
}
//Below is not working, where should I put res.json(result)?
// if(result.length == req.user.conversations.length){
// res.json(result);
// }
});
}
});
I'm not sure you even asked for this, but I think you should slightly rewrite your controller code.
In this example, a promise is being returned after$http(...). Instead of using .success I would recommend using .then. It's important to understand the difference between the two so I'll try to explain briefly. Here's the .success() method in angular source code:
promise.success = function(fn) {
// ...
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
and here is their official documentation
.success the same as the .then except success will take the first argument of the successful call result, so you wouldn't have to write out response.data. It's sort of convenient, but if you ever use caching or want to chain other promises off of this it becomes a pain in the ass. Here's my alternative for you.
$http({
url: '/loadConversations',
// url: '/about',
method: "GET"
})
.then(function(response) {
// this is your success callback
console.log("success with loadConversations: ", response);
console.log(response[0].data);
}, function(error) {
// if the service call doesnt return 200, we fire this
console.log(error);
});
This console log will be executed asynchronously in this implementation, and will be easier to maintain than using .success would be.
EDIT: for the routes/api.js, I think you just need to return the results array
if(result.length == req.user.conversations.length){
return result;
}

angular clearing object after pushing to array

So i have the following object:
$scope.post = {user_id: $sessionStorage.user.user.id};
with this i have the following:
<textarea class="form-control" ng-model="post.text" style="overflow:scroll;height:150px;max-height:150px"></textarea>
On submit i want to do the following action:
$scope.addPost = function () {
$http.post(api.getUrl('post', null),
{
post: $scope.post
}).success(function (response) {
$scope.post.id = response;
$scope.posts.push($scope.post);
});
$scope.post = {};
}
However when i clear $scope.post because of the databinding the post is empty. So my question is how can i avoid this?
You can make a copy using angular.copy() which will return a new object with no references to the original.
This also removes any hashkeys that angular scope has added to the object which can be problematic when server sees unrecognized keys
$scope.addPost = function () {
var postData = angular.copy($scope.post);
$http.post(api.getUrl('post', null),
{
post: postData
}).success(function (response) {
$scope.post.id = response;
$scope.posts.push(postData);
$scope.post = {}; //wait for success to clear
});
}
As already noted should wait for success to clear the live version
It's because of asynchronous nature of Ajax call - your $scope.post = {}; is executed earlier than success / error callbacks.
You should do clearing $scope.post inside the callback, not outside.

Ajax with external parameter Angular

I am new to angular and what I am willing to do is replace a piece of code I wrote in the past in jquery to angularjs.
The goal is to take a string from a span element, split it in two and pass the two strings as parameters in a GET request.
I am trying to learn best coding pratices and improving myself so any comments of any kind are always welcome.
Working Code in jquery:
//Get Song and Artists
setInterval(function () {
var data = $('#songPlaying').text();
var arr = data.split('-');
var artist = arr[0];
var songTitle = arr[1];
//Request Lyrics
$.get('lyricsRequester.php', { "song_author": artist, "song_name" : songTitle},
function(returnedData){
console.log(returnedData);
$('#refreshLyrics').html(returnedData);
});
},10000);
Code in Angular
var app = angular.module("myApp", []);
app.factory('lyricService', function($http) {
return {
getLyrics: function($scope) {
//$scope.songArr = $scope.currentSong.split('-'); <-- **undefined currentSong**
//$scope.artist = $scope.songArr[0];
//$scope.songTitle = $scope.songArr[1];
return
$http.get('/lyricsRequester.php', {
params: {
song_author: $scope.artist,
song_name: $scope.songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService, $interval) {
$interval(function(){
lyricService.getLyrics().then(function(lyrics) {
$scope.lyrics = lyrics; <-- **TypeError: Cannot read property 'then' of undefined**
console.log($scope.lyrics);
});
}, 10000);
});
index.html (just a part)
<div class="col-md-4" ng-controller="lyricsController">{{lyrics}}</div>
<div class="col-md-4"><h3><span id="currentSong" ng-model="currentSong"></span></h3><div>
You need to be careful with your return statement when used in conjunction with newlines, in these lines:
return
$http.get('/lyricsRequester.php',
If you don't, JS will automatically add a semicolon after your return, and the function will return undefined.
Move the $http.get statement to the same line as your return statement.
return $http.get('/lyricsRequester.php', ...
Refer to the following docs:
MDN return statement
Automatic Semicolon Insertion
As for your second issue, you $scope is not really something you inject into your services (like $http). Scopes are available for use in controllers.
You need to refactor your code a bit to make things work.
eg. Your getLyrics function can take a song as a parameter. Then in your controller, you call lyricsService.getLyrics(someSong). Scope access and manipulation are only done in your controller.
app.factory('lyricService', function($http) {
return {
getLyrics: function(song) {
var songArr = song.split('-');
var artist = songArr[0];
var songTitle = songArr[1];
return $http.get('/lyricsRequester.php', {
params: {
song_author: artist,
song_name: songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService) {
$scope.currentSong = 'Judas Priest - A Touch of Evil';
$interval(function(){
lyricService.getLyrics($scope.currentSong).then(function(lyrics) {
$scope.lyrics = lyrics;
console.log($scope.lyrics);
});
}, 10000);
});
You also have some other issues, like using ng-model on your span. ng-model is an angular directive that is used in conjunction with form elements (input, select etc.), not a span as you have. So you might want to change that into an input field.
$http does not use .then, it uses .success and .error. the line that you have where it says then is undefined, should be replaced with a success and error handler instead. Below is a sample from the docs:
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
See Link:
https://docs.angularjs.org/api/ng/service/$http

Categories