Angular2 table is not refreshing data - angular2-template

I have an Angular2 application which is getting Json data and displaying it as a table. When I try to refresh / Update it with new data, table is showing old data only.
getting data through service like below:
#Input()
participants:Participant[];
testClasses: TestClass[] = [];
ngOnChanges() {
let codes="";
for (let item of this.participants)
{
codes = codes.concat(item.Id).concat(",");
}
this.buildTable(codes);
}
buildTable(codes){
let url = this.serverUrl + "api/test?codes=" + codes;
// passing input participants as parameter in url
this.testService.getData(url)
.subscribe(data => this.onDataUpdate(data));
}
onDataUpdate(data: any) {
this.testClasses = data;
// here I am getting new/refreshed data.
}
displying in table like below:
<table id="testGrid">
<tr *ngFor="let testClass of testClasses">
<td>{{testClass.name}}</td>
<td *ngFor="let t of (testClass.Score)">
{{ t }}
</td>
</tr>
</table>
When page loads data first time, table is showing data, but when I try to refresh data, I can see new refreshed data in onDataUpdate funcion, but html table is not refreshing view with new data.
Any idea or help would be much appreciated.
I tried below link:
https://github.com/swimlane/ngx-datatable/issues/255
Angular 2 - View not updating after model changes

I made my own example and I was able to reproduce the behaviour.
So what going on here it's that you use an array as input. (participants:Participant).
And after what you said about your checkboxes its seems that you mutate your array. It has for effect that the reference of the array is still the same therefor angular don't detect a change. (by the way I don't know if it's an expected behaviour).
A solution to your problem could be to create a new array of participants at each change on checkboxes.
Something like that:
this.participants = this.participants.slice(0)
You could also look at this answer: Angular2 change detection: ngOnChanges not firing for nested object

Related

How to display "No data available" in datatable after deleting rows

I have datatable with action onclick delete function that deleting the row.
How can i display "no data available" in case i delete all the row?
Here's my code.
{
"data": "Action",
render: (data, type, row) => {
return `<div class="actionIcon"><a id="viewExamination" onclick="individualDetails('${row["NAME"]}', '${row["BIRTHDAY"]}', '${row["Phone Number"]}')" data-toggle="modal" data-target="#asdffdsddd" data-backdrop="false" data-keyboard="false"><i class="fa-solid fa-outdent"></i></a><a id="delThisData" onclick="deleteDetailsNewHired()"><i class="fas fa-trash-alt"></i> </a> </div>`;
}
}
function deleteDetailsNewHired() {
$("#delThisData").closest('tr').remove();
}
There is a difference between the data stored inside the DataTables object and the data you can see displayed in the web page. If you only delete a <tr> from the HTML, you have not deleted that row from the underlying DataTable.
See the DataTables API function row().remove().
Note the use of the draw() function as well, to re-draw the table data so you actually see the resuts of the remove() operation reflected in the HTML.
After that, you can take a look at the language.emptyTable option (which you set once as part of the table definition) if you want to use a custom message. By default, the message you see is:
No data available in table
But it can be anything you want.
This is a similar overall issue as noted here. It is important to understand the difference between the data stored in your DataTable and the data which happens to be rendered on the currently visible web page.

Looping through and sending to modal

I'm trying to understand why when I click on the button below I get three different objects before it says that invoice is undefined.
<tr :key="invoice" v-for="(invoice, index) in invoices">
<td><button #click.prevent="openEdit(index)" Edit Invoice</button></td>
</tr>
openEdit(index) {
var invoice = this.invoices.splice(index, 1)[0];
this._beforeEditingCache = Object.assign({}, invoice);
console.log(invoice);
Bus.$emit('editting', { invoice: invoice, phase: this.phase, editModalName: this.editModalName });
},
After a long time i think i understood that question
You want to open a modal with the values of table row that gets clicked.
*I made a jsfiddle for this.Take a look here :https://jsfiddle.net/Roland1993/eywraw8t/5415/
That fiddle is very simple.I suggest you to use the modal as child component.
But if you using vue it would be vuetiful to use vuetify.Take a look to this table which includes edit,delete and add new item. click here to see

Changing complex computed object in vue.js

I have complicated object for a table. Looks like this:
{
1510002000: {
date: "07.11.17"
hours: {
1510002000:{
activity:{
activity: "Тест",
color: "#00ff00",
end_at: 1510005600,
start_at: 1510002000,
type_id: 1
}
},
1510005600: {
...
},
...
}
},
....
}
This is a code from template that uses this object:
<tr v-for="date in this.tds">
<td>{{ date.date }}</td>
<td is="hour-td"
v-for="hour in date.hours"
:color="hour.activity.color"
:start_at="hour.activity.start_at"
:end_at="hour.activity.end_at"
:activity="hour.activity.activity"
:type_id="hour.activity.type_id"
>
</td>
</tr>
I evaluated it as a computed property, but I need to rerender table when parent component provides data assync, so I have a watcher for prop (prop called "activities"):
watch: {
activities: function(){
var vm = this;
let dth = new DateTimeHelper;
if (this.activities.length > 0){
this.activities.forEach(function(activity){
let dateTimestamp = dth.getDateTimestampFromTimestamp(activity.start_at); // just getting the key
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
});
}
console.log(vm.tds) // here I can see that the object is filled with new data
}
},
The problem is that the table doesn't rerender. More precisely, the component "hour-td" does not contain the new data.
Also I've tried to use Vue.set, but no success with that
Can you help me with the updating table? I've spent like 5 hours for refactoring and attempts.
Thanks in advance
SOLUTION
In my case there can be two states: there are activities, there are no activities. So I made two computed props for each case and render them separately and switch by v-if="activities.length"
I think that your problem is with Vue known issue for Change Detection Caveats (you can read here) with array direct assignation, that don't detect changes.
You should change this part of code (with direct array assignation):
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
With the Vue.set() option for arrays in order to detect the change and re-renders the component. It worked for me in differents occassions:
// Vue.set(object, key, value)
// something like this:
Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity.activity,1,activity.activity);
More info here: https://codingexplained.com/coding/front-end/vue-js/array-change-detection
Edit:
I see now that you said:
Also I've tried to use Vue.set, but no success with that
What you mean with: "no success" ? Can you share the code? I have the same issue and I resolved with Vue.set..
You can also take a look to vm.$forceUpdate(), try to execute after the last console.log or grab all the code inside a vm.$nextTick( [callback] ) in order to execute all the actions (load data in the table) and then, re-render the component on next tick.
More info here: https://v2.vuejs.org/v2/api/#vm-forceUpdate && https://v2.vuejs.org/v2/api/#Vue-nextTick
Edit 2:
I think that your problem is with the index of the array, you should take a look here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection .
Try changing the:
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
and simplify with:
if (vm.tds[dateTimestamp] && vm.tds[dateTimestamp].hours[activity.start_at]){
Vue.set( vm.tds, vm.tds.indexOf(vm.tds[dateTimestamp].hours[activity.start_at]), activity);
}
Hope it helps!

Aurelia ui-virtualization infinite-scroll-next function not being hit after data refresh

I'm using aurelia's ui-virtualization library to create a table using virtual-repeat and infinite-scroll-next. Html looks something like this:
<table>
<thead>
...
</thead>
<tbody>
<tr virtual-repeat.for="item of items" infinite-scroll-next="getMore">
...
</tr>
</tbody>
</table>
This works great, except I have certain ui components and interactions that update what is in my list array. Once that has be updated, the infinite-scroll-next function (getMore()) is never called. I update something like this:
update() {
let vm = this;
vm.apiService.getData(vm.filterOption)
.then(response => {
vm.items = response.content.items;
});
}
Where filterOptions are changed through the ui and apiService.getData() returns a promise from an http call. The data in the table updates correctly, but the infinite scroll is then broken.
Am I doing something wrong, or is this a bug in ui-virtualization?
Edit:
It appears there are some properties added to the array __array_observer__ and __observers__. Thinking overwriting the whole array and thus removing these properties might be causing the problem, I tried an approach where I just add or remove elements appropriately. This does not work either.
Edit:
It seems to fail if I leave fewer than 7 of the original elements in the array.

Detecting Selected Row in html table Knockout/ASP.NET MVC

I've loaded an ASP.NET MVC viewModel into KnockoutJS using ko.mapping.fromJS(Model).
My viewModel looks something like this:
public IEnumerable<FunkyThing>funkyThings;
public FunkyThing selectedFunkyThing;
I've then got in my HTML View a table which looks something like this
<tbody data-bind="foreach: funkyThings">
<tr>
<td data-bind="text:name"></td>
<td data-bind="text:funkiness"></td>
<td>
</td>
</tr>
</tbody>
and all is good with this table. Clicking the select funky thing link happily calls the selectFunkyThing function:
model.selectFunkyThing= function (funkyThing) {
window.location = '#Url.Action(MVC.FunkyThingController.Index())' + "?funkyThingID=" + funkyThing.funkyThingID();
};
which in turn refreshes the page. The MVC viewmodels is reloaded and selectedFunkyThing is populated with the selected FunkyThing and the knockout view models are then re-read from the MVC viewmodel. So far so good.
I then wanted to update the table to highlight the selected entry.
So I updated the tr line as follows:
<tr data-bind="css:{'selected': $root.isSelected()}">
and created the new knockout function:
model.isSelected = function (funkyThing) {
return funkyThing.funkyThingID== model.selectedFunkyThing.funkyThingID;
};
but... it's not working.
Chrome throws a javascript exception stating that the FunkyThing parameter is undefined.
Technically I figure I could solve it by changing the MVC viewModel to actually set a isSelected on each FunkyThing within the array. However I figure there's got to be away of doing this from Knockout?
You were close! I added the ko.utils.unwrapObservable call because I bet the funkyThingID is an observable and not just a straight property - you did this yourself in your selectFunkyThing function. You could just execute them as well. I like the verbosity of unwrap though.
model.isSelected = function (funkyThing) {
var thisId = ko.utils.unwrapObservable(model.selectedFunkyThing.funkyThingID);
return ko.utils.unwrapObservable(funkyThing.funkyThingID) == thisId;
};
and then in your document you actually have to execute this function when ko parses the binding
<tr data-bind="css:{'selected': $root.isSelected($data)}">
Are those properties not both observables, so you need to reference them as functions? You also need to make your function a computed observable, I think:
model.isSelected = ko.computed(function (funkyThing) {
return funkyThing().funkyThingID() == model.selectedFunkyThing().funkyThingID();
});