Unable to figure out the async nature of the $http call - angularjs

My controller/service retrieve the data from the url, and it returns with no errors. However, the repeat for the <tbody> doesn't show the data. The inspector shows no errors, and fiddler shows the url is called for the rest api.
Model
export interface IAuthor {
AuthorId: number;
FirstName: string;
LastName: string;
GoogleAuthorId: string;
CreatedByEmployeeId: any;
CreatedOn: Date;
ModifiedByEmployeeId: any;
ModifiedOn: Date;
PhotoURI: string;
}
Service
export class AuthorService implements IAuthorService {
private url: string;
static $inject = ["$http"];
constructor(private $http: ng.IHttpService) {
this.url = "/odata/Author";
}
getAuthors(): ng.IPromise<IAuthor[]> {
return this.$http
.get(this.url)
.then((response: ng.IHttpPromiseCallbackArg<IAuthor[]>): IAuthor[] => {
return response.data;
});
}
}
Controller
export class AuthorController implements IAuthorController {
authorItems: IAuthor[];
static $inject: string[] = ["AuthorService"];
constructor(private authorService: Services.IAuthorService) {
var results = this.getAuthors();
}
getAuthors(): IAuthor[] {
this.authorItems = [];
var results = this.authorService.getAuthors()
.then((authorItemsLocal: IAuthor[]): void => {
this.authorItems = authorItemsLocal;
});
return this.authorItems;
}
}
Html
<div class="container" ng-controller="AuthorController as vm">
<h2>Authors</h2>
<div>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Author ID</th>
</tr>
</thead>
<tbody ng-repeat="c in vm.authorItems">
<tr>
<td>{{ c.FirstName }}</td>
<td>{{ c.LastName }}</td>
<td>{{ c.AuthorId }}</td>
</tr>
</tbody>
</table>
</div>
</div>

I had typed the results to the proper type in typescript. When I typed them to "any", it worked.

Related

How to display json data from api in tabular form?

I am new to angular.I am trying to consume the data from api https://api.covid19india.org/travel_history.json. How to print the data from above api json data in tabular form? Is there any changes required? When the angular application is served the travel_history is to be in tabular form.
Here is the Component.ts
import { Component } from "#angular/core";
import { coronaApi } from "../service/service";
import { HttpErrorResponse } from "#angular/common/http";
#Component({
selector:'corona',
templateUrl:'./component.html'
})
export class CoronaComponent{
public result:Array<any>;
constructor(public service:coronaApi){}
ngOnInit(){
this.service.getData().subscribe((posRes)=>{
this.result=(posRes);
},(errRes:HttpErrorResponse)=>{
console.log(errRes)
})
}
}
Here is the component.html.
<table border="2">
<thead>
<tr>
<th colspan="11"> Travel History </th>
</tr>
<tr>
<th>_CN6CA</th>
<th>Accuracy Location</th>
<th>Address</th>
<th>Datasource</th>
<th>Latlong</th>
<th>Mode of Travel</th>
<th>Pid</th>
<th>Place Name</th>
<th>Time From</th>
<th>Time to</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let x of result";*ngFor = " let y of x ">
<td>{{y._cn6ca}}</td>
<td>{{y.accuracylocation}}</td>
<td>{{y.address}}</td>
<td>{{y.datasource}}</td>
<td>{{y.latlong}}</td>
<td>{{y.modeoftravel}}</td>
<td>{{y.pid}}</td>
<td>{{y.placename}}</td>
<td>{{y.timefrom}}</td>
<td>{{y.timeto}}</td>
<td>{{y.type}}</td>
</tr>
</tbody>
</table>
Hope this will work. While you are subscribing to getData() function you should use second loop.
this.result = response.travel_history;
Here are the component.ts
import { Component } from "#angular/core";
import { coronaApi } from "../service/service";
import { HttpErrorResponse } from "#angular/common/http";
#Component({
selector:'corona',
templateUrl:'./component.html'
})
export class CoronaComponent{
public result:Array<any>;
constructor(public service:coronaApi){}
ngOnInit(){
this.service.getData().subscribe((posRes)=>{
this.result=posRes.travel_history;
},(errRes:HttpErrorResponse)=>{
console.log(errRes)
})
}
}
Here are component.html
<tbody>
<tr *ngFor="let y of result">
<td>{{y._cn6ca}}</td>
<td>{{y.accuracylocation}}</td>
<td>{{y.address}}</td>
<td>{{y.datasource}}</td>
<td>{{y.latlong}}</td>
<td>{{y.modeoftravel}}</td>
<td>{{y.pid}}</td>
<td>{{y.placename}}</td>
<td>{{y.timefrom}}</td>
<td>{{y.timeto}}</td>
<td>{{y.type}}</td>
</tr>
</tbody>
On your ts file you add a boolean and display the data after the response. You don't need second loop just set
this.result = response.travel_history;
ts
isDataLoaded: boolean = false;
public result;
ngOnInit(){
this.service.getData().subscribe(response => {
this.result = response.travel_history;
this.isDataLoaded = true;
});
}
html
<tbody *ngIf="isDataLoaded">
<tr *ngFor="let x of result">
<td>{{x._cn6ca}}</td>
<td>{{x.accuracylocation}}</td>
<td>{{x.address}}</td>
<td>{{x.datasource}}</td>
<td>{{x.latlong}}</td>
<td>{{x.modeoftravel}}</td>
<td>{{x.pid}}</td>
<td>{{x.placename}}</td>
<td>{{x.timefrom}}</td>
<td>{{x.timeto}}</td>
<td>{{x.type}}</td>
</tr>
</tbody>

Angular execute get service based on array returned by antoher get with ForkJoin

Problem:
Trying to populate a table with information from 2 services:
The first service returns a lista of fabric plans that contain an array with machine operations. The second service is a get that returns a specific operation by id, that is used to get the information about the operation.
The problem is that the service can only receive one id.
How can I populate the table with the operation information knowing that a fabric plan as one or more operations.
The Fabric Plan Model
export class Fabricplan{
Id: number;
Description: string;
operationsIds: Array<Number> = [];
DateStart: Date;
}
The service responsible for the post request:
getOperation(Id):Observable<Operation[]>{
return this.http.get<Operation[]>(`${this.operations}${Id}`, httpOptions);
}
The html table is the following:
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th scope="col">Number</th>
<th scope="col">Description</th>
<th scope="col">Operations</th>
<th scope="col">DateStart</th>
</tr>
</thead>
<tbody>
<tr scope="row" id="table" *ngFor="let fabricplan of fabricplans let i = index" (click)="open(content, fabricplan.id)">
<td>{{ i+1 }}</td>
<td>{{ fabricplan?.description }}</td>
<td>{{ oper_desc[id] }}</td>
<td>{{ fabricplan?.dateStart }}</td>
</tr>
</tbody>
</table>
In the fabricplan.component.ts file
ngOnInit() {
this.fabricplanservice.getfabricplans().subscribe(fabricplans => {
this.fabricplans = fabricplans;
const opers_desc$ = this.fabricplans.map( element =>
this.operationservice.getOperation(element).pipe(
// map it to the value you want
map((response) => {
this.operation = response;
return this.operation['description'];
}),
// replace 404 responses with the 'No fabric plan'
// if not 404 throw the error again
catchError((error) => {
if(error.status === 404){
return this.empt;
} else {
throwError(error);
}
}
)));
forkJoin(opers_desc$).subscribe((opers_desc) => this.oper_desc = opers_desc);
});
this.operationservice.getOperations().subscribe(operation => {
this.operations = operation;
});
}
}

Using AngularJS, how can I match items from two separate ng-repeats?

Using AngularJS, I am creating a table that is pulling data with two web requests.
Each web request has it's own ng-repeat in the HTML, ng-repeat="user in users" and ng-repeat="app in apps". Right now all existing apps are showing in every repeat of user. What I'd like to do is some kind of match, lookup, or filter and only show apps that the user is associated with. So, when user.Title == app.Title.
Here is the HTML:
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<div class="ProfileSheet" ng-repeat="user in users">
<h3 class="heading">User Profile</h3>
<table id="Profile">
<tr>
<th>User</th>
<td>{{user.Title}}</td>
</tr>
<tr>
<th>First Name</th>
<td>{{user.FirstName}}</td>
</tr>
<tr>
<th>Last Name</th>
<td>{{user.LastName}}</td>
</tr>
<tr>
<th>Job Title</th>
<td>{{user.JobTitle}}</td>
</tr>
<tr>
<th>Emp ID</th>
<td>{{user.EmployeeID}}</td>
</tr>
<tr>
<th>Officer Code</th>
<td>{{user.OfficerCode}}</td>
</tr>
<tr>
<th>Email</th>
<td>{{user.Email}}</td>
</tr>
<tr>
<th>Telephone</th>
<td>{{user.WorkPhone}}</td>
</tr>
<tr>
<th>Fax Number</th>
<td>{{user.WorkFax}}</td>
</tr>
<tr>
<th>Location Description</th>
<td>{{user.LocationDescription}}</td>
</tr>
<tr>
<th>Mailstop / Banking Center #</th>
<td>{{user.Mailstop}}</td>
</tr>
</table>
<br>
<h3 class="heading">User Applications</h3>
<div style="border:3px solid #707070; padding-right:12px;">
<h4 style="padding-left:5px;">User Applications</h4>
<table id="Apps">
<tr id="AppsHeading">
<th>Application</th>
<th>User ID</th>
<th>Initial Password</th>
<th>Options / Comment</th>
<th>Setup Status</th>
</tr>
<tr ng-repeat="app in apps">
<td>{{app.Application.Title}}</td>
<td>{{app.Title}}</td>
<td>{{app.InitialPassword}}</td>
<td>{{app.OptionsComments}}</td>
<td style="border-right:3px solid #707070;">{{app.SetupStatus}}</td>
</tr>
</table>
</div>
</div>
</div>
The JS:
var app = angular.module('myApp', ['ngSanitize']);
var basePath = "https://portal.oldnational.com/corporate/projecthub/anchormn/associates"
app.controller('MainCtrl', function($scope, $http, $q){
var supportList;
$(document).ready(function() {
$scope.getAdminList();
$scope.getAppsList();
});
// $scope.selectedIdx = -1;
// $scope.showResults = false
$scope.prepContext = function(url,listname,query){
var path = url + "/_api/web/lists/getbytitle('" + listname + "')/items" + query;
console.log(path);
return path;
}
$scope.getAdminList = function() {
supportList = $http({
method: 'GET',
url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Administration","?$orderBy=LastName"),
headers: {
"Accept": "application/json; odata=verbose"
}
}).then(function(data) {
//$("#articleSection").fadeIn(2000);
console.log("adminlist", data.data.d.results);
$scope.users = data.data.d.results;
});
};
$scope.getAppsList = function() {
supportList = $http({
method: 'GET',
// url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Applications","?$select=Title,InitialPassword,OptionsComments,SetupStatus,Application/Title&$orderBy=Application&$expand=Application"),
url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Applications","?$select=Title,InitialPassword,OptionsComments,SetupStatus,Application/Title,ParentUserLink/ID&$orderBy=Application&$expand=Application,ParentUserLink"),
headers: {
"Accept": "application/json; odata=verbose"
}
}).then(function(data) {
//$("#articleSection").fadeIn(2000);
console.log("appslist", data.data.d.results);
$scope.apps = data.data.d.results;
});
};
});
app.config([
'$compileProvider',
function($compileProvider){
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrom-extension|javascript):/)
}
]);
How can I do this?
There's a lot of extraneous code in the controller. For the purposes of this answer I removed it. Also I understand that users and apps are related by a property called Title but the name was confusing me - forgive me if the data doesn't make sense.
Suggestion: Only use $(jQuery) where strictly necessary. Angular provides a lot of functionality that replaces jQuery functionality. Instead of using $.ready like:
$(document).ready(function() {
$scope.getAdminList();
$scope.getAppsList();
});
wait to bootstrap your application until the document is ready using the following code:
(function () {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
})();
Then you don't have to burden the controller with the responsibility of waiting until the document is loaded. Note: ng-app was removed from the markup.
Suggestion: Use $q.all() to wait for multiple promises to resolve. $q.all() will wait until all promises resolve to call .then(). This helps to ensure that all data is available when you start to use it.
Suggestion: Only assign properties and functions to $scope if they will be used by the view. I removed the functions that are not used by the view from the $scope object.
How does it work?
When the controller loads, we use $q.all() to invoke and wait for fetchAdminList() and fetchAppsList() to fetch data from an API. Once each API request resolves we unwrap the data in .then() callbacks and return it (read more on promise chaining to understand what happens when a value is returned from .then()). When both promises resolve, we store the data on $scope to make it available to the view. We also pre-compute which applications each user can use and store that data in $scope.userApps to make it available to the view.
I did not have access to the APIs you are fetching data from. I substituted $http calls with an immediately resolving promise using $q.resolve() and static data. When you are ready just replace $q.resolve(...) with the original $http(...) calls in the fetch functions.
Run the snippet to see it in action.
var app = angular.module('myApp', []);
(function () {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
})();
var USERS = [
{
Title: 'Software Engineer',
FirstName: 'C',
LastName: 'Foster',
EmployeeID: 1
},
{
Title: 'Software Engineer',
FirstName: 'J',
LastName: 'Hawkins',
EmployeeID: 2
},
{
Title: 'CEO',
FirstName: 'Somebody',
LastName: 'Else',
EmployeeID: 3
}
];
var APPS = [
{
Application: { Title: 'StackOverflow' },
Title: 'Software Engineer'
},
{
Application: { Title: 'Chrome' },
Title: 'Software Engineer'
},
{
Application: { Title: 'QuickBooks' },
Title: 'CEO'
}
]
app.controller('MainCtrl', function ($scope, $http, $q) {
$q.all({
users: fetchAdminList(),
apps: fetchAppsList()
})
.then(function(result) {
// Store results on $scope
$scope.users = result.users;
$scope.apps = result.apps;
// Pre-compute user apps
$scope.userApps = $scope.users.reduce(
function(userApps, user) {
userApps[user.EmployeeID] = getUserApps(user.Title);
return userApps;
},
[]
);
});
function fetchAdminList() {
return $q.resolve({ data: { d: { results: USERS } } })
.then(function (data) { return data.data.d.results; });
}
function fetchAppsList() {
return $q.resolve({ data: { d: { results: APPS } } })
.then(function (data) { return data.data.d.results; });
}
// Get a list of apps that apply to user title
function getUserApps(userTitle) {
return $scope.apps.filter(function(app) {
return app.Title === userTitle;
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<div>
<div ng-controller="MainCtrl">
<div class="ProfileSheet" ng-repeat="user in users">
<h3 class="heading">User Profile</h3>
<table id="Profile">
<tr>
<th>User</th>
<td>{{user.Title}}</td>
</tr>
<tr>
<th>First Name</th>
<td>{{user.FirstName}}</td>
</tr>
<tr>
<th>Last Name</th>
<td>{{user.LastName}}</td>
</tr>
<tr>
<th>Job Title</th>
<td>{{user.JobTitle}}</td>
</tr>
<tr>
<th>Emp ID</th>
<td>{{user.EmployeeID}}</td>
</tr>
<tr>
<th>Officer Code</th>
<td>{{user.OfficerCode}}</td>
</tr>
<tr>
<th>Email</th>
<td>{{user.Email}}</td>
</tr>
<tr>
<th>Telephone</th>
<td>{{user.WorkPhone}}</td>
</tr>
<tr>
<th>Fax Number</th>
<td>{{user.WorkFax}}</td>
</tr>
<tr>
<th>Location Description</th>
<td>{{user.LocationDescription}}</td>
</tr>
<tr>
<th>Mailstop / Banking Center #</th>
<td>{{user.Mailstop}}</td>
</tr>
</table>
<br>
<h3 class="heading">User Applications</h3>
<div style="border:3px solid #707070; padding-right:12px;">
<h4 style="padding-left:5px;">User Applications</h4>
<table id="Apps">
<tr id="AppsHeading">
<th>Application</th>
<th>User ID</th>
<th>Initial Password</th>
<th>Options / Comment</th>
<th>Setup Status</th>
</tr>
<tr ng-repeat="app in userApps[user.EmployeeID]">
<td>{{app.Application.Title}}</td>
<td>{{app.Title}}</td>
<td>{{app.InitialPassword}}</td>
<td>{{app.OptionsComments}}</td>
<td style="border-right:3px solid #707070;">{{app.SetupStatus}}</td>
</tr>
</table>
</div>
</div>
</div>

Smart Table data is disappearing when sorting

I have created a custom directive with Typescript angular for smart table. But when I sort the columns table data is disappearing... When I write the same only with angular js. It works fine.
Can you please help what I did wrong...
Grid-HTML
<table st-safe-src="rowCollection" st-table="gridOptions" class="table table-condensed table-hover">
<thead>
<tr>
<th st-sort="type">Type</th>
<th st-sort="name">Name</th>
<th st-sort="address">Address</th>
<th ng-if="city">City/State</th>
<th ng-if="zip">Zip</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in options">
<td width="20%">{{row.type | uppercase}}</td>
<td width="20%">{{row.name | uppercase}}</td>
<td width="30%">{{row.address | uppercase}}</td>
<td width="15%" ng-if="city">{{row.cityState | uppercase}}</td>
<td width="15%" ng-if="zip">{{row.zip | uppercase}}</td>
</tr>
</tbody>
</table>
Typescript controller
/// <reference path="../../typings/app.d.ts" />
namespace app.widgets {
'use strict';
interface IGridAttributes extends ng.IAttributes {
cityVisible: string;
zipVisible: string;
options: string;
}
export class updGridCtrl {
static $inject: Array<string> = ['$scope', '$filter'];
constructor(private scope: ng.IFormController) {
this.activate();
}
gridOptions: any = null;
city: boolean;
zip: boolean;
activate() {}
}
class LtcgUpdGridDirective implements ng.IDirective {
static instance(): ng.IDirective { return new LtcgUpdGridDirective(); }
restrict: string = 'E';
transclude = true;
scope = {
'cityVisible': "#",
'zipVisble': "#",
'options': '='
};
templateUrl = 'views/widgets/ltcg-updgrid.html';
link = (scope: any, element: ng.IAugmentedJQuery, attrs: IGridAttributes, ctrl: any) => {
scope.gridOptions = scope.options //private scoped from options : '=',
scope.city = true;
scope.zip = true;
console.log(scope.zipVisible);
if (attrs.cityVisible == "no") {
scope.city = false;
}
if (attrs.zipVisible == "no") {
scope.zip = false;
}
}
}
angular
.module('app.widgets')
.directive('ltcgUpdGrid', LtcgUpdGridDirective.instance);
}

angular angular-table dynamic header name

I use angular and angular-table to display multiple tables on the same page.
I need to create dynamic table with dynamic header and dynamic content.
Plunkr her
This is a working example with non dynamic header but I don't find how to make dynamic
The controller :
angular.module('plunker', ['ui.bootstrap',"angular-table","angular-tabs"]);
function ListCtrl($scope, $dialog) {
$scope.cols= ['index','name','email'];
$scope.list = [
{ index: 1, name: "Kristin Hill", email: "kristin#hill.com" },
{ index: 2, name: "Valerie Francis", email: "valerie#francis.com" },
...
];
$scope.config = {
itemsPerPage: 5,
fillLastPage: true
};
}
HTML
<!-- this work -->
<table class="table table-striped" at-table at-paginated at-list="list" at-config="config">
<thead></thead>
<tbody>
<tr>
<td at-implicit at-sortable at-attribute="name"></td>
<td at-implicit at-sortable at-attribute="name"></td>
<td at-implicit at-sortable at-attribute="email"></td>
</tr>
</tbody>
</table>
<!-- this fail ... -->
<table class="table table-striped" at-table at-paginated at-list="list" at-config="config">
<thead></thead>
<tbody>
<tr>
<td ng-repeat='col in cols' at-implicit at-sortable at-attribute="{{col}}"></td>
</tr>
</tbody>
</table>
I am missing some think or it is not possible with this module ?
Did you know another module where you can have dynamic header and pagination ? ( i try also ngTable but have some bug issu with data not being displayed )
Through the below code, you can generate dynamic header
<table class="table table-hover table-striped">
<tbody>
<tr class="accordion-toggle tblHeader">
<th ng-repeat="(key, val) in columns">{{key}}</th>
</tr>
<tr ng-repeat="row in rows">
<td ng-if="!$last" ng-repeat="col in key(row)" ng-init="val=row[col]">
{{val}}
</td>
</tr>
</tbody>
</table>
Angular Script
var app = angular.module('myApp', []);
app.controller('myControl', function ($scope, $http) {
$http.get('http://jsonplaceholder.typicode.com/todos').success(function (data) {
$scope.columns = data[0];
$scope.rows = data;
}).error(function (data, status) {
});
$scope.key = function (obj) {
if (!obj) return [];
return Object.keys(obj);
}
});

Resources