json data not accessible in html - angular5

I'm trying to access the JSON data using the following component.html
<table>
<thead>
<tr>
<th>name</th>
<th>website</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let d of users1">
<td>{{users1.id}}</td>
<td>{{users1.email}}</td>
</tbody>
</table>
I'm able to access the string variable correctly in the HTML but i think there is some delay in getting the JSON data.
My service.ts looks like this:
export class DataService {
constructor(private http: HttpClient) { }
users: string[] = ['jgfjyghj', 'gjygh'];
getUser() {
return this.users[0];
}
getUsersJ() {
return this.http.get('https://jsonplaceholder.typicode.com/users')
}
}
component.ts looks like this:
export class ListTasksComponent implements OnInit {
users1: any;
user: string;
constructor(private dataservice: DataService) { }
ngOnInit() {
this.dataservice.getUsersJ().subscribe(data => {
this.users1 = data;
console.log(this.users1);
});
this.user = this.dataservice.getUser();
}
}

Your problem is that you use users1 inside your *ngFor.
You should use what you defined in the let - in this case d.
<table>
<thead>
<tr>
<th>name</th>
<th>website</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let d of users1">
<td>{{d.id}}</td>
<td>{{d.email}}</td>
</tbody>
</table>
See stackblitz

Related

How can I get a result from a POST request into a v-for?

I have something like this:
<table class="table">
<tbody>
<tr v-for="(option, index) in Weapons">
<td>Primary</td>
<td>[[ getWeaponType(option.WeaponType) ]]</td>
</tr>
</tbody>
</table>
In my Vue object, in methods, I have this:
getWeaponType: function(weaponTypeNumber){
axios.get('/path/to/api')
.then(response => {
return response.data
})
}
I send an ID and it returns the name for that ID. But I need for it to show in my table whose rows are being generated by the v-for. This isn't working since it is a Promise and the values are not showing. Is there any way I can achieve getting that value to show in the table? I didn't want to do it server side so I'm trying to see if I have any options before I do that.
May I suggest an alternative method?
data() {
return {
weaponsMappedWithWeaponTypes: [];
}
}
mounted() { // I am assuming the weapons array is populated when the component is mounted
Promise.all(this.weapons.map(weapon => {
return axios.get(`/path/to/api...${weapon.weaponType}`)
.then(response => {
return {
weapon,
weaponType: response.data
}
})
).then((values) => {
this.weaponsMappedWithWeaponTypes = values
})
}
computed: {
weaponsAndTheirWeaponTypes: function () {
return this.weaponsMappedWithWeaponTypes
}
}
And then in your template
<table class="table">
<tbody>
<tr v-for="(option, index) in weaponsAndTheirWeaponTypes">
<td>Primary</td>
<td>option.weaponType</td>
</tr>
</tbody>
</table>

sorting does not works with Virtual scrolling custom data source

Angular 8 brings really cool feature to implement virtual scroll. They have provide example here regarding usage of it. Although, its really difficult implement with mat-table.
I have been working on to implement virtual scroll on mat-table and found that, we need to have our own custom data source for that. I have create new data source by implementing DataSource class but what I found is that, I loose the sort and other features due to custom data source.
app.component.html
<cdk-virtual-scroll-viewport class="example-viewport">
<table class="table" mat-table #table matSort [dataSource]="dataSource" [multiTemplateDataRows]="true">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header> id </th>
<td mat-cell *matCellDef="let element"> {{element.id}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="age">
<th mat-header-cell *matHeaderCellDef> Age</th>
<td mat-cell *matCellDef="let element"> {{element.age}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<!-- cdkVirtualScrollViewport uses transform: translateY to correct for all elements that are removed.
We will use a plcaholder row in the table instead because the translate causes problems with the sticky header -->
<tr [style.height.px]="placeholderHeight" mat-row *matRowDef="let row; let index = index; columns: []; when: placeholderWhen"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</cdk-virtual-scroll-viewport>
app.component.ts
import { Component, OnInit, ViewChild, AfterViewInit } from "#angular/core";
import {
CdkVirtualScrollViewport,
FixedSizeVirtualScrollStrategy,
VIRTUAL_SCROLL_STRATEGY
} from "#angular/cdk/scrolling";
import { CollectionViewer } from "#angular/cdk/collections";
import { MatSort } from "#angular/material";
// import { GridTableDataSource } from './gridTable.datasource'
import { GridTableDataSource } from "./gridTableM.datasource";
const ROW_HEIGHT = 48;
/**
* Virtual Scroll Strategy
*/
export class CustomVirtualScrollStrategy extends FixedSizeVirtualScrollStrategy {
constructor() {
super(ROW_HEIGHT, 1000, 2000);
}
attach(viewport: CdkVirtualScrollViewport): void {
this.onDataLengthChanged();
}
}
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
providers: [
{ provide: VIRTUAL_SCROLL_STRATEGY, useClass: CustomVirtualScrollStrategy }
]
})
export class AppComponent implements OnInit, AfterViewInit {
placeholderHeight = 0;
displayedColumns: string[] = ["id", "name", "age"];
dataSource: GridTableDataSource<any>;
rows = Array(200)
.fill(0)
.map((x, i) => {
return { name: "name" + i, id: i, age: 27 };
});
itemSize = 48;
#ViewChild(CdkVirtualScrollViewport, { static: true })
viewport: CdkVirtualScrollViewport;
#ViewChild(MatSort, { static: false }) sort: MatSort;
constructor() {}
ngOnInit() {
this.dataSource = new GridTableDataSource(
this.rows,
this.viewport,
this.itemSize
);
this.dataSource.offsetChange.subscribe(offset => {
this.placeholderHeight = offset;
});
this.dataSource.data = this.rows;
// debugger;
// this.dataSource.sort = this.sort; // this.sort is null here
}
ngAfterViewInit() {
debugger;
this.dataSource.sort = this.sort; // this.sort is null here as well
}
placeholderWhen(index: number, _: any) {
return index == 0;
}
}
To implement sorting and other feature, I just copied code from MatTableDataSource. but somehow, this.sort is always null in any method. I couldn't find reason for this. Can anyone explain this behavior?
Here is stackblitz to play with.
it doesn't work.
angular team doesn't support this
https://github.com/angular/components/issues/19003

Calling a vue component into v-for

I have the following construction:
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Наименование</th>
<th scope="col">API ключ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="adv in advertisers">
<th scope="row">{{ adv.id }}</th>
<td>{{ adv.name }}</td>
<td>{{ adv.api_key }}</td>
<td>
<advertiser-delete-component :advertiser-id="adv.id"></advertiser-delete-component>
<advertiser-edit-component :advertiser-id="adv.id" :advertiser-name="adv.name"></advertiser-edit-component>
</td>
</tr>
</tbody>
</table>
Array "advertisers" keeps data from the server. Data is correct. But I see that all 'advertiser-delete-component' and 'advertiser-edit-component' have the first item of advertisers array in component props.
Here is the code of advertiser-edit-component:
<script>
import { EventBus } from '../../app.js';
export default {
mounted() {
},
props: ['advertiserId', 'advertiserName'],
data: function() {
return {
formFields: {
advertiserName: '',
advertiserId: this.advertiserId,
},
errors: []
}
},
methods: {
submit: function (e) {
window.axios.post('/advertiser/edit', this.formFields).then(response => {
this.formFields.advertiserName = '';
EventBus.$emit('reloadAdvertisersTable');
$('#addAdvertiser').modal('hide');
}).catch(error => {
if (error.response.status === 422) {
this.errors = error.response.data.errors || [];
}
});
}
}
}
The props props: ['advertiserId', 'advertiserName'] are the same for each component call with my code. I want them to be dynamic where they take corresponding elements from the array as the loop goes through it one by one.
What did I do wrong?
UPDATE:
Here is full code of table component:
<template>
<div>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Наименование</th>
<th scope="col">API ключ</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="adv in advertisers">
<th scope="row">{{ adv.id }}</th>
<td>{{ adv.name }}</td>
<td>{{ adv.api_key }}</td>
<td>
<advertiser-delete-component :advertiser-id="adv.id"></advertiser-delete-component>
<advertiser-edit-component :advertiser-id="adv.id"
:advertiser-name="adv.name"></advertiser-edit-component>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import {EventBus} from '../../app.js';
export default {
mounted() {
this.getAdvertisersTable();
EventBus.$on('reloadAdvertisersTable', this.getAdvertisersTable)
},
props: ['totalCountOfAdvertisers'],
data: function () {
return {
advertisers: [],
}
},
methods: {
getAdvertisersTable: function () {
window.axios.get('/advertisers/all')
.then(r => {
this.advertisers = r.data.data;
})
.catch(e => {
console.log(e.response.data.errors);
})
}
}
}
</script>
I think formFields should be array of objects like:
formFields: [{
advertiserName: '',
advertiserId: this.advertiserId,
}],

Sorting table doesnt working Vue

I found this pen https://codepen.io/cfjedimaster/pen/BYpJgj But it doesn't work in my implementation. I can't understand what i am doing wrong
<table>
<thead>
<tr class="header">
<th #click="sort('campaign')">Campaign</th> // click heandler
<th #click="sort('time')">Time</th>
<th #click="sort('views')">Views</th>
<th #click="sort('visitors')">Visitors</th>
<th #click="sort('ctr')">CTR</th>
<th #click="sort('cpc')">CPC</th>
<th #click="sort('cpv')">CPV</th>
<th #click="sort('cpm')">CPM</th>
<th #click="sort('status')">Status</th>
</tr>
</thead>
<tbody> //target table
<tr v-for="item in data" :key="item.id"> // loop
<td>{{item.name}}</td> //row
<td>{{item.time}}</td>
<td>{{item.views}}</td>
<td>{{item.visitors}}</td>
<td>{{item.ctr}}</td>
<td>{{item.cpc}}</td>
<td>{{item.cpv}}</td>
<td>{{item.cpm}}</td>
<td>{{item.status}}</td>
</tr>
</tbody>
</table>
data(){
return{
data: [], // data
currentSort:'campaign', // dynamic property
currentSortDir:'asc'
}
},
methods:{ // methods
getData(){
this.data = this.$store.state.graphArr // get data from store
},
sort(s) { // sort function
//if s == current sort, reverse
if(s === this.currentSort) { // if statement
this.currentSortDir = this.currentSortDir==='asc'?'desc':'asc'; }
this.currentSort = s; // setting currentSortDir
}
},
computed:{ // computed
sortedCats() { // where this function should be called
return this.data.sort((a,b) => { // ES Lint highlits "this". Unexpetced side effect
let modifier = 1;
if(this.currentSortDir === 'desc') modifier = -1;
if(a[this.currentSort] < b[this.currentSort]) return -1 * modifier;
if(a[this.currentSort] > b[this.currentSort]) return 1 * modifier;
return 0;
});
}
}
Maybe i should somewhere call sortedCats function? And the role of computed property at all in this case?
You need to loop through sortedCats array to get the desired result instead of data as sortedCats returns new value every time sort is clicked through computed property.
<tr v-for="item in sortedCats" :key="item.id">

Is it possible to use a component property as a data variable?

I've copied my current code below. I'm trying to dynamically generate table headers depending on what type I pass as a prop to my tables component (standings and status are what I have as the data arrays in my example).
I've accomplished this by using the if statement in the header computed value to return the proper list.
However I'd like to not have to add additional if statements for every type.
Is there a way that I can leverage the type prop to bind directly to the matching data?
<div id="root" class="container">
<tables type="stats">
</tables>
</div>
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</tr>
</tbody>
</table>
`,
data () {
return {
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
};
},
computed: {
header() {
if (this.type == 'standings') {
return this.standings;
} else {
return this.stats;
}
}
},
props: {
type: { required: true }
}
});
If you change up your data structure slightly, you can remove your if statements.
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
Here is an example.
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</td>
</tr>
</tbody>
</table>
`,
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
});
new Vue({
el: "#root"
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="root" class="container">
<tables type="stats">
</tables>
</div>