Passing data from Service AngularJS - javascript

Dear all I am having trouble with the scope of my $scope or how should I put it.
I am retrieving the data from my Service successfully but I´m having trouble with accessing $scope.players and $scope.tournament and I think it has something to do with being inside the service call. If I console.out() inside the service call everything is just fine. How can I be able access the data which is inside of the service call.
Version 1:
Here console log simply states undefined.
.controller('SelectCtrl', ['$scope','$stateParams', '$location', '$window','playerService','tournamentService', function ($scope, $stateParams, $location, $window, playerService, tournamentService) {
init();
function init() {
playerService.getPlayers().then(function (data) {
$scope.players = [];
angular.forEach(data, function (player, index) {
$scope.players.push(player.info);
});
});
tournamentService.getTournaments().then(function (data) {
var result = data.filter(function (element) {
if (element.ID == $stateParams.id) {
return true;
} else {
return false;
}
});
$scope.tournament = result;
});
};
console.log($scope.tournament);//undefined
console.log($scope.players); //undefined
}
Version 2:,
Here console log simply states the Object {then: function, catch: function, finally: function} Which is not what I wan´t I want the data to be able to display it in my view.
.controller('SelectCtrl', ['$scope','$stateParams', '$location', '$window','playerService','tournamentService', function ($scope, $stateParams, $location, $window, playerService, tournamentService) {
init();
function init() {
$scope.players = playerService.getPlayers().then(function (data) {
$scope.players = [];
angular.forEach(data, function (player, index) {
$scope.players.push(player.info);
});
});
$scope.tournament = tournamentService.getTournaments().then(function (data) {
var result = data.filter(function (element) {
if (element.ID == $stateParams.id) {
return true;
} else {
return false;
}
});
$scope.tournament = result;
});
};
console.log($scope.tournament);//Object {then: function, catch: function, finally: function}
console.log($scope.players);//Object {then: function, catch: function, finally: function}
}
Your help is really appreciated!
The Services:
.factory('playerService', function ($http,$q) {
return {
getPlayers: function () {
//return the promise directly.
var deferred = $q.defer();
$http.get(webServiceUrl + 'api/Player/GetAllPlayers')
.success(function (data) {
//resolve the promise as the data
deferred.resolve(data);
}).error(function () {
deferred.reject();
});
return deferred.promise;
}
}
})
.factory('tournamentService', function ($http,$q) {
return {
getTournaments: function () {
//return the promise directly.
var deferred = $q.defer();
$http.get(webServiceUrl + 'api/Tournament/GetAllTournaments')
.success(function (data) {
//resolve the promise as the data
deferred.resolve(data);
}).error(function () {
deferred.reject();
});
return deferred.promise;
}
}
})
Part of the view:
<h1 style="display: inline-block; margin-left:15px;">Enter <i>{{tournament.Name}}</i></h1>
<div class="row">
<div class="selectinforow">
<div class="col-xs-2 selectinfo">
<span>{{tournament.EntryFee}}$</span></br>
<span>Entry Fee</span>
</div>
<div class="col-xs-2 selectinfo">
<span>{{tournament.Entries}}</span></br>
<span>Entries</span>
</div>
<div class="col-xs-2 selectinfo">
<span>{{tournament.Size}}</span></br>
<span>Max Size</span>
</div>
<div class="col-xs-2 selectinfo">
<span>{{tournament.StartTime}}</span></br>
<span>Start Date</span>
</div>
<div class="col-xs-2 selectinfo">
<span>{{tournament.Entryfee*tournament.Entries}}$</span></br>
<span>Winnings</span>
</div>
</div>
</div>

So if you read your code carefully you will notice you are using a promise on the following line:
tournamentService.getTournaments().then(function (data) {
// [your data is set here - AFTER the service call runs]
}
// [your print is here - run BEFORE service call runs]
The key to the "then" statement is it isn't executed right away, but is instead run when data is returned from the service call. In other words, you have your print in the wrong spot - I would expect the values to be undefined there. If you move the console.log statements into the promise (then) - I would expect to see the valid values. You can also put a break point in the browser debugger to see the values in the "then" function if you want to validate that things are working. Hope this puts you on the right track!
EDIT
Once the promise completes, angular automatically updates the view. Lets say you have the following in your view (just an example):
<h1 ng-bind="tournament.Title">Default Text</h1>
When the view/page loads you will see "Default Text". After the promise completes, if a tournament has been loaded, angular will automatically update the "h1" to now have the Title for that tournament. This happens because angular automatically runs an "$apply()" after a promise completes.

Your code is executed before the promise response.
If you need to code "procedurally", you should $watch the scope variable as below to detect any changes.
For example:
$scope.$watch('tournament', function() {
console.log($scope.tournament);
}, true);

Related

unable to use $q and promise

I want to use the variable StatusASof to display data in the inserthtml function as below.
App.controller("SS_Ctrl", function ($scope, $http, $location, $window, $sce, $q) {
var ShiftDetails = [];
function getMAStatusASof(Id) {
var defer = $q.defer();
$http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
}).then(function successCallback(response) {
StatusASof = response.data;
alert("getMAStatusASof : " + StatusASof); --> Got data from API here in this alert.
defer.resolve(response);
}, function errorCallback(response) {});
}
function insertHtml(dates, ShiftDetails, Id) {
// var promise = getMAStatusASof(Id); promise.then(
var defer = $q.defer();
getMAStatusASof(Id);
alert(StatusASof); --> alert says empty here
defer.resolve();
var Content;
Content = '<table class="clsTable"> <tr> <td rowspan="2">Cases ' + $scope.StatusASof + ' </td> <td rowspan="2">Total</td> ';
for (var i = 0; i <= 6; i++) {
if (i == daySeq - 1) {
Content = Content + '<td colspan="3" style="background-color:red"> {{dates[ ' + i + ']}} </td> ';
}
}
}
but $scope.StatusASof is undefined while displaying the result. Looks like $q.defer is not working for me.
How can I continue the execution of code after getting data only from the getMAStatusASof(Id);
Can somebody help here.
Update
you need to return defer.promise;
function getMAStatusASof(Id) {
var defer = $q.defer();
$http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
}).then(function successCallback(response) {
StatusASof = response.data;
alert("getMAStatusASof : " + StatusASof); --> Got data from API here in this alert.
defer.resolve(StatusASof);
}, function errorCallback(response) {
deferred.reject(false);
});
return defer.promise;
}
and the you can use this function like :
getMAStatusASof(Id).then(function(res){
if(res){
$scope.StatusASof = res;
}
})
No need to use $q.defer() here...
Just do
function getMAStatusASof(Id) {
return $http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
})
.then(function successCallback(response) {
return response.data;
})
.catch(function errorCallback(response) {
return null; //Effectively swallow the error an return null instead.
});
}
Then, use
getMAStatusASof(Id).then(function(result) {
console.log(result);
});
//No .catch, as we've caught all possible errors in the getMAStatusASof function
If you really would like to use $q.defer(), then the function would need to return defer.promise, like Jazib mentioned.
But as I said, since $http already returns a promise, doing the whole $q.defer() + return defer.promise thing is superfluous.
Instead, use that construction only when the thing you need to wrap isn't returning a promise on its own. E.g when you open a bootbox modal and want to be informed of when the user clicked the close button
function notifyMeOfClosing() {
var deferred = $q.defer();
bootbox.confirm("This is the default confirm!", function(result){
if(result) {
deferred.resolve();
}
else {
deferred.reject();
}
});
return deferred.promise;
}
Unable to update #Daniël Teunkens post with following code(from "}" to ")"). So adding as new answer.
getMAStatusASof(Id).then(function(result) {
// your code here. your HTML content.
....
console.log(result);
})
It will work hopefully.

Call a function to update some variables

I have a collection lines i get from ajax call and i use ng-repeat and for each item line the property line.date need some modification before to be displed
the problem is that I don't know how to call the function to make the modification ?
I try data-ng-init and ng-init the function is called but the variables are not updated !
Html code
<div ng-controller="lineController" data-ng-init="loadLines()">
<div ng-repeat="line in lines">
...
<div data-ng-init="loadDates(line.date)">
...
{{ leftDays}}
</div>
</div>
</div>
Js code :
var app = angular.module("searchModule", []);
app.controller("lineController", function ($scope, $http)
{
// LOAD LINES AJAX
$scope.loadLines = function () {
$http({method: 'GET', url: '..'}).success(function(data) {
angular.forEach(data.content, function(item, i) {
$scope.lines.push(item);
});
});
};
$scope.loadDates = function (date) {
// complex updating of date variable
....
$scope.leftDays = ...;
};
});
Why not manage each line in you angular.forEach?
Like this :
$http({method: 'GET', url: '..'}).success(function(data) {
angular.forEach(data.content, function(item, i) {
//Do stuff to item here before pushing to $scope.lines
//item.date = new Date(item.date) blah blah
$scope.lines.push(item);
});
});
If, you want line.date to be displayed in a different way in you html, and dont want to modify the actual data, why not use a $filter for that?
Like this :
<span ng-repeat="line in lines">{{line.date|yourCustomFilter}}</span>
I think you don't need to do this in this way. You can do this as follows;
$scope.loadLines = function () {
$http({method: 'GET', url: '..'}).success(function(data) {
angular.forEach(data.content, function(item, i) {
$scope.lines.push(item);
});
$scope.lines.map(function(line) {
// here is to modify your lines, with a custom
line.date = $scope.loadDates(line.date);
return line;
})
});
};
By the way, I think you can modify your ajax loading function with this;
$scope.loadLines = function () {
$http({method: 'GET', url: '..'}).success(function(data) {
$scope.lines = data.content.map(function(line) {
// here is to modify your lines, with a custom
line.date = $scope.loadDates(line.date);
return line;
})
});
};
And if you don't need to use loadDates function in view, you don't need to set this function to $scope. You can set this function with just var. Then you can use that function like; loadDates(...) instead of $scope.loadDates(...).
If you don't have to update that $scope.lines variable, you don't need to use .map for this. You can update that function as follows;
$scope.loadLines = function () {
$http({method: 'GET', url: '..'}).success(function(data) {
$scope.lines = data.content;
angular.forEach($scope.lines, function(line) {
// here is to modify your lines, with a custom
$scope.loadDates(line.date);
})
});
};

update scope variable on submit and update view in angular js

I'm creating my first web app in angularjs and can't get the page to update with new values once the user submits text/numbers in an input box.
I'm using Java8, MongoDB, angularJS and twitter bootstrap
HTML:
<td>
<input type="text" class="form-control" placeholder="enter bugnumber" data-ng-model="auditdata.newbugnumber">
<h4>Bug Numbers</h4>
{{a}}
</td>
<td>
<button type="submit" data-ng-click="add(auditdata)" class="btn btn-danger" >Save</button>
</td>
In the HTML above i take input from user in ng-model=auditadata.newbugnumber but on server side it sill gets update in the bugnumber filed. The newbugnumber field is acting like a temp variable which is just used to send the new data to the server. The reason for using the temp variable is to avoid two way binding in angularjs.
I tried using $apply(), $watch and digest in the JS below but can't get the value to be updated in the view. The only way the data gets update in view is when i reload the page which is not an option
app.controller('listCtrl', function ($scope, $http,$route) {
$scope.isCollapsed = true;
$http.get('/api/v1/result').success(function (data) {
$scope.audit = data;
}).error(function (data, status) {
console.log('Error ' + data);
})
$scope.add= function(bugInfo) {
$http.post('/api/v1/result/updateBug', bugInfo).success(function (data) {
bugInfo.newbugnumber='';
console.log('audit data updated');
}).error(function (data, status) {
console.log('Error ' + data);
}
};
});
Update function on server side
public void updateAuditData(String body) {
Result updateAudit = new Gson().fromJson(body, Result.class);
audit.update(
new BasicDBObject("_id", new ObjectId(updateAudit.getId())),
new BasicDBObject("$push", new BasicDBObject().append("bugnumber",
updateAudit.getNewbugnumber())));
}
how bugnumber filed in collection looks like
> db.audit.find({"_id" : ObjectId("5696e2cee4b05e970b5a0a68")})
{
"bugnumber" : [
"789000",
"1212"
]
}
Based on your comment, do the following:
Place all your $http handling i a service or factory. This is good practice and makes reuse and testing easier
app.factory('AuditService', function($http) {
var get = function() {
return $http.get("/api/...") // returns a promise
};
var add = function() {
return $http.post("/api/...") // returns a promise
};
return {
get: get,
add: add
}
});
And then in your controller
// note the import of AuditService
app.controller('listCtrl', function ($scope, $http,$route, AuditService) {
// other code here
// If insert is successful, then update $scope by calling the newly updated collection.
// (this is chaining the events using then())
$scope.add = function(bugInfo) {
AuditService.add().then(
function(){ // successcallback
AuditService.get().then(
function(data){ // success
$scope.audit = data;
},
function(){ // error
console.log('error reading data ' + error)
})
},
function(error){ // errorcallback
console.log('error inserting data ' + error)
})
});
The same approach can be applied to all CRUD operations

How to pass value from ng-click (AngularJS) to Laravel?

How to get value from ng-click and send to laravel for query?
//.html
<div ng-controller="recipientsController">
<div ng-repeat="recipient in recipients | orderBy:'-created_at'" ng-click="select(recipient.id)">
<p class="recipientname">{{ recipient.name }}</p>
</div>
</div>
//xxController.js
$scope.select = function() {
Comment.get()
.success(function(data) {
$scope.comments = data;
$scope.loading = false;
});
}
//xxService.js
get:function(){
var comments = $http.get('api/comments');
return comments;
},
//xxController.php [laravel]
public function index()
{
$comments = DB::table('c')
->join('u', 'c.id', '=', 'u.id')
->select('u.id', 'u.name', 'c.comments', 'c.created_at')
->where('u.id','=', Auth::user()->id)
->orWhere('u.id','=', **39 => this part has to be from ng-click value**)
->orderBy('c.created_at','asc')
->get();
return Response::json($comments);
}
You have passing the recipient.id parameter in your ng-click function but you did't retrieve the parameter in your js function
you need to retrieve the parameter
$scope.select = function(**id**) {
var selectedId=id;//you can check here
Comment.get()
.success(function(data) {
$scope.comments = data;
$scope.loading = false;
});
}
For passing data with $http.get method, there is second argument for [config] you can use that.
see: https://docs.angularjs.org/api/ng/service/$http#get for more reference about get method

Angular Controller Loading Before Data Is Loaded

I am writing a simple app that loads movie info from an API. After this loading, I am attempting to use Angular to display the movies in a simple list view. I am correctly loading the movies, but it seems like the angular controller is created and sends the movie array to the view before the movie array is populated. I am unsure how to get around this.
var movieList = [];
var app = angular.module('top250', []);
// immediately make a call to the server to get data (array of movies from text file)
$.post('/', {}, function(data) {
init(data);
});
app.controller('MovieController', function() {
// should be setting this.movies to an array of 250 movies
this.movies = movieList;
});
function init(data) {
// cycle through array, use title to retrieve movie object, add to array to be sent to view
$.each(data, function(index, value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
$.getJSON(url, function(data) {
console.log('in get json', data);
var movieObj = data;
storeMovie(movieObj);
});
});
}
function storeMovie(movieObj) {
movieList.push(movieObj);
}
And my HTML (although I'm certain this isn't the problem:
<body ng-controller="MovieController as MovieDB">
<div class="row">
<div class="large-12 columns">
<h1>IMDB Top 250 List</h1>
</div>
</div>
<div class="row">
<div class="large-12 columns" id="movie-list">
<div class="list-group-item" ng-repeat="movie in MovieDB.movies">
<h3>{{movie.Title}} <em class="pull-right">{{movie.Plot}}</em></h3>
</div>
</div>
<script src="js/foundation.min.js"></script>
<script>
$(document).foundation();
</script>
</body>
First I transformed your ajax calls to an angular factory:
app.factory('MoviesService', function($http, $q) {
function getMovie(value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
return $http.get(url).then(function(res){ return res.data; });
}
return $http.post('/').then(function(res) {
return $q.all(res.data.map(getMovie));
});
});
Then I can consume it like so:
app.controller('MovieController', function(MoviesService) {
var self = this;
MoviesService.then(function(movies) {
self.movies = movies;
});
});
don't use jquery
use angular $http or $resource
using $http, you set scope var to the data inside promise, and life will be good
You need to wait for you init method to complete:
function init(data, complete) {
$.each(data, function(index, value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
$.getJSON(url, function(data) {
console.log('in get json', data);
var movieObj = data;
storeMovie(movieObj);
}).always(function(){ // count competed ajax calls,
// regardless if they succeed or fail
if(index === data.length -1)
complete(); // call callback when all calls are done
});
});
}
Now you can do this:
app.controller('MovieController', function() {
$.post('/', {}, function(data) {
init(data, function(){
this.movies = movieList;
});
});
});
Personally I would just keep the movieList inside of the init method and send it with the callback when you're done, but that's just a preference.

Categories

Resources