I have a controller in the provided module that uses data from either a JSON file or an API call. The JSON file version GetActionItems2() works perfectly. Unfortunately, I cannot get the GetActionItems() to work just like its counterpart function (the JSON version). I am using deferred promises and Angular DataTables. No errors in console, but my table has no data in the page.
How do I resolve this?
Controller
angular.module('Action').controller('ActionController', ['$http', '$resource', '$scope', '$state', '$timeout', '$q', 'DTOptionsBuilder', function($http, $resource, $scope, $state, $timeout, $q, DTOptionsBuilder){
$scope.actionitems = {};
function GetActionItems2()
{
return $resource('actionitems.json').query().$promise;
}
function GetActionItems() {
var defer = $q.defer();
$http.get('api/actionitems')
.then(function(response){
defer.resolve(response);
});
return defer.promise;
}
$scope.init = function(){
var vm = this;
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
var defer = $q.defer();
GetActionItems().then(function(result) {
$scope.actionitems = result;
defer.resolve(result);
});
return defer.promise;
})
.withPaginationType('full_numbers')
//.withOption('drawCallback', reload)
.withDisplayLength(10)
//.withOption('order', [1, 'desc'])
.withOption('scrollY', 500)
.withOption('scrollX', '100%')
.withDOM('lftrpi')
.withScroller();
}
}]);
Template
<div ng-init="init()" ng-controller="ActionController">
ActionItems
<table id="actionitems" class="row-border hover action" datatable="" dt-options="dtOptions">
<thead>
<tr>
<th>
ID
</th>
<th>
Action Item Title
</th>
<th>
Criticality
</th>
<th>
Assignor
</th>
<th>
Owner
</th>
<th>
Alt Owner
</th>
<th>
Approver
</th>
<th>
Assigned Date
</th>
<th>
DueDate
</th>
<th>
ECD
</th>
<th>
Completion Date
</th>
<th>
Closed Date
</th>
<th>
Team
</th>
<th>
Meeting
</th>
<th>
Phase
</th>
<th>
Source
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="actionitem in actionitems">
<td>{{actionitem.ActionItemID}}</td>
<td>{{actionitem.Title}}</td>
<td>{{actionitem.Criticality}}</td>
<td>{{actionitem.Assignor}}</td>
<td>{{actionitem.Owner}}</td>
<td>{{actionitem.AltOwner}}</td>
<td>{{actionitem.Approver}}</td>
<td>{{actionitem.AssignedDate}}</td>
<td>{{actionitem.DueDate}}</td>
<td>{{actionitem.ECD}}</td>
<td>{{actionitem.CompletionDate}}</td>
<td>{{actionitem.ClosedDate}}</td>
</tbody>
</table>
</div>
Returning $http.get('api/actionitems').then(function(result) to fromFnPromise with an embedded return result.data inside of function(result) resolved the issue and avoided usage of the deferred Anti-pattern.
$scope.init = function() {
var vm = this;
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
return $http.get('api/actionitems').then(function(result) {
$.each(result.data, function(key, actionitem) {
result.data[key] = [
actionitem.actionitemid,
actionitem.actionitemtitle,
actionitem.criticality,
actionitem.assignor,
actionitem.owner,
actionitem.altowner,
actionitem.approver,
actionitem.assigneddate,
actionitem.duedate,
actionitem.ecd,
actionitem.completiondate,
actionitem.closeddate
];
});
$scope.actionitems = result.data;
return result.data;
});
})
.withPaginationType('full_numbers')
//.withOption('drawCallback', reload)
.withDisplayLength(10)
//.withOption('order', [1, 'desc'])
.withOption('scrollY', 500)
.withOption('scrollX', '100%')
.withDOM('lftrpi')
.withScroller();
}
If this asynchronous processing comes from a third party, you may need to manually trigger a data update for that controller.
// ...
$scope.init = function(){
var vm = this;
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
var defer = $q.defer();
GetActionItems().then(function(result) {
$scope.actionitems = result;
// here trigger a data update
$scope.$digest();
defer.resolve(result);
});
return defer.promise;
})
// ...
The $digest method is to manually trigger the change detection of the controller.
SEE EDIT BELOW
My initial solution was a two step process to get data into my table.
I introduced a custom apply function then a getRes function which then populated array of actionitems with individual actionitems with only an array of values to be sent off to datatables and asign to a $scope.acionitems. Otherwise I would get an alert from datatables that regarding the format of each item being in JSON format with corresponding keys.
angular.module('Action').controller('ActionController', ['$http', '$resource', '$scope', '$state', '$timeout', '$q', 'DTOptionsBuilder', function($http, $resource, $scope, $state, $timeout, $q, DTOptionsBuilder){
$scope.actionitems = {};
function GetActionItems2()
{
return $resource('actionitems.json').query().$promise;
}
function GetActionItems() {
var defer = $q.defer();
$http.get('api/actionitems')
.then(function(response){
defer.resolve(response);
});
return defer.promise;
}
var getRes = function(res){
$.each(res, function(key, actionitem){
res[key] = [
actionitem.actionitemid,
actionitem.actionitemtitle,
actionitem.criticality,
actionitem.assignor,
actionitem.owner,
actionitem.altowner,
actionitem.approver,
actionitem.assigneddate,
actionitem.duedate,
actionitem.ecd,
actionitem.completiondate,
actionitem.closeddate
];
});
$scope.actionitems = res;
}
function apply(scope, fn, res) {
(scope.$$phase || scope.$root.$$phase) ?
fn(res) :
scope.$apply(fn(res));
}
$scope.init = function(){
var vm = this;
vm.dtOptions = DTOptionsBuilder.fromFnPromise(function() {
var defer = $q.defer();
GetActionItems().then(function(result){
apply($scope, getRes, result.data);
defer.resolve(result.data);
});
return defer.promise;
})
.withPaginationType('full_numbers')
//.withOption('drawCallback', reload)
.withDisplayLength(10)
//.withOption('order', [1, 'desc'])
.withOption('scrollY', 500)
.withOption('scrollX', '100%')
.withDOM('lftrpi')
.withScroller();
}
}]);
EDIT
I simplified the code and used a resolve on the actual data attribute of result not object itself which produced data in my table.
The following modification based on my Anti-Pattern usage is the best I could get so far to achieve the same results.
angular.module('Action').controller('ActionController', ['$http', '$resource', '$scope', '$state', '$timeout', '$q', 'DTOptionsBuilder', function($http, $resource, $scope, $state, $timeout, $q, DTOptionsBuilder){
$scope.actionitems = {};
function GetActionItems2()
{
return $resource('actionitems.json').query().$promise;
}
function GetActionItems() {
var defer = $q.defer();
var res = $http.get('api/actionitems').then(function(result){
var data = result.data;
$.each(data, function(key, actionitem){
data[key] = [
actionitem.actionitemid,
actionitem.actionitemtitle,
actionitem.criticality,
actionitem.assignor,
actionitem.owner,
actionitem.altowner,
actionitem.approver,
actionitem.assigneddate,
actionitem.duedate,
actionitem.ecd,
actionitem.completiondate,
actionitem.closeddate
];
});
$scope.actionitems = data;
defer.resolve(data);
});
return defer.promise;
}
$scope.init = function(){
var vm = this;
vm.dtOptions = DTOptionsBuilder
.fromFnPromise(GetActionItems)
.withPaginationType('full_numbers')
//.withOption('drawCallback', reload)
.withDisplayLength(10)
//.withOption('order', [1, 'desc'])
.withOption('scrollY', 500)
.withOption('scrollX', '100%')
.withDOM('lftrpi')
.withScroller();
}
}]);
Related
I need to do four $http.get call and I need to send returned $scope variable to the one HTML to print all the information. In the moment I have the next code in JS:
(function (ng) {
var mod = ng.module("serviciosAdminModule");
mod.controller('serviciosAdminCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get("api/hoteles").then(function (response) {
$scope.serviciosHotelesRecords = response.data;
});
}]);
mod.controller('serviciosAdminCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get("api/paseos").then(function (response) {
$scope.serviciosPaseosRecords = response.data;
});
}]);
mod.controller('serviciosAdminCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get("api/aseos").then(function (response) {
$scope.serviciosAseosRecords = response.data;
});
}]);
mod.controller('serviciosAdminCtrl', ['$scope', '$http',
function ($scope, $http) {
$http.get("api/entrenamientos").then(function (response) {
$scope.serviciosEntrenamientosRecords = response.data;
});
}]);
})(window.angular);
In the HTML I have the next:
<tr ng-repeat="servicio in serviciosAdminRecords">
<td id="{{$index}}-id" class="parrafoscards" style="font-size: 16px">
<a ui-sref="servicioAdminDetail({servicioId: servicio.id})">{{servicio.id}}</a>
</td>...
In the HTML I ask for serviciosAdminRecords that is the $scope variable where I want to put all the .get data
You probably need to chain the promises in order to add all the responses together into the one array. Get rid of all the different controllers - they will have separate scopes - and just have the one controller, making sure it is being used in the view by using ng-controller='serviciosAdminCtrl' as an attribute in your view.
mod.controller('serviciosAdminCtrl', ['$scope', '$http',
function ($scope, $http) {
$scope.serviciosAseosRecords = [];
$http.get("api/hoteles").then(function (response) {
$scope.serviciosAseosRecords = $scope.serviciosAseosRecords.concat(response.data);
$http.get("api/paseos").then(function (response) {
$scope.serviciosAseosRecords = $scope.serviciosAseosRecords.concat(response.data);
$http.get("api/aseos").then(function (response) {
$scope.serviciosAseosRecords = $scope.serviciosAseosRecords.concat(response.data);
$http.get("api/entrenamientos").then(function (response) {
$scope.serviciosAseosRecords = $scope.serviciosAseosRecords.concat(response.data);
});
});
});
});
}]);
If your response.data is an array of object then initialize serviciosPaseosRecords as array.
$scope.serviciosHotelesRecords = [];
instead of assigning response.data to serviciosPaseosRecords
$scope.serviciosAseosRecords = response.data
Concat response.data with existing serviciosAseosRecords array.
$scope.serviciosAseosRecords = $scope.serviciosAseosRecords.concat(response.data)
I want to increment the data via Button click or scroll.
I have a function which loads the data after button click loadDataQueryDB(param, param, param). With that, I am passing data to MongoDB query.
Well how can I increment my var limit = 50; + 5; after each button click?
Node.js
router.get('/load', function(req, res) {
var skip = 0;
var limit = 50;
var place_val = req.query.place;
var category_val = req.query.category;
var specCategory_val = req.query.specCategory;
if(category_val, specCategory_val, place_val){
Experiences
.find({category : category_val, city:place_val})
.lean()
.skip(skip)
.limit(limit)
.exec(function(err, docs_accommo) {
res.send(docs_accommo);
console.log("First");
});
}
});
Angular.js
app.controller('loadData', ['$scope', '$http', '$window', '$upload', '$rootScope',
function($scope, $http, $window, $upload, $rootScope) {
$scope.loadDataQueryDB = function(place_value, category_value, specCategory_value){
console.log(place_value);
console.log(category_value);
console.log(specCategory_value);
$scope.datafront = [];
var options = {
place : place_value,
category: category_value,
specCategory : specCategory_value
};
$http.get('/load',
{params: options})
.success(function(data) {
$scope.datafront = data;
});
};
});
HTML
<div ng-click="loadDataQueryDB(place, category, specCategory)">
<div ng-repeat="x in datafront | limitTo:? track by x._id" ng-cloak>
{{x}}
</div>
</div>
<button class="btn btn-default" style="float:right; margin-bottom:20px;"/>
Something like the code below, using services and http promises, the data returned form the server its on promise.data.
app.controller('loadData', ['$scope', '$http', '$window', '$upload', '$rootScope','dataService',
function($scope, $http, $window, $upload, $rootScope, dataService) {
$scope.loadDataQueryDB = function(place_value, category_value, specCategory_value){
console.log(place_value);
console.log(category_value);
console.log(specCategory_value);
$scope.datafront = [];
var params = {
place : place_value,
category: category_value,
specCategory : specCategory_value
skip : $scope.skip,
limit : $scope.limit
}
dataService.getData(params).then(function(promise){
$scope.dataFront = promise.data;
//Increment the limit by ten
$scope.limit =+ 10;
})
};
});
app.module('dataService').factory('dataService',['$http',function ($http) {
var service = {};
factory.getData= function (params) {
var promise = $http({
method: 'GET',
url: '/load',
params: params
});
return promise;
}
return service;
}]);
You should have only the gets that return views on the router and the rest of the get and post calls on a service, at least I develop like that and its more confortable.
I'm trying to intgrate Angular JS with an existing Spring MVC project.
I had à problem calling a Spring controller from the Angular JS controller.
This is my app.js:
'use strict';
var AdminApp = angular.module('AdminApp',[]);
And the service:
'use strict';
AdminApp.factory('AdminService', ['$http', '$q', function($http, $q) {
return {
fetchAllTerminals: function() {
return $http.get('http://localhost:8081/crmCTI/admin/terminal')
.success(function(response) {
console.log('Service');
return response.data;
})
.error(function(errResponse) {
console.error('Error while fetching terminals');
return $q.reject(errResponse);
});
}
};
}]);
and the controller:
'use strict';
AdminApp.controller('AdminController', ['$scope', 'AdminService', function($scope, AdminService) {
var self = this;
self.terminal={id:'',connectedUser:'',type:'',state:''};
self.terminals=[];
self.fetchAllTerminals = function() {
console.log('Controller');
AdminService.fetchAllTerminals()
.success(function() {
self.terminals = d;
})
.error(function() {
console.error('Error while fetching Terminals');
});
};
self.reset = function() {
self.terminal = {id : null, connectedUser : '', type : '', state : ''};
};
}]);
The JSP I'm using to display the data is:
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head></head>
<body ng-app="AdminApp" ng-init="names=['Jani','Hege','Kai']">
<div ng-controller="AdminController as adminController">
<table>
<thead>
<tr>
<th>Id</th>
<th>Login</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="terminal in adminController.terminals">
<td>{{terminal.id}}</td>
<td>{{terminal.connectedUser}}</td>
<td>{{terminal.type}}</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript" src="${pageContext.request.contextPath}/vendors/angular/1.4.4/angular.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/app.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/controller/admin-controller.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/service/admin-service.js"></script>
</body>
</html>
I can access my Spring Controller from a web browser and it returns some data but it's not being called by the Angular JS controller
Am I missing something here?
Could you please help me?
Thank you
To return a data from your service function you should use .then function which has ability to return a data when promise gets resolved OR reject. That you can't to with .success & .error function.
.success & .error method of $http has been **deprecated
Factory
AdminApp.factory('AdminService', ['$http', '$q', function($http, $q) {
return {
fetchAllTerminals: function() {
return $http.get('http://localhost:8081/crmCTI/admin/terminal')
.then(function(response) {
console.log('Service');
return response.data;
},function(errResponse) {
console.error('Error while fetching terminals');
return $q.reject(errResponse);
});
}
};
}]);
Then controller method will again place .then function on the factory method. So the 1st function of .then will get called on resolved of fetchAllTerminals call, if it gets rejected 2nd function will get called.
Controller
self.fetchAllTerminals = function() {
console.log('Controller');
AdminService.fetchAllTerminals()
.then(function(data) {
self.terminals = data;
}, function(error) {
console.error('Error while fetching Terminals');
});
};
try this:
'use strict';
angular.module('AdminApp',[]);
And the service:
'use strict';
angular.module('AdminApp').factory('AdminService', ['$http', '$q', function($http, $q) {
return {
fetchAllTerminals: function() {
return $http.get('http://localhost:8081/crmCTI/admin/terminal')
.success(function(response) {
console.log('Service');
return response.data;
})
.error(function(errResponse) {
console.error('Error while fetching terminals');
return $q.reject(errResponse);
});
}
};
}]);
controller:
'use strict';
angular.module('AdminApp').controller('AdminController', ['$scope', 'AdminService', function($scope, AdminService) {
var self = this;
self.terminal={id:'',connectedUser:'',type:'',state:''};
self.terminals=[];
self.fetchAllTerminals = function() {
console.log('Controller');
AdminService.fetchAllTerminals()
.success(function() {
self.terminals = d;
})
.error(function() {
console.error('Error while fetching Terminals');
});
};
self.reset = function() {
self.terminal = {id : null, connectedUser : '', type : '', state : ''};
};
}]);
I am trying to create an Angular Factory, this is based on a example from a plural site course http://www.pluralsight.com/training/player?author=shawn-wildermuth&name=site-building-m7&mode=live&clip=3&course=site-building-bootstrap-angularjs-ef-azure.
From debugging the code in Chrome it appears to run fine. I can see when I debug it that the service gets my data and puts it in my array but when I look at the controller in either $scope.data or dataService.data the arrays are empty. I don't see any javascript errors. I'm not sure what I'm doing wrong, any suggestions. I'm using AngularJS v1.3.15.
module.factory("dataService", function($http,$routeParams,$q) {
var _data = [];
var _getData = function () {
var deferred = $q.defer();
$http.get("/api/v1/myAPI?mainType=" + $routeParams.mainType + "&subType=" + $routeParams.subType)
.then(function (result) {
angular.copy(result.data,_data);
deferred.resolve();
},
function () {
//Error
deferred.reject();
});
return deferred.promise;
};
return {
data: _data,
getData: _getData
};});
module.controller('dataController', ['$scope', '$http', '$routeParams', 'dataService',function ($scope, $http, $routeParams, dataService) {
$scope.data = dataService;
$scope.dataReturned = true;
$scope.isBusy = true;
dataService.getData().then(function () {
if (dataService.data == 0)
$scope.dataReturned = false;
},
function () {
//Error
alert("could not load data");
})
.then(function () {
$scope.isBusy = false;
})}]);
On
return {
data: _data,
getData: _getData
};});
you have "data: _data," while your array is named just "data". Change the name of the variable to match and it will work:
var _data = [];
Why would you use deferred from $q this way?
The proper way to use $q:
$http.get("/api/v1/myAPI?mainType=" + $routeParams.mainType + "&subType=" + $routeParams.subType)
.success(function (result) {
deferred.resolve(result);
}).error(
function () {
//Error
deferred.reject();
});
And then in controller
dataService
.getData()
.then(function success(result) {
$scope.data = result; //assing retrived data to scope variable
},
function error() {
//Error
alert("could not load data");
});
In fact, there are some errors in your codes :
In your Service, you define var data = [];, but you return data: _data,. So you should correct the defination to var _data = []
you don't define _bling, but you use angular.copy(result.data,_bling);
One more question, why do you assigne the service to $scope.data : $scope.data = dataService ?
EDIT :
Notice that there 3 changes in the following codes:
comment the $scope.data = dataService;, because it makes no sense, and I think that $scope.data should be the data that the service returns.
$scope.data = dataService.data;, as I described in 1st point. You can see the result from the console.
In the if condition, I think that you want to compare the length of the returned data array, but not the data.
module.controller('dataController', ['$scope', '$http', '$routeParams', 'dataService',function ($scope, $http, $routeParams, dataService) {
// $scope.data = dataService;
$scope.dataReturned = true;
$scope.isBusy = true;
dataService.getData().then(function () {
if (dataService.data.length === 0){
$scope.dataReturned = false;
}else{
$scope.data = dataService.data;
console.log($scope.data);
}
},
// other codes...
})}]);
For example, I have such request to my server. As a response, I will get an array of objects with parameters: id, name and position. All of these loads into a table. How can I manipulate with an array $scope.employees if i will decide to change it later?
An answer from server is:
data = [{"id":1,"name":"Jack","position":"City guard"},{"id":2,"name":"Jim","position":"Sheriff"},{"id":4,"name":"Jack","position":"Cruel genius"},{"id":7,"name":"Guy","position":"Manager"}]
And how to be sure, that request is already posted into a table, so i can perform some later operations?
angular
.module('MyApp', [])
.controller('MyController', ['$scope', '$http', MyController]);
function MyController ($scope, $http) {
$http.get("/servlet").success(function(data){
$scope.employees = data;
});
}
function otherOperation () {
$scope.employees.push({
id : 5,
name : "John",
position : "Manager"
});
}
HTML code:
<div id="content" ng-controller='MyController'>
<table id="table">
<tr>
<th> ID </th>
<th> Name </th>
<th> Position </th>
</tr>
<tr ng-repeat="employee in employees">
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
<td>{{employee.position}}</td>
</tr>
</table>
<button ng-click="otherOperation"> Button </button>
</div>
The otherOperation method should be nested inside MyController, like this:
angular
.module('MyApp', [])
.controller('MyController', ['$scope', '$http', MyController]);
function MyController ($scope, $http) {
function otherOperation () {
$scope.employees.push({
id : 5,
name : "John",
position : "Manager"
});
}
$http.get("/servlet").success(function(data){
$scope.employees = data;
});
}
You could also pass the $scope as a parameter, like this:
angular
.module('MyApp', [])
.controller('MyController', ['$scope', '$http', MyController]);
function MyController ($scope, $http) {
$http.get("/servlet").success(function(data){
$scope.employees = data;
});
otherOperation($scope);
}
function otherOperation ($scope) {
$scope.employees.push({
id : 5,
name : "John",
position : "Manager"
});
}