How to do $http.get wait response - angularjs

I have a trouble with $http.get. When my service.js access my MVC Controller, the $http.get don't wait for response. How can I do $http.get wait MVC Controller response? My code.
AngularJS Service:
angular.module("cadastroCliente").factory("clientesAPI", function($http) {
var getClientes = function () {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/");
};
return {
getClientes: getClientes
};
});
ControllerJS - Edited
angular.module("cadastroCliente").controller("cadastroClienteCtrl", function ($scope, $http, $q, $timeout, clientesAPI) {
$scope.app = "Pesquisar Clientes";
$scope.clientes = [];
var carregarClientes = function () {
clientesAPI.getClientes().success(function (data) {
$scope.clientes = data;
}).error(function (data, status) {
$scope.message = "Aconteceu um problema: " + data;
});
};
carregarClientes();
$scope.totalItems = $scope.clientes.length;
$scope.itemsPerPage = 2;
$scope.currentPage = 1;
$scope.maxSize = 5;
$scope.bigTotalItems = 175;
$scope.bigCurrentPage = 1;
$scope.pageCount = function () {
return Math.ceil($scope.clientes.length / $scope.itemsPerPage);
};
$scope.$watch('currentPage + itemsPerPage', function () {
var begin = (($scope.currentPage - 1) * $scope.itemsPerPage),
end = begin + $scope.itemsPerPage;
$scope.filteredClientes = $scope.clientes.slice(begin, end);
});
});
View:
<div class="table-responsive">
<table class="table table-bordered table-hover table-striped" id="clienteId">
<thead>
<tr>
<th class="col-lg-1">Código</th>
<th class="col-lg-7">Nome</th>
<th class="col-lg-3">Documento</th>
<th class="col-lg-1">Extrato</th>
</tr>
</thead>
<tbody ng-repeat="cliente in filteredClientes">
<tr>
<td>{{cliente.ClienteId}}</td>
<td>{{cliente.Nome}}</td>
<td>{{cliente.CpfCnpj}}</td>
<td style="cursor: pointer"><i class="glyphicon glyphicon-search"></i></td>
</tr>
</tbody>
</table>
</div>
<uib-pagination total-items="totalItems" items-per-page="itemsPerPage" ng-model="currentPage" ng-change="pageChanged()"></uib-pagination>

You can't set $scope.totalItems until the data has arrived which means you need to set it in the success callback where you assign $scope.clientes from the response data.
$http is an ajax request and the first A in ajax is for "asynchronous"
Note that success and error are deprecated and you should be using the recommendation in documentation to use the promise callbacks then() and catch()

I suggest that you put as much logic pertaining to the request in your factory as possible, including handling the response:
var getClientes = function () {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/")
.then(function(res) {
return res.data
});
};
Also don't forget that the get request returns the response object, and that you might need to return the data property on the response (if that's what you need).
You should also favor the .then() method on promises.
In es6 we can make this very precise and also catch any errors like so:
var getClientes = () => {
return $http.get("http://localhost:39732/Cadastro/BuscarClientes/")
.then(({data}) => data)
.catch(({data}) => console.log(data.message));
};
In your controller we can then do this:
var carregarClientes = function () {
clientesAPI.getClientes()
.then(function (data) {
$scope.clientes = data;
})
};
In es6:
var carregarClientes = () => {
clientesAPI.getClientes()
.then(data => $scope.clientes = data);
};
EDIT: Response to update code in OP
Unfortunately, anything pertaining to clientes should be done in the callback of the request:
var carregarClientes = function () {
clientesAPI.getClientes()
.then(function (data) {
$scope.clientes = data;
//Assign any other variables
})
};
The reason being, is that all your code in the controller referencing clientes is referencing the original empty array and not the data from the request. This is because it's outside of the call. Therefore, that code is run while the request is being made. If you place it inside the callback it has access to the returned data once it arrives.

Related

angularjs object list not binding to ng-repeat

I am using angular 1.5.5 with ui router 0.2.14. I have the view of employee list to be displayed. EmployeeList template is as follows:
<table class="employeeListContainer">
<tr ng-repeat="employee in employees">
<td>
<a ng-bind="employee.EmployeeId" class="employeeId"></a>
<!--ui-sref="employeeDetails{ employeeId: employee.EmployeeId }"-->
</td>
<td ng-bind="employee.FirstName"></td>
<td ng-bind="employee.LastName"></td>
</tr>
<tr>
<td colspan="3" class="paging">
<button ng-disabled="!IsPrevButtonEnabled" ng-click="prevPage()" class="prev-next"><</button>
<span ng-bind="PageNumber"></span>
<button ng-disabled="!IsNextButtonEnabled" ng-click="nextPage()" class="prev-next">></button>
</td>
</tr>
<tr>
<td colspan="3" class="paging">
<span ng-bind="ErrorMessage" ng-show="IsError"></span>
</td>
</tr>
</table>
I have configured the app as follows:
var app = angular.module('app', ['ui.router']);
app.config(function ($urlRouterProvider, $stateProvider, $httpProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('employeeList', {
url: '/List',
templateUrl: '../../Templates/EmployeeList.html',
controller: 'EmployeeListController',
resolve: {
employeeListRs: function (dataService) {
var employeeListRq = getEmployeeListRqInit();
return dataService.callApi('GetEmployees', 'post', [employeeListRq])
.then(function (data) { return data.data; });
},
employeeListRq: function(){
return getEmployeeListRqInit();
},
greeting: function ($q, $timeout) {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('Hello!');
}, 1000);
return deferred.promise;
}
}
});
$stateProvider.state('default', {
url: '/',
//templateUrl: '../../Templates/EmployeeList.html',
controller: 'defaultController'
});
$httpProvider.defaults.headers.common = {};
$httpProvider.defaults.headers.post = {};
$httpProvider.defaults.headers.put = {};
$httpProvider.defaults.headers.patch = {};
var getEmployeeListRqInit = function () {
return {
PageNumber: 1,
PageSize: 10,
SessionId: "123"
};
}
});
dataService is a service that is wrapper to the original $http.post call. Controller code is as follows:
app.controller('EmployeeListController', function ($scope, employeeListRq, employeeListRs, greeting) {
$scope.PageNumber = employeeListRq.PageNumber;
$scope.PageSize = employeeListRq.PageSize;
$scope.IsError = !employeeListRs.IsSuccess;
$scope.TotalCount = (employeeListRs.EmployeeList == null) ? 0 : employeeListRs.EmployeeList.TotalCount;
$scope.employees = (employeeListRs.EmployeeList == null) ? null : employeeListRs.EmployeeList.Employees;
if ($scope.employees = null) return 1;
var remainder = $scope.TotalCount % $scope.PageSize;
var pageNumber = Math.floor($scope.TotalCount / $scope.PageSize);
var lastPageNumber = remainder > 0 ? pageNumber + 1 : pageNumber;
$scope.IsNextButtonEnabled = $scope.PageNumber != lastPageNumber;
$scope.IsPrevButtonEnabled = $scope.PageNumber != 1;
$scope.IsLoading = false;
$scope.ErrorMessage = employeeListRs.IsSuccess ? '' : employeeListRs.ErrorMessage;
});
I see while debugging in chrome that $scope.employees is set to an array containing 10 objects all with proper fields and values. Also the IsPrevButtonEnabled and IsNextButtonEnabled are set perfectly. The binding is reflected on UI too, perfectly.
But I don't see the table containing employees list. Any suggestions on what I am missing?
Note: I don't get any error on console.
A few things you can try:
(1) Not saying yours is incorrect, but the preferred way to bind the data would be to use expressions. So, instead of this:
<td ng-bind="employee.FirstName"></td>
try this:
<td>{{employee.FirstName}}</td>
(2) This line looks suspicious in your controller:
if ($scope.employees = null) return 1;
It looks like you are assigning a null value to $scope.employees instead of checking for null. I/my teams try to use angular.isDefined($scope.employees) when we want to check for existence.
What are you trying to accomplish with that line?
(3) This looks a little different than how I use services and how I see others use them:
resolve: {
employeeListRs: function (dataService)
It looks to me that employeeListRs returns a promise.
What I typically do is call the service (my angular service which in turn calls the $http service) from inside the controller and then handle the response (both expected and error responses). From there I push the data into the controller's model. I haven't yet mixed service calls into my state machines - I let the controllers make the service calls.
(4) What is inside this css class - employeeListContainer? Could there be something there hiding your table? You might want to share your html and css as well.

angularjs: unable to get data from factory

I am unable to get my json data from factory and show it in table.
When I was using the $scope object, it was working fine but then I saw in official website that they don't recommend using $scope anymore. So I am using this parameter as suggested in demo examples. And now my code is not working anymore.
Please see my code and help me in this regard:
HTML:
<body ng-app="admin">
<div ng-controller="controller1 as ctrl1">
<div class="container">
<div class="row">
<div class="col-sm-12">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>IP</th>
<th>Time</th>
<th>No. of times</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="iprow in ctrl1.ipLog">
<td>{{iprow.ip}}</td>
<td>{{iprow.time}}</td>
<td>{{iprow.count}}
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="../framework/angular/angular.min.js"></script>
<script src="javascript/app.js"></script>
<script src="javascript/controllers/profileController.js"></script>
</body>
angular app.js
var admin = angular.module('admin', ['myController']);
admin.factory('simpleFactory', function ($http) {
var ipLog = [];
var factory = {};
factory.getIpLog = function () {
// Simple GET request example:
return $http({method: 'GET', url: 'mysql-query.php'}).
then(function successCallback(response) {
ipLog = response.data;
return ipLog;
}, function errorCallback(response) {
console.log(response.data);
return ipLog;
});
};
return factory;
});
angular profileController.js
var myController = angular.module('myController', []);
myController.controller('controller1', ['simpleFactory', function (factory) {
this.ipLog = [];
init();
function init() {
var myDataPromise = factory.getIpLog();
myDataPromise.then(function (result) {
// this is only run after getData() resolves
this.ipLog = result;
});
}
}]);
Your view:
<body ng-app="admin">
<div ng-controller="controller1">
...
<tr ng-repeat="iprow in ipLog">
...
</body>
factory code:
var admin = angular.module('admin', []);
admin.factory('simpleFactory', function ($http) {
var factory = {};
factory.getIpLog = function () {
// Simple GET request example:
return $http({method: 'GET', url: 'mysql-query.php'});
};
return factory;
});
Grab the factor module inside the controller.
Controller:
var myController = angular.module('myController', ['admin']);
myController.controller('controller1', ['simpleFactory', function (factory) {
$scope.ipLog = [];
function init() {
var myDataPromise = factory.getIpLog();
myDataPromise.then(function (result) {
$scope.ipLog = result.data;
});
}
init();
}]);
in app.js
factory.getIpLog = function () {
// Simple GET request example:
return $http({method: 'GET', url: 'mysql-query.php'});
};
in profileController.js
myController.controller('controller1', ['simpleFactory', function (factory) {
this.ipLog = [];
init();
function init() {
var myDataPromise = factory.getIpLog();
myDataPromise.then(function (success) {
this.ipLog = success;
}, function(error){
console.log(error);
});
}
}]);
In your profileController.js, this in this.ipLog=[] refers to myController but when when you are assigning value to this.ipLog=result, this here doesn't refer to myController. SO your this.ipLog is always an empty array.
try this code:
var myController = angular.module('myController', []);
myController.controller('controller1', ['simpleFactory', function (factory) {
var fixedThis=this;
fixedThis.ipLog = [];
init();
function init() {
var myDataPromise = factory.getIpLog();
myDataPromise.then(function (result) {
// this is only run after getData() resolves
fixedThis.ipLog = result;
});
}
}]);
Please try this code and tell if it works.

angularjs not updaing propeties when promises used

I am using service to get JSON object using promises. It is then converted into array and assign to a property which is in $scope object. The problem is that array is not getting updated or any properties inside promise then() method.
Controller
var searchController = function ($scope, $SPJSOMService) {
$scope.myName = "old name";
$scope.getUsers = function ($event) { //called on button click
$event.preventDefault();
var queryText = "test user";
$SPJSOMService.getUsers(queryText)
.then(function myfunction(users) {
$scope.userCollection = JSON.parse(JSON.stringify(users)).d.results;
// $scope.$apply(); this line throwing error. $rootScope in progress
$scope.myName = "new name"; //not getting updated
}, function (reason) {
alert(reason);
});
};
};
Service
var SPJSOMService = function ($q, $http, $rootScope) {
this.getUsers = function (userToSerach) {
var deferred = $q.defer();
$http({
url:'some url',
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
"content-Type": "application/json;odata=verbose"
}
})
.success(function (data, status, headers, config) {
deferred.resolve(data); //successfully return data
})
.error(function (data, status, headers, config) {
deferred.reject(data);
});
return deferred.promise;
};
};
Updated
index.html
<div ng-include="'Search.html'">
</div>
<div ng-include="'searchResults.html'"></div>
search.html
<div id="containerDiv" ng-controller="searchController as search">
<input class="button" type="button" ng-click="getUsers($event);" value="Search" id="btnSearch" />
</div>
searchResults.html
<div ng-controller="searchResultController as result">
<table >
<thead>
<tr>
<th>Name</th>
<th>EMail</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in userCollection">
<td>{{$index}} {{item.userName}}</td>
<td>{{$index}} {{item.userEmail}}</td>
</tr>
</tbody>
</table>
</div>
So search.html returns the data by calling the service and I am trying to pass it to the page namely searchResults.html which have its own controller.
$scope.users = $scope.userCollection;
Service class correctly returning data, but the problem is inside then(), which is not updating $scope.userCollection, hence UI is not getting updated.
Please help me out, what am I missing here.
In angular controllers can bind to the view in two ways:
"controller as"
through scope
It looks you are using the "controller as" flavor here...
<div ng-controller="searchResultController as result">
Refer to https://docs.angularjs.org/api/ng/directive/ngController for more information.
Change your controller code to:
var searchController = function ($scope, $SPJSOMService) {
//note use of "this" instead of "$Scope"
var _this = this;
_this.myName = "old name";
_this.getUsers = function ($event) { //called on button click
$event.preventDefault();
var queryText = "test user";
$SPJSOMService.getUsers(queryText)
.then(function myfunction(users) {
_this.userCollection = users.d.results; //You don't need JSON.parse not JSON.stringify
_this.myName = "new name";
}, function (reason) {
alert(reason);
});
};
};
and in your view use the controller "alias":
<div ng-controller="searchResultController as result">
...
<tr ng-repeat="item in result.userCollection">
...
You have to push it to the new array I think. Have you tried this in your then statement?
$scope.myName.push("new name");
It should put it in the existing array. Let me know if it works.

ng table with data from http request

Does anyone have a decent example with ng-table loading data upon a success callback from http service?
this.getData = function(){
tmp = this;
tmp.loading = true;
$http.post('/fetch',
$.param({
service_request: JSON.stringify(this.session)
}
)).success(function(data) {
tmp.loading = false;
tmp.tableData = data;
});
};
I would like to build the table from the tableData variable.
Thanks
I think this is one way of doing it.
Create a service to get the table data (I just picked the code from your :
app.factory('getTableData', function($http) {
return {
getData: function(session){
return $http.post('/fetch',
{
service_request: session
}))
}
}
})
Then you inject the service in your controller:
app.controller('DemoCtrl', function(getTableData, $scope, $window) {
$scope.loadTable = function(){
getTableData.getData(JSON.stringify($window.session)) //not sure where you have the session variable stored.
.sucess(function(data){
$scope.tableData = data;
})
.error(function(){
//do something
})
}
})
The HTML should be pretty straightforward:
<button ng-click="loadTable()">Load Table</button>
<table ng-table class="table">
<tr ng-repeat="row in tableData">
<td data-title="'Property1'">{{row.Property1}}</td>
<td data-title="'Property2'">{{row.Property2}}</td>
</tr>
</table>

why is this resource not updating the view after using $delete method

In my angular app I have a controller as follows:
function TemplateListControl($scope, TemplateService){
$scope.templates = TemplateService.get(); // Get objects from resource
// Delete Method
$scope.deleteTemplate = function(id){
$scope.templates.$delete({id: id});
}
}
Within the view I have a table thats bound to templates model. as follows:
<table ng-model="templates">
<thead>
<tr>
<th style="width:40%">Title</th>
<th style="width:40%">controls</th>
</tr>
<thead>
<tbody>
<tr ng-repeat="template in templates">
<td>
<!-- Link to template details page -->
<a href="#/template/[[template.id]]">
[[template.title]]
</a>
</td>
<td>
<!-- Link to template details page -->
<a class="btn btn-primary btn-small"
href="#/template/[[template.id]]">Edit
</a>
<!-- Link to delete this template -->
<a class="btn btn-primary btn-small"
ng-click="deleteTemplate(template.id)">Delete
</a>
</td>
</tr>
</tbody>
</table>
Now when I click on the delete link in the above template, It calls the deleteTemplate method and a successful DELETE call is made to the REST api. But the view does not get updated until it is refreshed and $scope.templates = TemplateService.get(); is initiated again. What am I doing wrong?
I prefer using promises instead of callback. Promises are the new way to handle asynchronous processes. You can inspect the result using a a promise right after it came back from the server.
//Controller
myApp.controller('MyController',
function MyController($scope, $log, myDataService) {
$scope.entities = myDataService.getAll();
$scope.removeEntity = function (entity) {
var promise = myDataService.deleteEntity(entity.id);
promise.then(
// success
function (response) {
$log.info(response);
if (response.status == true) {
$scope.entities.pop(entity);
}
},
// fail
function (response) {
$log.info(response);
// other logic goes here
}
);
};
});
//DataService
myApp.factory('myDataService', function ($log, $q, $resource) {
return {
getAll: function () {
var deferred = $q.defer();
$resource('/api/EntityController').query(
function (meetings) {
deferred.resolve(meetings);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
},
deleteEntity: function (entityId) {
var deferred = $q.defer();
$resource('/api/EntityController').delete({ id: entityId},
function (response) {
deferred.resolve(response);
},
function (response) {
deferred.reject(response);
});
return deferred.promise;
}
};
});
//Web API Controller
public class MeetingController : BaseApiController
{
// .... code omited
public OperationStatus Delete(int entityId)
{
return _repository.Delete(_repository.Single<MyEntity>(e => e.EntityID == entityId));
}
}
Note: $log, $q, $resource are built in services. Hope it helps :)
You also have to update client side so modify your source code as below
ng-click="deleteTemplate($index)">
$scope.delete = function ( idx ) {
var templateid = $scope.templates[idx];
API.Deletetemplate({ id: templateid.id }, function (success) {
$scope.templates.splice(idx, 1);
});
};
You could pass a callback function to $resource.$delete
function TemplateListControl($scope, TemplateService){
$scope.templates = TemplateService.get(); // Get objects from resource
// Delete Method
$scope.deleteTemplate = function(id){
TemplateService.$delete({id: id}, function(data) {
$scope.templates = data;
});
}
}
Attention
If your REST APIs delete function returns an array you have to set isArray: true in your Angular $resource to avoid a AngularJS $resource error - TypeError: Object # has no method 'push'
$resource(URL, {}, {
delete: {method:'DELETE', isArray: true}
});

Resources