Trying to solve performance issue with vuejs table - javascript

So I have a table with 50 columns and about 4-600 rows and laggy poor performance. There are some computed properties in use. The tables cells are inputs, selects and textareas.
I use the vue-scrolling-table, because I need vertical and horizontal scrolling, and fixed first column. But the performance is really bad.
I also have the same setup, somewhere else is the app, but it doesn't have the same performance issues.
I already tried to use a virtual list, but I could'nt get it working with the vue-scrolling-table/ (the header didn't follow the scrolling, and the vertical scroller was only available if I scroll to the bottom)
I also looked at virtual scroller but it recomanded not to use with inputs and such, since it is replacing the data inside the input, and that would trigger the change event etc.
I have a v-for, that goes through the data, and one v-for inside, for a subset in the columns. (I can't be sure how many columns there will be.)
<template slot="tbody">
<tr v-for="(row,index) in table" :key="index">
<td>
<input v-model="table[index].someItem">
</td>
<td>
<select v-model="listyThing">
<option v-for="item in list">{{item}}<option>
</select>
</td>
...
# I have some computed stuff ,(like 4)
SomeComputedFunction: function() {
return this.table.map((row) => {
return row.some * row.somethingelse
})
}
This should work fine. The data I'm working with is not that big, that it should cause problems. I try to do anything in this, and the cpu spins up to 100% and everything happens with 2s delays.
Also one interesting thing I found, the selects open in like 3 secs. I thought that was curious.
When I used the virtual list the performance was better, but I need some other solution for the table.
Does anyone have an idea what is the difference between this, and my other one which works just fine (more data and more computed properties actually)
So what I think the difference can be is the selects and the v-for rendered columns (about 20 of them rendered this way). Or maybe the textareas?
Thanks in advance!

Related

angular js, accessibility , tables, keyboard arrow keys navigation

I got an old angular js application and my mission is to fix accessibilty problems. I have a table that includes radio buttons within the cells and I need to navigate between them using the arrow up and down keys.
I already have a method that deals with this issue and its working fine until i test it using screen reader (NVDA)
What happens is for some reason the SR cause the method to be ignored
and causes the table to lose its focus.
the only way that I managed to make it work is by setting the table with role="application".
but from what I have been reading so far it is wrong to use it in this case
This is a sample of one of the cells :
<td class="scheduleLineCell" style="text-align: center;">
<input type="radio"
class="btnRadio ng-valid ng-not-empty ng-touched ng-dirty"
aria-describedby=""
value="126" data-ng-model="selectedIdList[childSchedule.gridIndex]"
data-ng-key-press="scheduleKeyPress"
data-ng-grid-index="0"
xng-focus="isFocusOnStationButton == false &&
isFocusOnMapButton == false && ScheduleLine.Id ==
selectedIdList[search.currentScheduleGridIndex]" name="2096">
</td>
Can you make a working example on here or on JSfiddler?
Off the top of my head I can say this.
You should not put controls in tables or uses tables for design purposes.
a. If you have an enter data row make it from "div"s
b. Remember screen readers repeat table headers a lot!
JAWS and NVDA does mess with a screens JSEvents and get us a example page to help
(AngularJS) Refactor the control back to a component or directive and see if that helps.
a. e.g.
<custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>

How to display a large list with checkboxes inside

I've been working on some webapp where i have a table which is filled with some data and to select which data should be displayed i use filters. These filters are <div> elemtens which contain one <div> per filter option like so:
<div id="selectedFilter">
<div>
<input id="selectedFilter1" type="checkbox" name="selectedFilter" multiple="true" value="1" checked="true">
<label for="selectedFilter1" title="Option1">Option1</label>
</div>
<div>
<input id="selectedFilter2" type="checkbox" name="selectedFilter" multiple="true" value="2" checked="true">
<label for="selectedFilter2" title="Option2">Option2</label>
</div>
<div>
The html code for the options (the whole <div> with <label> and <input> ) is generated in my java backend and gets transferred to the site like this:
$.post("filter", result, function(data) {
$("#selectedFilter").html(data);
});
Now my problem:
The amount of filters gets sometimes really high (e.g. 800 rows) and there are two more filters on the page with about the same amount of options. With such a load of data the browser begins to hang and becomes unresponsive.
To fix this i tried using virtual scrolling since this seems to be the most reasonable way to keep the browsercache on a good level. But this clears the checkboxes, if i render the whole div again.
On top of that i need to know which checkboxes are checked even if they are not in the viewport because the filters are influencing each others possible options in a cascading manner (Filter1 influences Filter2 influences Filter3). If one filter changes (something gets selected) the subsequent one (or two) gets cleared and requests new data from the java backend.
Question:
Is there maybe a js-library which supports the virtual scrolling but manages the checkboxes by itself so i can extract just the checked ones? Something like this.
I might try to save which chechbox was selected but i fear that the virtualisation of the scrolling might be very slow if there is a matching of the displayed options whether they are checked or not in every single scroll.
Does anybody have suggestions or experiences?
You are saying you are having 800 filters , do you really think any user gonna see all those 800 or more filters together. The best thing is to categories your filters and then load them on click of category.
Virtual Scrolling is good idea. You need to append filter data as .html() is overriding your previous content.
$("#selectedFilter").append(data);

Highlight last inserted document in Meteor

I have form and list of objects at the same page. When I insert a new row, it is not very easy to see where the newly inserted row is placed. Therefore, I thought I could color/highlight the newly inserted row (and perhaps remove the highlight after a few seconds).
How can I do this? I think a way to do this could be using a method on the server which returns the inserted id (return Collection.insert(doc);) and on the client use a callback with
Meteor.call('insertDoc', function(err,result) {
// do something with result
});
I think I can use a reactive-var to save the id of the last inserted row and in the loop highlight the row with
{{#each docs}}
<li class="{{isActive}}">{{name}}</li>
{{/each}}
and have a helper to return active if this._id equals the reactive var with the last inserted id.
But is this the best way to do it? How can I remove the color after some seconds? I have seen such behaviour on many pages but I cannot find any tutorials/code snippets to achieve this.
I wrote a package that uses Meteor's UI hooks to fade items in and out of a list as they are added and removed, to help users maintain context as data changes:
https://github.com/mizzao/meteor-animated-each
There is a demo at http://animated-each.meteor.com/. You can see that as items are added and removed, they are faded in and out. If items are inserted off the screen, the visible area does not scroll.
This isn't doing exactly what you want, but you can use the same idea to highlight items as they appear as well, as opposed to the simple fade in.
Note that all of this happens at the UI rendering level - not the template/code level. The UI hooks are also not well documented right now, but they've been around for a while.
I don't know if your method is the best, but that's how I'd go about doing it.
As for the animation, I'd use a CSS3 animation. Plenty to choose from ( https://developer.mozilla.org/en-US/docs/Web/CSS/animation ), and you can easily make them fade to the standard color. The animation would also only be applied to the last inserted item (because of the way you did it, only the last item would have the "active" class)

Efficient way to bind once but allowing to refresh the whole items

Let's suppose a list of 1000 items displayed with infinite scrolling.
Each item displays: a person's firstName, lastName, and mood. (to make it simple)
Initially, I didn't want to listen for updates.
So the great angular-bindonce directive or even better: angular 1.3 one-binding feature made the trick.
Now, I created a pull-to-refresh component, allowing to refresh the whole items.
However, as binding once, (and not reloading the page) my whole list didn't take the updates in account.
Using angular-bindonce, I have this currently:
<div bindonce ng-repeat="person in persons track by person.id">
<span bo-text="person.firstName"></span>
<span bo-text="person.lastName"></span>
<span bo-text="person.currentMood"></span>
</div>
The pull-to-refresh triggers this function:
$scope.refresh() {
Persons.getList(function(response)) {
$scope.persons = response.data; //data being an array
}
}
Question is:
Is there a way to refresh all the data ONLY when the pull-to-refresh is triggered?
In this case, I would be able to keep this one-binding that would greatly improve performance when dealing with huge lists of persons.
Until now, I'm forced to....use two-way binding, the natural way of Angular works.
More generally, how to deal with huge lists with infinite scrolling that needs to be updated only when some events are triggered?
Get angular-bind-notifier.
Use native bindings (with a somewhat modified syntax) and setup your markup like so:
<div ng-repeat="person in persons track by person.id" bind-notifier="{ eventKey:watchedExpression }">
<span>{{:eventKey:person.firstName}}</span>
<span>{{:eventKey:person.lastName}}</span>
<!-- or with ng-bind if you prefer that -->
<span ng-bind=":eventKey:person.currentMood"></span>
</div>
Now, whenever the value of watchedExpression changes - a $broadcast will be sent down through the childscope created by bind-notifier and tell every binding with the :key:expr syntax to re-evaluate.
If you need to, you can also send the $broadcast manually in the following format:
$scope.$broadcast('$$rebind::' + key) // where 'key' === 'eventKey' in the example above.
refresh-on directive could do the trick, found a reference HERE:
<div bindonce="persons" refresh-on="'refresh'" ng-repeat="person in persons track by person.id">
<span bo-text="person.firstName"></span>
<span bo-text="person.lastName"></span>
<span bo-text="person.currentMood"></span>
</div>
Instead of trying to work around not using two-way binding but still have all of its benefits there is more likely and easier solution. You say that there are 1,000 rows, are all 1,000 rows with the viewport / visible to the user at once?
I would assume not, so I would suggest using a buffered view for the list of items. Buffering the rows would mean that the rows that are not visible have no bindings but still take up space in the DOM so the scroll bar is always accurate.
The one major caveat of buffering is that all rows should be the same height, no variable height rows.
Here are some virtual scrolling / buffering directives to take a look at:
https://github.com/EnzeyNet/VirtualScroll
https://github.com/stackfull/angular-virtual-scroll
https://github.com/kamilkp/angular-vs-repeat

myGrid.setStore in nested div

I am trying to develop a ersi map javascript page.
I am creating
var myData = { items: myItems };
var myStore = new dojo.data.ItemFileReadStore({ data: myData });
These create correctly and have the correct data in
I have a problem updating a dojox.grid.DataGrid with the myGrid.setStore(myStore);
The code I have for the grid is:
<table dojotype="dojox.grid.DataGrid" jsid="myGrid" noDataMessage="No results found in the current map extent" style="width:100%;height:100%;" selectionMode="none">
<thead>
<tr>
<th field="NAME" width=75%>Neighborhood</th>
<th field="ownPct" width=25%>% Own</th>
</tr>
</thead>
</table>
This table in nested in a number of divs in the HTML.
I suspect that when I run myGrid.setStore(myStore); it cannot find the table in the DOM.
If I move the table to be out of any DIV's and have it directly after the <body> tag (see link). the code will work and populate the table. If I move the table to the end of the HTML just before the </body> tag it will work (see link). But when I put the table in the nested Divs then it will not populate (see link). this should be on the policy tab!!
I hope i make sense, this has been driving me crazy
thanks in advance Paul
I think the problem is not with setStore, but one or two other things.
When the DataGrid is instantiated, it attempts to detect the space available (to determine how many rows to render etc). You've told it to be 100% of its parent div, but remember that divs have height 0px by default!
So, you can try to give the grid (or gridDiv) an explicit height. However, the grid is also in an inactive tab so it still detects it has no height (it's invisible, after all!).
So, next you can try to set the grid's tab as the active one (i.e. <div class="tab-pane active" id="policies">). That should give you a rendered grid when the page loads.
Alas, you don't want the grid's tab to be the active one. So the final solution is that you need to explicitly tell the grid to recalculate its height (myGrid.resize()) when the policy tab is being activated. I haven't used bootstrap's tabs, so I don't know exactly how to do that - perhaps you do?

Categories

Resources