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.
Related
I'm building a silly little Football app. On the first page, I am trying to load the country's top division standings and the coming week's fixtures.
I retrieve the data, using a RESTful Web Service and so is done asynchronously. The table is fine, but not the fixtures.
There is an array of fixture objects, within them, there's a 'matchday' and 'status' property. If you look at the 'this.getFixtures' function, look at the success code block. What I am trying to do is only display the fixtures for a certain match day. If there is one game left to be played on a certain matchday, then I only want that fixture displayed. If not, display next matchday's fixtures.
The 'status' property typically has a value of 'SCHEDULED' or 'FINISHED'. In the success code block I am saying:
Loop through all fixtures retrieved.
If this fixture is scheduled, that means, we're on the matchday for this fixture.
In which case, break loop.
I am then trying to use that value outside the get method, but I keep getting undefined. Is there any way to access that value outside the success block?
I'll use the $scope.matchDay function as the filter.This will help me to only display scheduled fixtures in that matchday with ng-repeat.
Anyway, sorry for the long winded post, but here's the code:
HTML:
<div class="grid-x">
<div class="medium-8 medium-offset-2 cell">
<div id="premier-league-banner">
<div class="banner-shade">
<div class="grid-x">
<div class="medium-5 cell">
<table>
<tr ng-repeat="team in premierLeagueTable.standing | limitTo: 6">
<th>{{ $index + 1 }}</th>
<td><img class="prem-thumbnail" src="{{ team.crestURI }}" /></td>
<th>{{ team.teamName }}</th>
<th>{{ team.playedGames }}</th>
<th>{{ team.goalDifference }}</th>
<th>{{ team.points }}</th>
</tr>
</table>
</div>
<div class="medium-2 cell">
<img src="images/prem-logo.png" />
</div>
<div class="medium-5 cell">
<table>
<tr ng-repeat="fixture in premierLeagueFixtures.fixtures | filter:{matchday: 10}">
<th>{{fixture.homeTeamName}}</th>
<td>vs</td>
<th>{{fixture.awayTeamName}}</th>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
Angular JS
// MODULE
var quickEleven = angular.module('quickEleven', ['ngRoute', 'ngResource']);
// ROUTES
quickEleven.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'pages/home.htm',
controller: 'homeController'
})
});
// CONTROLLERS
quickEleven.controller('homeController', ['$scope', '$resource', '$log', 'footballData', function($scope, $resource, $log, footballData) {
function getMonday(date) {
var day = date.getDay() || 7;
if( day !== 1 )
date.setHours(-24 * (day - 1));
return date;
}
function convertDate(date) {
var yyyy = date.getFullYear().toString();
var mm = (date.getMonth()+1).toString();
var dd = date.getDate().toString();
var mmChars = mm.split('');
var ddChars = dd.split('');
return yyyy + '-' + (mmChars[1]?mm:"0"+mmChars[0]) + '-' + (ddChars[1]?dd:"0"+ddChars[0]);
}
var thisMonday = getMonday(new Date);
var nextMonday = getMonday(new Date);
nextMonday.setDate(nextMonday.getDate() + 7);
$log.info("Boom! " + convertDate(thisMonday));
$log.info("For! " + convertDate(nextMonday));
$scope.premierLeagueTable = footballData.getLeagueTable("http://api.football-data.org/v1/competitions/:competitionId/leagueTable", 445);
//http://api.football-data.org/v1/competitions/:competitionId/fixtures?timeFrameStart=2018-03-01&timeFrameEnd=2018-03-05
//"http://api.football-data.org/v1/competitions/:competitionId/fixtures/?matchday=9"
$scope.premierLeagueFixtures = footballData.getFixtures("http://api.football-data.org/v1/competitions/:competitionId/fixtures?timeFrameStart=" + convertDate(thisMonday) + "&timeFrameEnd=" + convertDate(nextMonday), 445);
$log.info($scope.premierLeagueFixtures);
$log.info($scope.premierLeagueTable);
$scope.matchdayValue = 9;
$scope.matchDay = function() {
return footballData.getMatchday();
};
}]);
quickEleven.service('footballData', ['$resource', '$log', function($resource, $log) {
//Referring to the latest matchday with the status as 'SCHEDULED'
var self = this;
var test;
self.latestScheduledMatchday = 0;
self.getMatchday = function() {
$log.info("This is: " + test);
return self.latestScheduledMatchday;
}
this.getLeagueTable = function (footballUrl, compId) {
this.footballAPI =
$resource(footballUrl, {}, {
get: {
method: "GET",
headers: {
"X-Auth-Token": "f73808b698e84dccbe4886da3ea6e755"
}
}
})
.get({
competitionId: compId
}, function(data) {
this.fussball = data;
}, function(err) {
$log.error(err);
});
return this.footballAPI;
};
this.getFixtures = function (footballUrl, compId) {
// var self;
this.footballAPI =
$resource(footballUrl, {}, {
get: {
method: "GET",
headers: {
"X-Auth-Token": "f73808b698e84dccbe4886da3ea6e755"
}
}
})
.get({
competitionId: compId
}, function(data) {
// self = data.fixtures;
self.latestScheduledMatchday = data.fixtures[0].matchday
for (var i = 0; i < data.fixtures.length; i++) {
var fixture = data.fixtures[i];
if (fixture.status == 'SCHEDULED') {
test = fixture.matchday;
break;
}
}
$log.info("Dollar signs... " + test);
}, function(err) {
$log.error(err);
});
return this.footballAPI;
};
}]);
I see 2 issues so far. One on the note of undefined values is your service might not be getting implemented correctly. AFAIK you should be returning the service in the "function($resource, $log) {" function.
Here's how I'd change it (note I've not tested this)
quickEleven.service('footballData', ['$resource', '$log', function($resource, $log) {
//Referring to the latest matchday with the status as 'SCHEDULED'
var wrappedService = {};
var test;
var latestScheduledMatchday = 0;
var getMatchday = function() {
$log.info("This is: " + test);
return latestScheduledMatchday;
}
wrappedService.getLeagueTable = function (footballUrl, compId) {
wrappedService.footballAPI =
$resource(footballUrl, {}, {
get: {
method: "GET",
headers: {
"X-Auth-Token": "f73808b698e84dccbe4886da3ea6e755"
}
}
})
.get({
competitionId: compId
}, function(data) {
wrappedService.fussball = data;
}, function(err) {
$log.error(err);
});
return wrappedService.footballAPI;
};
wrappedService.getFixtures = function (footballUrl, compId) {
wrappedService.footballAPI =
$resource(footballUrl, {}, {
get: {
method: "GET",
headers: {
"X-Auth-Token": "f73808b698e84dccbe4886da3ea6e755"
}
}
})
.get({
competitionId: compId
}, function(data) {
latestScheduledMatchday = data.fixtures[0].matchday
for (var i = 0; i < data.fixtures.length; i++) {
var fixture = data.fixtures[i];
if (fixture.status == 'SCHEDULED') {
test = fixture.matchday;
break;
}
}
$log.info("Dollar signs... " + test);
}, function(err) {
$log.error(err);
});
return wrappedService.footballAPI;
};
return wrappedService;
}]);
So instead of the function returning no service, you have your service wrapped and returned as I believe you were intending. I also removed references to "self" since your intention there (internal service variables) is more eloquently handled with var scoping in the function.
Second issue that you will see once your service is working.
$scope.premierLeagueTable = footballData.getLeagueTable("http://api.football-data.org/v1/competitions/:competitionId/leagueTable", 445);
This line does not return the request data, but returns the request object. In fact by the time $scope.premierLeagueTable is set, the request hasn't even completed yet. What you do get is access to a promise that you can put a callback function in. See the angular resource documentation for more info, specifically the third example in the user-resource section where you see .$promise https://docs.angularjs.org/api/ngResource/service/$resource#user-resource.
Whatever functionality you want to apply the data return to should live inside that .$promise.then(...) callback. I'm not entirely sure if the promise in there receives the response data, or your callback return. You'll have to read further or experiment to find that out.
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.
I created one angular JS application using MVC 4
where i created one view which renders templates in that we have one template which contains large amount of data as one lack records for that i am looking to implement Infinite Scrolling
1.index.cshtml
<div id="sidebar-left" class="span2">
<div class="nav-collapse sidebar-nav">
<ul class="nav nav-tabs nav-stacked main-menu">
<li class="navbar-brand">Talks</li>
<li class="navbar-brand">SRDNames</li>
<li class="navbar-brand">Speakers</li>
<li class="navbar-brand">Add Talk</li>
</ul>
</div>
</div>
SRDNames.cshtml
<div class="box-content">
<table class="table table-striped table-bordered bootstrap-datatable datatable">
<tr>
<th>
SRD_NAME
</th>
<th>
CREATED_BY_USER_ID
</th>
</tr>
<tr ng-repeat="srdname in srdnames">
<td>
{{srdname.sRD_NAME}}
</td>
<td>
{{srdname.cREATED_BY_USER_ID}}
</td>
</tr>
</table>
3.eventModule.js
var eventModule = angular.module("eventModule", []).config(function ($routeProvider, $locationProvider) {
//Path - it should be same as href link
$routeProvider.when('/Events/Talks', { templateUrl: '/Templates/Talk.html', controller: 'eventController' });
$routeProvider.when('/Events/Speakers', { templateUrl: '/Templates/Speaker.html', controller: 'speakerController' });
$routeProvider.when('/Events/AddTalk', { templateUrl: '/Templates/AddTalk.html', controller: 'talkController' });
$routeProvider.when('/Events/SRDNames', { templateUrl: '/Templates/SRDNames.html', controller: 'srdnamescontroller' });
$locationProvider.html5Mode(true);
});
srdnamescontroller.js
eventModule.controller("srdnamescontroller", function ($scope, EventsService) {
EventsService.getSRDName().then(function (srdnames) { $scope.srdnames = srdnames }, function ()
{ alert('error while fetching talks from server') })
});
5.EventsService.js
eventModule.factory("EventsService", function ($http, $q) {
return {
getSRDName: function () {
// Get the deferred object
var deferred = $q.defer();
// Initiates the AJAX call
$http({ method: 'GET', url: '/events/GetSRDName' }).success(deferred.resolve).error(deferred.reject);
// Returns the promise - Contains result once request completes
return deferred.promise;
},
});
looking to implement like http://jsfiddle.net/vojtajina/U7Bz9/ in above application.. please help
Demo
There are many possible solutions. Here is one that may work for you.
Implement a scroll module that defines the following:
An infiniteScroll directive
A data service to get the scrollable data
You can use the scroll module from within your app:
HTML:
<div ng-app="app" ng-controller="ctrl">
<div infinite-scroll="items">
</div>
</div>
JS:
var app = angular.module('app', ['scroll']);
app.controller('ctrl', function($scope, dataService) {
$scope.items = [];
dataService.loadMore($scope.items, function(lastItem) {
var items = [];
var id = lastItem ? lastItem.id : 0;
for (var i = 0; i < 5; i++) {
items.push({id: id + i});
}
return items;
});
});
The dataService exposes a loadMore method that accepts an array, and a callback function to load more data. The above example loads more data by looping through 5 items, and adding to the array. But you can customize this function callback to retrieve data from another service:
var app = angular.module('app', ['scroll']);
app.controller('ctrl', function($scope, $http, dataService) {
$scope.items = [];
dataService.loadMore($scope.items, function(lastItem, done) {
var lastItemId = lastItem ? lastItem.id : '';
$http({ method: 'GET',url:'api/items/' + lastItemId})
.success(function(items) {
done(items);
});
});
});
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>
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}
});