I'm having issues loading two modals (openModalEdit and openModalDetail method) on my Angular 9 project. When I open it, it automaticly navigates to the root route.
I have another instance (openModalCreate method) of a modal in the same component, apparently both are the same changing only a couple of parameters like the modal title, but the first navigates and the other stays in the modal.
You get to see the modal appearing just before the navigation moves to the root route and the OnInit method of the modal component doesn't have any code, so the modal component doesn't have any functionality that can provoke the navigation in any point.
My bootstrap installed version is "#ng-bootstrap/ng-bootstrap": "^6.0.3".
Does anyone know how to prevent navigation on NgbModal load?
Codebehind:
emitIconButtonClick (action, i, content) {
switch (action) {
case 'edit':
this.openModalEdit(i);
break;
case 'delete':
this.onDeleteRow(i);
break;
case 'detail':
this.openModalDetail(i, content);
break;
default:
break;
}
}
openModalCreate () {
this._formsService.editing = false;
const modalRef = this.modalService.open(DynamicModalComponent, {
size: 'lg',
});
modalRef.componentInstance.title = 'Nuevo ' + this.config.label;
modalRef.componentInstance.fields = this.config.fields;
modalRef.componentInstance.close.subscribe(() => {
modalRef.close();
});
modalRef.componentInstance.save.subscribe((event) => {
this._formsService.setSavedStatusForm(false);
this.rows.push(event);
this.bindToForm();
modalRef.close();
});
}
openModalEdit (index: number) {
const modalRef = this.modalService.open(DynamicModalComponent, {
size: 'lg',
});
modalRef.componentInstance.title = 'Editar ' + this.config.label;
modalRef.componentInstance.fields = this.config.fields;
modalRef.componentInstance.data = this.rows[index];
modalRef.componentInstance.close.subscribe(() => {
modalRef.close();
});
modalRef.componentInstance.save.subscribe((event) => {
this.rows[index] = event;
this._formsService.setSavedStatusForm(false);
this.bindToForm();
modalRef.close();
});
}
openModalDetail (i: number, content: any) {
this.detailArray = [];
Object.entries(this.rows[i]).forEach((e) => {
const entry = {
name: e[0],
value: e[1],
};
this.detailArray.push(entry);
});
this.modalService.open(content).result.then(
(result) => { debugger },
(reason) => { debugger }
);
}
HTML
<div class="form-group dynamic-group field" [formGroup]="group">
<div class="add-btn">
<app-button (click)="openModalCreate()" clasesBtn="btn-primary" icono="plus-circle"> </app-button>
</div>
<div [attr.id]="config.name" [attr.name]="config.name" class="master-table">
<table class="table table-striped">
<thead>
<tr>
<th *ngFor="let header of config.fields" scope="col">
{{ (header.header ? header.header : header.name) | translate }}
</th>
<th *ngIf="config.actions">
{{ 'actions' | translate }}
</th>
</tr>
</thead>
<tbody [ngSwitch]="true">
<tr *ngFor="let row of rows; index as i">
<td *ngFor="let header of config.fields">
<div class="ellipsis max-width-cell">
{{ showDataTable(row[header?.name], header.name) }}
</div>
</td>
<td *ngIf="config.actions">
<div class="table-body_row_actions">
<a *ngFor="let action of config.actions" href="" (click)="emitIconButtonClick(action.name, i, content)" [ngClass]="{
'table-body_row_actions-container': true,
delete: action.name === 'delete'
}">
<i-feather name="{{ action.icon }}" class="feather-icon"></i-feather>
</a>
</div>
</td>
<ng-template #content let-modal>
<div class="modal-header">
<h4 class="modal-title">
{{ 'detail' | translate }}
</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss('')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body custom-modal-body">
<div class="flex-container">
<div class="dataCell" *ngFor="let field of detailArray">
<div class="header">
{{ field.name | translate }}
</div>
<div class="data">
{{ showDataTable(field.value, field.name) }}
</div>
</div>
</div>
</div>
</ng-template>
</tr>
</tbody>
</table>
</div>
</div>
RESOLVED by #zainhassan
--> Remove href="" from a tag
Related
I am developing an e-commerce window and it was working perfectly until the part where I needed to add items to the cart. the browser console showed an error when I wanted to display the items in the cart. However, the strange thing is that it works perfectly in the incognito window. What could be the issue?
Below are my two files:
Code for the cart.vue file:
<template>
<div class="page-cart">
<div class="columns is-multiline">
<div class="column is-12">
<h1 class="title">Cart</h1>
</div>
<div class="column is-12 box">
<table class="table is-fullwidth" v-if="cartTotalLength">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th></th>
</tr>
</thead>
<tbody>
<CartItem
v-for="item in cart.items"
v-bind:key="item.product.id"
v-bind:initialItem="item"
v-on:removeFromCart="removeFromCart"/>
</tbody>
</table>
<p v-else>Are you gonna stay hungry?...😕</p>
</div>
<div class="column is-12 box">
<h2 class="subtitle">Summary</h2>
<strong>Ksh{{ cartTotalPrice.toFixed(2) }}</strong>, {{ cartTotalLength }} items
<hr>
<router-link to="/cart/checkout" class="button is-dark">Proceed to checkout</router-link>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import CartItem from '#/components/CartItem.vue'
export default {
name: 'Cart',
components: {
CartItem
},
data() {
return {
cart: {
items: []
}
}
},
mounted() {
this.cart = this.$store.state.cart
},
methods: {
removeFromCart(item) {
this.cart.items = this.cart.items.filter(i => i.product.id !== item.productid)
}
},
computed: {
cartTotalLength() {
return this.cart.items.reduce((acc, curVal) => {
return acc += curVal.quantity
}, 0)
},
cartTotalPrice() {
return this.cart.items.reduce((acc, curVal) => {
return acc += curVal.product.price * curVal.quantity
}, 0)
},
}
}
</script>
The following code is for a component, cartItem.vue:
<template>
<tr>
<td><router-link :to="item.product.get_absolute_url">{{ item.product.name }}</router-link></td>
<td>Ksh{{ item.product.price }}</td>
<td>
{{ item.quantity }}
<a #click="decrementQuantity(item)">-</a>
<a #click="incrementQuantity(item)">+</a>
</td>
<td>Ksh{{ getItemTotal(item).toFixed() }}</td>
<td><button class="delete" #click="removeFromCart(item)"></button></td>
</tr>
</template>
<script>
export default {
name: 'CartItem',
props: {
initialItem: Object
},
data() {
return {
item: this.initialItem
}
},
methods: {
getItemTotal(item) {
return item.quantity * item.product.price
},
decrementQuantity(item) {
item.quantity -= 1
if (item.quantity === 0) {
this.$emit('removeFromCart', item)
}
this.updateCart()
},
incrementQuantity(item) {
item.quantity += 1
this.updateCart()
},
updateCart() {
localStorage.setItem('cart', JSON.stringify(this.$store.state.cart))
},
removeFromCart(item) {
this.$emit('removeFromCart', item)
this.updateCart()
},
},
}
</script>
I also noted that the phone browser could not open some contents of the website on the network. Especially the contents from the server are not opened in my phone's browser. I am using Bulma CSS framework.
As I don't know the error from the console, I could only guess the problem.
You should try to use a different browser, or at least remove the data (cookies, storage) for your site, and see what happens. Perhaps there is some old previous data that would lead to a problem.
I have a modal in my vue component, which is working well if I just put static text in it, so I know it's perfectly functional
However, trying to pass data from the table cell being clicked into the modal is failing and saying that name is undefined.
What am I doing wrong trying to pass data for each cell into this modal?
<div id="app">
<table>
<tbody>
<tr>
<td v-for="name in names" #click="showDetailModal = true"></td>
<td>#{{ name }}</td>
</tr>
</tbody>
</table>
<div class="modal-vue">
<!-- overlay to help with readability of modal -->
<div class="overlay" v-if="showDetailModal" #click="showDetailModal = false"></div>
<!-- Modal for detail on table cell click event -->
<div class="modal" v-if="showDetailModal">
<button class="close" #click="showDetailModal = false">x</button>
<h3>#{{ name.age }}</h3>
</div>
</div>
</div>
new Vue({
el: "#app",
props: {
},
data: {
showDetailModal: false,
names: [
{
"name":"Amy",
"age":37
},
{
"name":"James",
"age":39
}
]
}
})
Create another data property called currentItem then assign the click item to it when you click on the table row :
<div id="app">
<table>
<tbody>
<tr>
<td v-for="name in names" #click="showModal(name )"></td>
<td>#{{ name }}</td>
</tr>
</tbody>
</table>
<div class="modal-vue">
<!-- overlay to help with readability of modal -->
<div class="overlay" v-if="showDetailModal" #click="showDetailModal = false"></div>
<!-- Modal for detail on table cell click event -->
<div class="modal" v-if="showDetailModal">
<button class="close" #click="showDetailModal = false">x</button>
<h3>#{{ currentItem.age }}</h3>
</div>
</div>
</div>
new Vue({
el: "#app",
props: {
},
data: {
showDetailModal: false,
names: [
{
"name":"Amy",
"age":37
},
{
"name":"James",
"age":39
}
],
currentItem:{name:'',age:''}
},
methods:{
showModal(item){
this.showDetailModal = true;
this.currentItem=item
}
}
})
The reason for name resulting undefined is because it lives only inside the v-for loop.
For this reason, when the table gets clicked, you need to store which user has been clicked. This way, the modal can show the selected user based on that value.
The solution suggested from Boussadjra Brahim works great, however maybe it could be a little cleaner by removing showDetailModal data.
<div id="app">
<table>
<tbody>
<tr>
<td v-for="name in names" #click="showModal(name )"></td>
<td>#{{ name }}</td>
</tr>
</tbody>
</table>
<div class="modal-vue">
<!-- overlay to help with readability of modal -->
<div class="overlay" v-if="currentItem" #click="currentItem = false"></div>
<!-- Modal for detail on table cell click event -->
<div class="modal" v-if="currentItem">
<button class="close" #click="currentItem = false">x</button>
<h3>#{{ currentItem.age }}</h3>
</div>
</div>
</div>
new Vue({
el: "#app",
props: {
},
data: {
names: [
{
"name":"Amy",
"age":37
},
{
"name":"James",
"age":39
}
],
currentItem: false
},
methods:{
showModal(item){
this.currentItem=item
}
}
})
isGridView: true,
isListView: true,
methods: {
switchView: function() {
this.isGridView = !this.isGridView;
},
switchData: function () {
this.isListView = !this.isListView;
}
<div class="product-grid1">item1</div>
<div class="product-grid2">item2</div>
<div class="product-grid3">item3</div>
<div class="product-list1">item1</div>
<div class="product-list2">item2</div>
<div class="product-list3">item3</div>
<div id="app-gridview">
<div>
<button class="button" v-on:click="switchView()"></button>
<button class="button" v-on:click="switchData()"></button>
</div>
<div v-bind:class="[ isGridView ? 'grid-wrapper' : 'list-wrapper' ]">
<div class="grid-row" v-if="isGridView">
<div class="grid-header" v-for="name in gridData.columns">{{ name }}</div>
</div>
<!-- GridView structure -->
<div v-if="isGridView" class="grid-row" v-for="row in gridData.data">
<div class="list-row-item" v-for="name in gridData.columns">
<div>{{ row[name] }}</div>
</div>
</div>
<!-- ListView structure -->
<div v-if="!isGridView" class="list-row" v-for="row in gridData.data">
<img v-bind:src="row.ImagePath" class="list-image" />
<div class="list-property">
<div class="list-row-item" v-for="name in gridData.columns">
<div class="list-property-name">{{ name }}</div>
<div>{{ row[name] }}</div>
</div>
</div>
</div>
</div>
I tried to implement the list and the grid view, Where I need to toggle between each one. For that i have taken isGrid and isList set to true, And from vue side i am trying to place ternary operator, And switch between each other.
Can you please help me on toggle from the list and grid view.
When you create a component whose view can be changed, I suggest that you use the container-presentational component pattern. Really easy to keep track, and it's a breeze to add a new "view" of the data.
// this is the grid view
// this is a presentational component:
// only displays what is passed through props
Vue.component("GridView", {
props: ["users"],
computed: {
headers() {
if (!this.users.length) return []
return Object.keys(this.users[0])
},
},
template: `
<table>
<thead>
<tr>
<th
v-for="header in headers"
:key="header"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="user in users"
:key="user.id"
>
<td
v-for="(val, key) in user"
:key="user.id + '-' + key"
>
{{ val }}
</td>
</tr>
</tbody>
</table>
`
})
// this is the list view
// this is a presentational component:
// only displays what is passed through props
Vue.component("ListView", {
props: ["users"],
template: `
<ol>
<li
v-for="user in users"
:key="user.id"
>
<div
v-for="(val, key) in user"
:key="user.id + '-' + key"
>
{{ key }}: {{ val }}
</div>
</li>
</ol>
`
})
// this component handles the data:
// fetching, mapping, transforming, etc.
// this is a renderless component
Vue.component("DataContainer", {
data() {
return {
users: []
}
},
mounted() {
this.fetchUsers()
},
methods: {
async fetchUsers() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users')
const json = await response.json()
this.users = json.map(({
id,
name,
username,
email
}) => ({
id,
name,
username,
email
}))
} catch (err) {
console.error(err)
}
}
},
render(h) {
// renders nothing, just provides the data
// by passing it through "users"
return this.$scopedSlots.default({
users: this.users,
})
},
})
// the Vue instance
new Vue({
el: "#app",
data() {
return {
layout: "list-view",
}
},
methods: {
switchView() {
this.layout = this.layout === "list-view" ? "grid-view" : "list-view"
}
},
template: `
<div>
<button
#click="switchView"
>
SWITCH VIEW
</button>
<data-container>
<template
#default="{ users }"
>
<component
:is="layout"
v-bind="{ users }"
/>
</template>
</data-container>
</div>
`,
})
table {
border-collapse: collapse;
}
table,
tr,
th,
td {
border: 1px solid black;
}
td,
th {
padding: 4px 8px;
}
th {
background-color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12/dist/vue.js"></script>
<div id="app"></div>
I understand that this is a bit further away from correcting a v-if condition-handling - but this setup would help you create flexible, extensible & maintainable solution.
I this is the cleanest solution
<template>
<button #click="toggleList">switch render view</button>
<component
:is="currentComponent"
:columns="gridData.columns"
:items="gridData.data"
/>
</template>
<script>
import gridComponent from "./your-grid-component.vue";
import listComponent from "./your-list-component.vue";
export default {
components: {
gridComponent,
listComponent,
},
data() {
return {
listType: "grid", //grid/list
gridData: {
columns: [],
data: [],
},
};
},
methods: {
toggleList() {
this.listType = this.listType === "grid" ? "list" : "grid";
},
},
computed: {
currentComponent() {
return this.listType === "grid" ? "gridComponent" : "listComponent";
},
},
};
</script>
In the following Vue Component I want to loop through dwarfs array. And as long as I am in the current component, everything is fine (TEST) and also all the following properties are correct.
Currenct_Component.vue :
<template>
<div>
<h2>Stamm: {{ tribeName }}</h2>
<div class="card-container">
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
<!-- TEST -->
<p>{{dwarf}}</p>
<!-- CHILD COMPONENT -->
<app-modal
:showModal="showModal"
:targetDwarf="dwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<!-- <img class="card-img-top" src="" alt="Card image cap">-->
<div class="card-body">
<h3 class="card-title" ref="dwarfName">{{ dwarf.name }}</h3>
<hr>
<ul class="dwarf-details">
<li><strong>Alter:</strong> {{ dwarf.age }}</li>
<li><strong>Waffen:</strong>
<ul v-for="weapon in dwarf.weapons">
<li><span>Name: {{ weapon.name }} | Magischer Wert: {{ weapon.magicValue }}</span></li>
</ul>
</li>
<li><strong>Powerfactor:</strong> {{ dwarf.weapons.map(weapon => weapon.magicValue).reduce((accumulator, currentValue) => accumulator + currentValue) }}</li>
</ul>
<button class="card-button" #click="showModal = true"><span class="plus-sign">+</span> Waffe</button>
</div>
</div>
</div>
<button id="backBtn" #click="onClick">Zurück</button>
</div>
</template>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
onClick() {
this.$emit('backBtn')
},
notifyApp() {
this.showModal = false;
this.$emit('weaponAdded');
}
},
}
</script>
But when I bind the element dwarf to the Child Component <app-modal/> it changes to the next dwarf in the array dwarfs (TEST) - (So as the result when i add a new weapon in the modal-form it gets added to the second dwarf...):
Child_Component.vue :
<template>
<div>
<div class="myModal" v-show="showModal">
<div class="modal-content">
<span #click="$emit('close')" class="close">×</span>
<h3>Neue Waffe</h3>
<!-- TEST -->
<p>{{ targetDwarf }}</p>
<form>
<input
type="text"
placeholder="Name..."
v-model="weaponName"
required
/>
<input
type="number"
placeholder="Magischer Wert..."
v-model="magicValue"
required
/>
<button #click.prevent="onClick">bestätigen</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
weaponName: '',
magicValue: '',
}
},
props: ['showModal', 'targetDwarf'],
methods: {
onClick() {
if(this.weaponName !== '' &&
Number.isInteger(+this.magicValue)) {
let newData = {...this.dwarf};
newData['weapons'] = [
...this.dwarf['weapons'],
{
"name": this.weaponName,
"magicValue": Number.parseInt(this.magicValue)
},
];
this.$http.post("https://localhost:5019/api", newData)
.then(data => data.text())
.then(text => console.log(text))
.catch(err => console.log(err));
this.$emit('weaponAdded');
} else {
alert('You should fill all fields validly')
}
},
}
}
</script>
It looks like you have the <app-modal/> component inside of the v-for="dwarf in dwarfs" loop, but then the control for showing all of the modal components created by that loop is just in one variable: showModal. So when showModal is true, the modal will show each of the dwarfs, and I'm guessing the second dwarf's modal is just covering up the first one's.
To fix this, you could move the <app-modal /> outside of that v-for loop, so there's only one instance on the page, then as part of the logic that shows the modal, populate the props of the modal with the correct dwarf's info.
Something like this:
<div class="card-container">
<div class="card" v-for="dwarf in dwarfs" :key="dwarf.name">
<p>{{dwarf}}</p>
<div class="card-body">
<button
class="card-button"
#click="() => setModalDwarf(dwarf)"
>
Waffe
</button>
</div>
</div>
<!-- Move outside of v-for loop -->
<app-modal
:showModal="!!modalDwarfId"
:targetDwarf="modalDwarfId"
#close="modalDwarfId = null"
#weaponAdded="onDwarfWeaponAdd"
/>
</div>
export default {
//....
data: () => ({
modalDwarfId: null,
)},
methods: {
setModalDwarf(dwarf) {
this.modalDwarfId = drawf.id;
},
onDwarfWeaponAdd() {
//...
}
},
}
You could then grab the correct dwarf data within the modal, from the ID passed as a prop, or pass in more granular data to the modal so it's more "dumb", which is the better practice so that the component isn't dependent on a specific data structure. Hope that helps
Courtesy of #Joe Dalton's answer, a bit alternated for my case:
<div class="card" style="width: 18rem;" v-for="dwarf in dwarfs" :key="dwarf.name">
...
<button class="card-button" #click="setModalDwarf(dwarf)"><span class="plus-sign">+</span> Waffe</button>
<div>
<app-modal
:showModal="showModal"
:targetDwarf="currentDwarf"
#close="showModal = false"
#weaponAdded="notifyApp"
/>
<script>
import Modal from './NewWeaponModal.vue';
export default {
data() {
return {
showModal: false,
currentDwarf: null,
}
},
components: { appModal : Modal },
props: ['tribeName', 'dwarfs'],
methods: {
setModalDwarf(dwarf) {
this.currentDwarf = dwarf;
this.showModal = true;
},
...
}
</script>
im having this issue which is breaking my head right now and I cant figured out what is the problem.
I have a form called device-form, which is called from the dashboard using routing. The device form has inside 4 tabs (angular material), and the second one must show a Highchart component, when you select that tab, the container appears but not the chart.... if you wait, the chart will never show up, but if you click in any place the chart appears, also if you hover some item with mouse, or if you resize the screen!!
App-chart-box.component.html:
<mat-card fxFlex="100%" fxLayout="row">
<mat-card-title></mat-card-title>
<mat-card-content>
<mat-tab-group class="form-wizard tab-group">
<mat-tab *ngFor="let chart of charts; let i = index">
<ng-template mat-tab-label>
{{ chart.sensorTypeName }}
</ng-template>
<div class="tab-content">
<vr-chart class="card-img-top" [title]="chart.sensorTypeName" [yTitle]="chart.sensorTypeUnit" type="spline" [series]="chart.series"
[period]="period" [minThreshold]="minThreshold" [maxThreshold]="maxThreshold"></vr-chart>
</div>
</mat-tab>
</mat-tab-group>
</mat-card-content>
<mat-card-actions>
<small class="footer">Last updated: {{ dateUpdated }}</small>
</mat-card-actions>
device-form.component.html:
<mat-tab>
<ng-template mat-tab-label>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<span>Statistics</span>
</div>
</ng-template>
<div fxFlex="100%" class="shipping-address" fxLayout="column">
<mat-form-field class="select-period">
<mat-select placeholder="{{ 'DEVICE_FORM.PERIOD' | translate }}" [(ngModel)]="filterPeriodSelected" (change)="loadDeviceChart()">
<mat-option *ngFor="let period of chartPeriods" [value]="period.value">
{{ period.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<!-- <div *ngIf="deviceCharts.length == 0" class="alert bg-primary alert-dismissible fade show" role="alert">
<strong class="ng-tns-c6-44"> {{'MESSAGE.NO_DATA_TO_SHOW' | translate}}</strong>
</div> -->
<vr-chart-box *ngIf="deviceCharts" class="fix-width" [charts]="deviceCharts" [period]="filterPeriodSelected"></vr-chart-box>
</div>
</mat-tab>
As you see the app-chart-box component is rendered, actually we can always see the footer loaded, always, but the body of the chart dont appear until click or hover o whatever.
Update:
Actually Im having the very same issue with a table which is filled with data coming from an API when you change a dropdown up there in the main dashboard page, the table component is always listening to .onCompanyChange.subscribe, read all the data, but the table is not showing until you make click or hover or whatever event... this is the code :
Table component:
export class CompanyHistoryComponent implements OnInit {
#ViewChild('tableInput') tableInput: ElementRef;
#Input() companyId = 0;
private rows: Observable<Array<any>>;
// public rows: any[] = [];
resultsLength: number;
dataSource: ListDataSource<any> | null;
database: ListDatabase<any>;
tableHover = true;
tableStriped = true;
tableCondensed = true;
tableBordered = true;
constructor(
public sensorService: SensorService,
public dashboardService: DashboardService,
private cd: ChangeDetectorRef,
) {
// Selected company changed
this.dashboardService.onCompanyChange.subscribe(
(id: number) => {
this.companyId = id;
this.loadSensorHistory();
cd.detectChanges();
});
}
public loadSensorHistory() {
const sDate: Number = moment().subtract(30, 'day').unix()
const eDate: Number = moment().unix();
this.sensorService.getSensorDataHistory(this.companyId, sDate, eDate, null, null, 1, 10).subscribe(response => {
if (response.statusCode !== 200 || !response.data) {
// this.toastrService.error('MESSAGE.NO_DATA', 'SENSOR_FORM_LIST.TITLE', { positionClass: 'toast-top-right', closeButton: true });
} else {
this.rows = response.data.list;
}
});
}
ngOnInit() {
}
As you see this time I added detectChanges() without any results :(
let me know if you have an idea.
This is the HTML of the Table :
<table class="table" [class.table-hover]="tableHover" [class.table-striped]="tableStriped" [class.table-condensed]="tableCondensed"
[class.table-bordered]="tableBordered">
<thead>
<tr>
<th>Date</th>
<th>Device</th>
<th>Sensor</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows" [class.row-failed]="row.failed > 0">
<td>{{ row.data_createdDate | date:'MM/dd/yyyy HH:mm:ss a Z' }}</td>
<td>
{{ row.deviceMAC }}
</td>
<td>
{{ row.sensorNumber }} - {{ row.sensorName }}
</td>
<td>{{ row.value }}</td>
</tr>
</tbody>
</table>
You should call ChangeDetectorRef detectChanges()
It might be an incorrect pipe operator.
I had a similar issue with Angular 11 and mat-table. The rows wouldn't show up until I hovered over them. My issue was that I was using an invalid pipe operator on one of the columns. I was using a decimal pipe on a string column. Once I corrected that it works perfectly.