Why is my tabulator custom header filter changing on keyup? - tabulator

To reproduce:
Run the Code Snippet (recommend Full Page mode)
Hold Ctrl or Shift while clicking to select multiple 'species' in the table column header
Note that while the key is held down, the table data is filtered according to selected 'species'
Release the key, observe changes in filtered table data
I think this is probably related to https://github.com/olifolkerd/tabulator/issues/975 and that I need to do something to override default tabulator keypress events.
JSFiddle: https://jsfiddle.net/jjech/3th28pv0/229/
const speciesTypes = [ 'Human', 'Android', 'Betazoid', 'Klingon', 'Ferengi', 'Tamarian' ];
function multiSelectHeaderFilter(cell) {
var values = speciesTypes;
const filterFunc = (rowData) => {
return values.includes(rowData['species']);
}
const getSelectedValues = (multiSelect) => {
var result = [];
var options = multiSelect && multiSelect.options;
var opt;
for (var i=0, iLen=options.length; i<iLen; i++) {
opt = options[i];
if (opt.selected) {
result.push(opt.value || opt.text);
}
}
return result;
}
const onChange = () => {
var editor = document.getElementById('speciesSelector');
values = getSelectedValues(editor);
console.log("values: "+values);
cell.getColumn().getTable().removeFilter(filterFunc);
cell.getColumn().getTable().addFilter(filterFunc);
}
var select = document.createElement("select");
select.multiple = "multiple";
select.id = 'speciesSelector';
select.style = 'width: 100%';
speciesTypes.forEach(species => {
select.innerHTML += "<option id='"+species+"' value='"+species+"' selected='selected'>"+species+"</option>";
});
cell.getColumn().getTable().addFilter(filterFunc);
select.addEventListener('change',onChange);
return select;
}
var table = new Tabulator("#tabulator", {
layout:"fitColumns",
data:[ {name:'Geordi La Forge',species:'Human'}, {name:'Dathon', species:'Tamarian'}, {name:'Jean-Luc Picard', species:'Human'}, {name:'Worf, son of Mogh', species:'Klingon'}, {name:'Tasha Yarr', species:'Human'}, {name:'Data', species:'Android'}, {name:'Wesley Crusher', species:'Human'}, {name:'Jalad', species:'Tamarian'}, {name:'Lwaxana Troi', species:'Betazoid'}, {name:'Temba', species:'Tamarian'}, {name:'T\'Kuvma', species:'Klingon'}, {name:'Lore', species:'Android'}, {name:'Noonian Soongh', species:'Human'}, {name:'Darmok', species:'Tamarian'}, {name:'Reittan Grax', species:'Betazoid'}, {name:'Quark', species:'Ferengi'} ],
headerSort:true,
columns:[ {title:'Name',field:'name',sorter:'string'},{title:'Species',field:'species',sorter:'string',headerFilter:multiSelectHeaderFilter }, ],
});
//document.multiselect('#speciesSelector');
<html>
<head>
<link href="https://unpkg.com/tabulator-tables#4.5.3/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables#4.5.3/dist/js/tabulator.min.js"></script>
<link href="https://cdn.jsdelivr.net/gh/mneofit/multiselect/styles/multiselect.css" rel="stylesheet">
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/mneofit/multiselect/multiselect.min.js"></script>
<script></script>
<style>
.tabulator .tabulator-header,
.tabulator .tabulator-header .tabulator-col
{
overflow: unset;
}
</style>
</head>
<body>
<div id="tabulator"></div>
</body>
</html>

Fixed by adding headerFilterLiveFilter:false to the column definition.
columns:[ {title:'Name',field:'name',sorter:'string'},{title:'Species',field:'species',sorter:'string',headerFilter:multiSelectHeaderFilter,headerFilterLiveFilter:false }, ],
https://jsfiddle.net/jjech/3th28pv0/237/

Related

SharePoint online show or hide columns multiple fields required with same functionality

I'm working on the SharePoint online form with show or hide columns with the below code. it is working well but not able to combine three codes together and on the edit item form by default all the fields are getting hide even though as per the code it should show the fields based on selected dropdown value. if we change the values in the edit form again the show values are working fine.
along with this code i want the make all the visible fields are mandate fields with, any assistance would be really appreciated.
<script src="/sites/XXXXXX/XXXXXXXX/SiteAssets/jquery-1.7.2.min%20-%20Show%20or%20Hide.js" type="text/javascript"> </script>
<script type="text/javascript">
$(document).ready(function () {
$('nobr:contains("Person1")').closest('tr').hide();
$('nobr:contains("Person2")').closest('tr').hide();
$('nobr:contains("Dropdown")').closest('tr').hide();
$('nobr:contains("Dropdown1")').closest('tr').hide();
$('nobr:contains("Text")').closest('tr').hide();
$('nobr:contains("Dropdown2")').closest('tr').hide();
$('nobr:contains("Date column")').closest('tr').hide();
$('nobr:contains("Number filed")').closest('tr').hide();
$('nobr:contains("Dropdown3")').closest('tr').hide();
$('nobr:contains("Remarks")').closest('tr').hide();
$('nobr:contains("Dropdown4")').closest('tr').hide();
$('nobr:contains("number2)")').closest('tr').hide();
$('nobr:contains("Dropdown5")').closest('tr').hide();
$('nobr:contains("Dropdown6")').closest('tr').hide();
$("select[title='Status Required Field']").change(function () {
console.log("selection changed", $("[title='Status Required Field'] option:selected").text());
alert($("[title='Status Required Field'] option:selected").text());
if ($("[title='Status Required Field'] option:selected").text() != "submitted") {
$('nobr:contains("Person1")').closest('tr').hide();
$('nobr:contains("Person2")').closest('tr').hide();
$('nobr:contains("Dropdown")').closest('tr').hide();
$('nobr:contains("Dropdown1")').closest('tr').hide();
$('nobr:contains("Text")').closest('tr').hide();
$('nobr:contains("Dropdown2")').closest('tr').hide();
$('nobr:contains("Date column")').closest('tr').hide();
$('nobr:contains("Number filed")').closest('tr').hide();
$('nobr:contains("Dropdown3")').closest('tr').hide();
$('nobr:contains("Remarks")').closest('tr').hide();
$('nobr:contains("Dropdown4")').closest('tr').hide();
$('nobr:contains("number2")').closest('tr').hide();
$('nobr:contains("Dropdown5")').closest('tr').hide();
$('nobr:contains("Dropdown6")').closest('tr').hide();
}
else {
$('nobr:contains("Person1")').closest('tr').show();
$('nobr:contains("Person2")').closest('tr').show();
$('nobr:contains("Dropdown")').closest('tr').show();
$('nobr:contains("Dropdown1")').closest('tr').show();
$('nobr:contains("Text")').closest('tr').show();
$('nobr:contains("Dropdown2")').closest('tr').show();
$('nobr:contains("Date column")').closest('tr').show();
$('nobr:contains("Number filed")').closest('tr').show();
$('nobr:contains("Dropdown3")').closest('tr').show();
$('nobr:contains("Remarks")').closest('tr').show();
$('nobr:contains("Dropdown4")').closest('tr').show();
}
});
});
</script>
<script src="/sites/XXXXXX/XXXXXXXX/SiteAssets/jquery-1.7.2.min%20-%20Show%20or%20Hide.js" type="text/javascript"> </script>
<script type="text/javascript">
$(document).ready(function () {
$('nobr:contains("number2")').closest('tr').hide();
$('nobr:contains("Dropdown5")').closest('tr').hide();
$('nobr:contains("Dropdown6")').closest('tr').hide();
$("select[title='Dropdown4']").change(function () {
console.log("selection changed", $("[title='Dropdown4'] option:selected").text());
alert($("[title='Dropdown4'] option:selected").text());
if ($("[title='Dropdown4'] option:selected").text() != "Active") {
$('nobr:contains("number2")').closest('tr').hide();
$('nobr:contains("Dropdown5")').closest('tr').hide();
$('nobr:contains("Dropdown6")').closest('tr').hide();
}
else {
$('nobr:contains("number2")').closest('tr').show();
$('nobr:contains("Dropdown5")').closest('tr').show();
}
});
});
</script>
<script src="/sites/XXXXXX/XXXXXXXX/SiteAssets/jquery-1.7.2.min%20-%20Show%20or%20Hide.js" type="text/javascript"> </script>
<script type="text/javascript">
$(document).ready(function () {
$('nobr:contains("Dropdown6")').closest('tr').hide();
$("select[title='Dropdown5").change(function () {
console.log("selection changed", $("[title='Dropdown5'] option:selected").text());
alert($("[title='Dropdown5'] option:selected").text());
if ($("[title='Dropdown5'] option:selected").text() != "Yes") {
$('nobr:contains("Dropdown6")').closest('tr').hide();
}
else {
$('nobr:contains("Dropdown6")').closest('tr').show();
}
});
});
</script>```
As far as fields hidden on Edit form your document ready function is hiding the fields. There are alot of ways to get the form state. You can simply check location contains New ,Display or Edit form and hide your fields accordingly.
var isNewMode = document.location.pathname.indexOf("/NewForm.aspx") > -1;
var isDisplayMode = document.location.pathname.indexOf("/DispForm.aspx") > -1;
var isEditMode = document.location.pathname.indexOf("/EditForm.aspx") > -1;
if(isNewMode)
{
alert("New");
}
if(isDisplayMode)
{
alert("Display");
}
if( isEditMode)
{
alert("Edit");
}
You can use PreSaveAction() function to do your custom validation. This function is executed once you click on the submit button. return true to submit the form or else return false to stay in the same page.
For Validation Add the code as follows:
function PreSaveAction(){
if($('nobr:contains("someid")').val() == ''){
alert('Required field'); // some custom validation
return false;
}else{
return true;
}
}

htmlparser2 how to replace a tag by another custom tag with the same attributes that has the tag one

I need to change a tag for another tag with the same properties. For example change this:
<TagOne width="500" height="200">asdfasdf</TagOne>
to this:
<AnotherTag width="500" height="200">sometext</AnotherTag>
With this code I can see the attributes but i don't know how to replace the tag for the other one:
const htmlparser2 = require("htmlparser2");
const DomUtils = require("htmlparser2").DomUtils;
const htmlContent = `<html>
<head></head> <body> <div id="content">
<TagOne width="500" height="200" src="image1.jpg">asdfasdf</TagOne>
<p>asdfasdf</p> </div></body></html>`; const parser = new htmlparser2.Parser(
{
onopentag(name, attribs) {
if (name === "TagOne") {
if(attribs.width ){
var width = attribs.width;
}
if(attribs.height ){
var height = attribs.height;
}
var new_tag = `<AnotherTag width:`+width+`; height:`+height+`;">sometext</div>`;
}
},
ontext(text) {
console.log("-->", text);
},
onclosetag(tagname,new_tag) {
if (tagname === "amp-iframe") {
console.log("That's it?!");
}
},
},
{ decodeEntities: true } );
var content = parser.write(htmlContent);
parser.end();
I have tried to do this on the onclosetag function:
//htmlparser2.DomUtils.removeElement(tagname);
//parser.write(new_tag);
but it gives me an error, it is not the correct way but I can't find anything similar in the documentation. can someone help me? Thanks.

How to update paginated dgrid periodically

I am trying to display and refresh periodically some server data using dgrid and derivative of Request dstore. The data are paginated and needs to be updated periodically. As a first naive approach I tried to call Dgrid.refresh() with setInterval. However, that results in complete rebuilding of grid rows which visually creates flickering effect. Using Trackable to the store does not help. Can anyone advise me how to refresh rows in the dgrid which would only update changed rows?
Here is my code reproducing the issue:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>DGrid flickering on slow updates</title>
<style>
#import "./dojo-release-1.11.1/dojo/resources/dojo.css";
#import "./dojo-release-1.11.1/dijit/themes/claro/claro.css";
#import "./META-INF/resources/webjars/dgrid/1.0.0/css/dgrid.css";
#import "./META-INF/resources/webjars/dgrid/1.0.0/css/skins/claro.css";
html, body {
padding: 10px;
width: 100%;
height: 100%;
}
</style>
<script>
var dojoConfig = {
async:true,
baseUrl: "./",
packages:[
{ name:"dojo", location:"dojo-release-1.11.1/dojo" },
{ name:"dijit", location:"dojo-release-1.11.1/dijit" },
{ name:"dgrid", location:"META-INF/resources/webjars/dgrid/1.0.0" },
{ name:"dstore", location:"META-INF/resources/webjars/dstore/1.1.1" }
]
};
</script>
<script src="dojo-release-1.11.1/dojo/dojo.js"></script>
<script>
require(["dojo/parser",
"dojo/dom",
"dojo/_base/declare",
"dstore/Store",
"dstore/Trackable",
"dstore/Cache",
"dstore/Memory",
"dstore/QueryResults",
"dojo/Deferred",
"dgrid/Grid",
"dgrid/Keyboard",
"dgrid/Selection",
"dgrid/extensions/Pagination",
"dojo/domReady!"],
function(parser, dom, declare, Store, Trackable, Cache, Memory, QueryResults, Deferred, Grid, Keyboard, Selection, Pagination)
{
parser.parse();
console.log("Parsed");
var makeSlowRequest =
function(kwArgs)
{
var responseDeferred = new Deferred();
var responsePromise = responseDeferred.promise;
// resolve promise in 2 seconds to simulate slow network connection
setTimeout(function ()
{
console.log("Generating response");
var data = {items: [], total: 100};
kwArgs = kwArgs || {start:0, end:100};
for(var i = kwArgs.start; i < kwArgs.end; i++)
{
data.items.push({id: "id-" + i,
name: "test-" + i,
value: Math.floor((Math.random() * 10) + kwArgs.start)
});
}
responseDeferred.resolve(data);
}, 2000);
return new QueryResults(responsePromise.then(function (data) { return data.items; }),
{ totalLength: responsePromise.then(function (data) { return data.total;}) });
};
var SlowStore = declare("SlowStore",
[Store, Trackable],
{ fetch: function (kwArgs) { return makeSlowRequest(kwArgs); },
fetchRange: function (kwArgs) { return makeSlowRequest(kwArgs); }
});
var store = new SlowStore();
var TestGrid = declare([Grid, Keyboard, Selection, Pagination]);
var grid = new TestGrid({
collection: store,
columns: {name: "Name", value: "Value"},
rowsPerPage: 10,
selectionMode: 'single',
cellNavigation: false,
className: 'dgrid-autoheight',
pageSizeOptions: [10, 20],
adjustLastColumn: true
}, dom.byId("grid"));
grid.startup();
// update grid every 5 seconds
var timer = setInterval(function(){console.log("Refreshing grid"); grid.refresh();}, 5000);
});
</script>
</head>
<body class="claro">
<div id="grid"></div>
</body>
</html>
I am guessing that instead of calling refresh I need get the request range from the dgrid (possibly using aspect on gotoPage) and call store.fetchRange(), iterate over the results, compare each item with previous results and invoke store.update or store.add or store.delete. I suppose that would give me what I want but before taking this approach I wondering if there is an easier way to refresh dgrid with updated data. Using Cache store does not work as it expects to fetch all the data from the server:
var store = Cache.create(new SlowStore(), {
cachingStore: new (Memory.createSubclass(Trackable))()
});

ext.net How to load JSON into Grid panel

I am using EXT.NET and have a question. I have a trouble to load json data into grid panel. My code is below. When tree item is clicked, selectNode function fires properly, but the JSON result can't be loaded into Grid panel.
How can I load into the Grid panel? Currently, white blank page pops up.
#model System.Collections.IEnumerable
#{
Layout = null;
var X = Html.X();
}
<script type="text/javascript">
var selectNode = function (item, record, node, index, event) {
Ext.Ajax.request({
url: "/Popup/GetDepartmentUser",
method: "POST",
params: {
id: record.data.id
},
callback: function (options, success, response) {
if (Ext.decode(response.responseText).success == false) {
Ext.Msg.alert("Failed", response.responseText);
} else {
var grid = App.theGrid;
grid.show();
grid.getStore().loadData(Ext.decode(response.responseText));
//Ext.Msg.alert("Success", response.responseText);
}
},
failure: function (response, options) {
Ext.MessageBox.alert("Failed", response.responseText);
},
timeout: '10000'
});
}
</script>
<!DOCTYPE html>
<html>
<head>
<title>Search User</title>
</head>
<body>
#(Html.X().ResourceManager())
#(X.Panel()
.Layout(LayoutType.Border)
.Width(780)
.Height(460)
.Items(
X.TreePanel()
.Title("Department")
.ID("theTree")
.Region(Region.West)
.Width(250)
.Height(460)
.Border(false)
.Split(true)
.UseArrows(true)
.Listeners(l => l.ItemClick.Fn = "selectNode")
.Store(
Html.X().TreeStore()
.Proxy(
Html.X().AjaxProxy().Url(Url.Action("GetDepartmentChildren"))
)
)
.Root(
Html.X().Node().NodeID("0").Text(ViewBag.OrganizationName)
),
Html.X().GridPanel()
.ID("theGrid")
.Title("User Information")
.Region(Region.Center)
.Width(530)
.Height(460)
.Border(false)
.Store(Html.X().Store()
.AutoLoad(false)
.Model(Html.X().Model()
.Fields(
new ModelField("FirstName", ModelFieldType.String),
new ModelField("LastName", ModelFieldType.String),
new ModelField("Email", ModelFieldType.String)
)
)
//.DataSource(Model)
.Reader(reader => reader.Add(Html.X().JsonReader().Root("data")))
)
.ColumnModel(
Html.X().Column()
.Text("First Name")
.DataIndex("FirstName")
.Flex(1),
Html.X().Column()
.Text("LastName")
.DataIndex("LastName")
.Width(70)
.Align(Alignment.Center),
Html.X().Column()
.Text("Email")
.DataIndex("Email")
.Width(140)
)
.View(Html.X().GridView().StripeRows(true).TrackOver(true))
)
)
</body>
</html>
Here is JSON result data
{
"success":true,
"total":2,
"data":"[
{
\"FirstName\":\"BlahBlah1\",
\"LastName\":\"Kwak\",
\"Email\":\"BlahBlah1#hotmail.com\"
},
{
\"FirstName\":\"BlahBlah2\",
\"LastName\":\"Kwak\",
\"Email\":\"BlahBlah1#hotmail.com\"
}
]"
}
I found that loadData method reads only Model record data.
So, I fixed it by using loadRawData method instead loadData.

How to create a "carousel"-like widget in spotify apps API?

Is it possible using the spotify apps API to create one of these widgets filled with my data of choice?
Yes, by using import/scripts/pager. Here's an example, extracted and simplified from the "What's New" app. Your pager.js:
"use strict";
sp = getSpotifyApi(1);
var p = sp.require('sp://import/scripts/pager');
var dom = sp.require('sp://import/scripts/dom');
exports.init = init;
function init() {
var pagerSection = dom.queryOne('#pager');
var datasource = new DataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
var options = {
perPage: 5,
hidePartials: true,
orientation: 'vertical', // 'vertical', 'horizontal'
pagingLocation: 'top', // 'top', 'bottom'
bullets: false,
listType: 'list', // 'table', 'list'
context: 'aToplist' // some string unique for each pager
};
var pager = new p.Pager(datasource, options);
pager.h2.innerHTML = "Example Pager";
dom.adopt(pagerSection, pager.node);
}
function DataSource(data) {
var data = data;
this.count = function() {
return data.length;
};
this.makeNode = function(index) {
var dataItem = data[index];
var li = new dom.Element('li');
var nameColumn = new dom.Element('div', {
className: 'nameColumn',
html: '<div class="nameColumn">'+
'Name' + dataItem + ''+
'Creator' + dataItem +''+
'</div>'
});
dom.adopt(li, nameColumn);
return li;
};
}
Your index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="pager.css">
</head>
<body onload="sp = getSpotifyApi(1); sp.require('pager').init();">
<div id="wrapper">
<section class="toplists" id="bottomToplists">
<section id="pager" class="playlists playlistsTable toplist"></section>
</section>
</div>
</body>
</html>
And lastly, copy the whatsnew.css into your project and rename it to pager.css. You will of course need to clean up the css and modify the elements in your index.html to fit with your app but this is a good starting point.
The "What's New" app also has an example of a horizontal pager with album artwork. Take a look at this question and answer to figure out how to extract the source of the app.
Also note that I am not sure whether the pager.js will be part of the public API. If not then you can of course extract it into your own pager widget and use it anyway.

Resources