Triggering an event from a custom formatter - javascript

I want to generate a table from a JSON objects array. Each object represents an application and has 4 properties: name, package, versions, and users.
name and package are textual, and users is numeric. versions, however, is an array of arrays: each internal array contains a version name and a version code. Here is a sample JSON:
[{
"name": "Angry Birds",
"package": "oldgames",
"versions": [
["alpha", 0.1],
["beta", 0.2],
["release", 1]
],
"users": 800
},
{
"name": "Temple Run",
"package": "oldgames",
"versions": [
["beta", 0.7],
["release", 2]
],
"users": 130
},
{
"name": "Snake",
"package": "veryoldgames",
"versions": [
["release", 0]
],
"users": 2
}]
The table requirements are as follows:
There should be 4 columns corresponding to the mentioned properties: Name, Package, Version (singular), and Users.
The Version column should contain a dropdown (an HTML select), with each option referring to a specific version. In addition, each dropdown contains an "All" option to refer to all the versions of the app.
The text of each option is the version name, and the value is the version code.
When a specific version is selected, an external function is called and returns the number of users that uses this specific version. Then, the row's Users cell is updated with the new number.
(I know it's a bit convoluted, and that there are probably better ways to visualize the data, but right now this is the architecture I have to implement and I can't change it)
To create the dropdown, I tried using the built-in List editor. I soon found out this is the wrong approach since the List editor is constructed with predefined values - identical values ​​for each and every row in the table, while I need every row to have a unique dropdown.
After researching a bit, I realized I needed to use a Custom Formatter. I take the versions array and manually construct a select. Here is a demo:
const data = [
{
"name": "Angry Birds",
"package": "oldgames",
"versions": [["alpha", 0.1], ["beta", 0.2], ["release", 1]],
"users": 800
},
{
"name": "Temple Run",
"package": "oldgames",
"versions": [["beta", 0.7], ["release", 2]],
"users": 130
},
{
"name": "Snake",
"package": "veryoldgames",
"versions": [["release", 0]],
"users": 2
}
];
new Tabulator("#example-table", {
data: data,
layout: "fitColumns",
height: 107,
columns: [
{
title: "Name",
field: "name",
},
{
title: "Package",
field: "package"
},
{
title: "Versions",
field: "versions",
formatter: function (cell, formatterParams, onRendered) {
const select = document.createElement("select");
const options = select.options;
options.add(new Option("all", "all"));
const value = cell.getValue();
for (let i = 0; i < value.length; i++) {
options.add(new Option(value[i][0], value[i][1]));
}
return select;
}
},
{
title: "Users",
field: "users"
}
],
});
<script src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet"/>
<div id="example-table"></div>
The data is displayed properly. Initially, I planned to bind a change event listener to the select, but the documentation recommends not doing that:
...it is a bad idea to try to use code outside of Tabulator to
directly alter or bind events to DOM elements inside the table,
because there is a good chance that the element you are trying to
manipulate will be destroyed on the next scroll.
Instead, the documentation recommends using its predefined events:
Tabulator has a wide range of callbacks, formatters and other
functions that allow you to manipulate the table contents in a way
that is safe and won't be affected by the rows being recreated.
The closest event I could find is cellEditing. However, I don't know how to trigger it when the value of the select changes.
What is the recommended way to implement these requirements?

Related

wix programmatically connect a table to a product page

Hello I have made a web store using the Wix platform and I am having an issue connecting my custom coded table to a product page. The way I have it is the customer can search the products by title and the results populate a table with custom fields. The issue I am having is I want the customer to be able to click on a row and that will navigate them to the product page displaying the product they have clicked. here is my table in JSON object form which I got from the API docs and replace my own properties (works fine):
$w('#table1').columns = [{
"id": "col1", // ID of the column for code purposes
// The field key in the collection whose data this column displays
"dataPath": "mainMedia",
"label": "Image", // The column header
"width": 100, // Column width
"visible": true, // Column visibility
"type": "image", // Data type for the column
// Path for the column if it contains a link
"linkPath": "link-path-or-property" //<this is what the doc says
},
{
"id": "col2",
"dataPath": "name",
"label": "Name",
"width": 350,
"visible": true,
"type": "text",
"linkPath": "this is where I should have a link I think but what link"
}, {
"id": "col3",
"dataPath": "formattedPrice",
"label": "Price",
"width": 100,
"visible": true,
"type": "text",
"linkPath": "ProductPageUrl"
}, {
"id": "col4",
"dataPath": "sku",
"label": "SKU",
"width": 100,
"visible": true,
"type": "text",
} //,
// more column objects here if necessary
// ...
// ...
];
Then I use the built in function for my click event:
export function table1_rowSelect(event, $w) {
//Add your code for this event here:
console.log(event.rowData); //It does read the correct item clicked
}
Is this even possible?
You're in the right direction. Yes, linkPath is where you put the link to the product page. (You could also use the rowSelect event with wixWindow.to(), but you don't need to do both.) Now all you need to do is figure out the correct path to use in linkPath.
Looks to me like you're using a Wix Stores collection for your row data. If so, when setting the table columns, you use the field key (not field name) of the field that contains the link. So for the product page, use productPageUrl. Note, that this is per column. So if you want each cell to be a link, you have to add the linkPath to every column.

How can I create divs with conditional selects in angular 2?

Here is my html component:
<div *ngFor="let chart of barChartTypeSelect">
{{chart.mainChart}}
<select [(ngModel)]="selectedRepositoryName" (ngModelChange)="onRepositorySelected()">
<option *ngFor=" let repository of barChartTypeSelect" [value]="repository.mainChart">
{{repository.mainChart}}
</option>
</select>
</div>
In my typescript file, I defined the following:
private onRepositorySelected() {
this.selectedRepository = this.barChartTypeSelect.find(repository => repository.mainChart === this.selectedRepositoryName);
}
What I am trying to achieve is to create dynamic divs based on a json file that I parsed. Each div contains a chart with a conditional selects to adjust it. By parsing the json file and being aware of each object, we can actually decide if we need to attach a selects or not (if the object only contains a key but without any value, then there is no selects).
In other words, the json object maps for each key on the first level to a chart. Every secondary level is a select option that redraws the chart on select, every third level is also a select option that only gets displayed if the second select is checked, and so on.
Here is the object parsed:
{
"consumerPriceIndex": [],
"grossDomesticProductPercentage": [],
"inflationRate": [],
"newUnderCons": [
"apartment",
"building",
"villa"
],
"population": [
"quarter",
"year"
],
"populationChange": [
"quarter",
"year"
],
"transactionVolume": {
"apartment": [
"quarter",
"year"
],
"total": [
"quarter",
"year"
],
"villa": [
"quarter",
"year"
]
},
"transactionVolumeOilIndex_total_quarter": []
}
I can always hard code the values, but I want to achieve the mvc structure as closely as possible. Thank you.

Javascript library for table rendering

I need to show an array of objects in the table like representation. Table has columns with the properties, and when clicked on the column it should show more data inside the table. It should be sortable.
Is there a JS library that could do this, so I dont have to write this from scratch?
Please see the attached image with the JSON object.
When the user clicks on Ana, additional row is inserted.
I created the demo https://jsfiddle.net/OlegKi/kc2537ty/1/ which demonstrates the usage of free jqGrid with subgrids. It displays the results like
after the user clicks on the "+" icon in the second line.
The corresponding code you can find below
var mydata = [
{ id: 10, name: "John", lname: "Smith", age: 31, loc: { location: "North America", city: "Seattle", country: "US" } },
{ id: 20, name: "Ana", lname: "Maria", age: 43, loc: { location: "Europe", city: "London", country: "UK" } }
];
$("#grid").jqGrid({
data: mydata,
colModel: [
{ name: "name", label: "Name" },
{ name: "lname", label: "Last name" },
{ name: "age", label: "Age", template: "integer", align: "center" }
],
cmTemplate: { align: "center", width: 150 },
sortname: "age",
iconSet: "fontAwesome",
subGrid: true,
subGridRowExpanded: function (subgridDivId, rowid) {
var $subgrid = $("<table id='" + subgridDivId + "_t'></table>"),
subgridData = [$(this).jqGrid("getLocalRow", rowid).loc];
$("#" + subgridDivId).append($subgrid);
$subgrid.jqGrid({
idPrefix: rowid + "_",
data: subgridData,
colModel: [
{ name: "location", label: "Localtion" },
{ name: "city", label: "City" },
{ name: "country", label: "Country" }
],
cmTemplate: { align: "center" },
iconSet: "fontAwesome",
autowidth: true
});
}
});
Small comments to the code. Free jqGrid saves all properties of input data in data parameter. I added id property to every item of input data. It's not mandatory, but it could be helpful if you would add more functionality to the grid. See the introduction for more details.
The columns are sortable based on the type of the data specified by sorttype property of colModel. To simplify usage some standard types of data free jqGrid provides some standard templates which are shortcurts for some set of settings. I used template: "integer" in the demo, but you could replace it to sorttype: "integer" if only sorting by integer functionality is important.
If the user click on "+" icon to expand the subgrid then jqGrid inserts new row and creates the div for the data part of the subgrid. You can replace subGridRowExpanded from above example to the following
subGridRowExpanded: function (subgridDivId) {
$("#" + subgridDivId).html("<em>simple subgrid data</em>");
}
to understand what I mean. The unique id of the div will be the first parameter of the callback. One can create any common HTML content in the subgrid. Thus one can create empty <table>, append it to the subgrid div and
then convert the table to the subgrid.
To access to the item of data, which corresponds to the expanding row one can use $(this).jqGrid("getLocalRow", rowid). The return data is the item of original data. It has loc property which we need. To be able to use the data as input for jqGrid we create array with the element. I's mostly all, what one have to know to understand how the above code works.
You can add call of .jqGrid("filterToolbar") to be able to filter the data or to add pager: true (or toppager: true, or both) to have the pager and to use rowNum: 5 to specify the number of rows in the page. In the way you can load relatively large set of data in the grid and the user can use local paging, sorting and filtering. See the demo which shows the performance of loading, sorting and filtering of the local grid with 4000 rows and another one with 40000 rows. All works pretty quickly if one uses local paging and not displays all the data at once.
I use datatables.net for all my "more complex than lists"-tables. I It's a very well kept library with loads of features and great flexibility.
In the "con" column I would say that it's so complex that it probably has quite a steep learning curve. Although the documentation is great so there is always hope for most problems.

Dojo combo box specify label and value

I am trying to follow this example on how to setup a combo-box using dojo, but wondering how one can specify name and value programmatically. The example presented uses the same values for label and value - which is probably not one wants in most cases.
{
"identifier": "abbreviation",
"label": "name",
"items": [
{ "abbreviation": "AL", "name": "Alabama" },
... other 48 states here ...
{ "abbreviation": "WY", "name": "Wyoming" }
]
}
If you are asking how to replace the hard coded list in the example then here is what you have to do. In the above scenario items was used to specify the data which is an array (abbreviations and names) of values.
In your case you will need to get the data / object from your data source. Once you have that data/object expose it to the view. Once this has been done you can now do the following structure.
You store is really your items above however stateStore will be a java script array which contains the data from your data source.
stateStore = [{"abbreviation": "AL", "name": "Alabama"},
... other 48 states here ...,
{ "abbreviation": "WY", "name": "Wyoming" }]
// create FilteringSelect widget, populating its options from the store
var select = new dijit.form.FilteringSelect({
name: "stateSelect",
placeHolder: "Select a State",
store: stateStore
}, "stateSelect");
HTML
<div style="width:50%;float: left;">
<h1>dijit.form.Select</h1>
<label for="stateSelect">State:</label>
<div id="stateSelect"></div>
</div>

How can I index child object properties in an array using ydn-db-fulltext?

I'm using Ydn-Db-Fulltext to allow users to search a local database of contacts in an HTML5 app. So far, when it comes to searching for names of people, it works great, is smart, and returns results instantly.
Here's an example of a contact object that contains an array of contact Methods:
{
"source": "COMPANY",
"ownerPin": "12345",
"name": "brian wilkins",
"dateUpdated": "2014-03-18T14:41:05.217Z",
"dateAdded": "2014-03-18T14:41:05.217Z",
"isFavorite": false,
"socialId": "54321",
"id": "1",
"deleted": false,
"edited": false,
"favorite": false,
"contactMethods": [
{
"id": "4321",
"contactKey": "12321",
"contactId": "1",
"value": "brian.wilkins#geemail.com",
"kind": "email",
"ownerPin": "12345",
"isPrimary": false
},
{
"id": "5432",
"contactKey": "2",
"contactId": "1",
"kind": "phone",
"ownerPin": "12345",
"isPrimary": false
},
{
"id": "23",
"contactKey": "333",
"contactId": "1",
"value": "112345",
"kind": "extension",
"ownerPin": "12345",
"isPrimary": false
}
]
}
To create the index on the "name" property, I setup the fullTextCatalog as follows:
fullTextCatalogs: [{
name: 'name',
lang: 'en',
sources: [
{
storeName: 'contacts',
keyPath: 'id',
weight: 1.0
}, {
storeName: 'contacts',
keyPath: 'name',
weight: 0.5
}
]
}],
stores: [
{
name: 'contacts',
keyPath: 'id',
autoIncrement: true
}
]
};
this.db = new ydn.db.Storage('thedatabase', db_schema);
I can search by name or by id (the key) and get a list of contacts that match. Little appears to be stored in memory. Every search queries the local backing indexedDB database.
The challenge is that I also want to be able to search based on email address and extension, which are stored in the contactMethods property inside an array of contactMethods. The "value" property is where we store the email address and/or extension depending on the contactMethod type.
I tried adding contactMethods as a secondary searchable object store, but this resulted in searches for "Brian" returning two results, both the contact containing the name, and the contactMethod containing the email address. Ideally, I'd want to take the contactId (foreign key to the contact) and use it to pull the actual contact object, but it seems like this could create very expensive overhead and negate the benefits of this great search tool.
Is there a way to index object properties that are not at the parent level? How can I approach this in a way that would scale and not eat up all of the resources?
this.db.get(entry.storeName, entry.primaryKey).done(function(x) {
this.textContent += ' [Full name: ' + x.name + ']'; // this is in the contact
this.textContent += ' [email: ' + x.value + ']'; // but this is in the contactMethod
}, span);
Is there a way to index object properties that are not at the parent level?
keyPath can refer to deep object property by using dotted notation. For example, you could specify contactMethods.value to index email, but unfortunately it does not work with array value - as in this case.
So, obvious choice is keeping contactMethods record in separate object store using parent-child relationship. Since ydn-db currently does not support embedded attribute in the schema, you will have to load all child records when loading parent object.
Alternatively, IndexedDB v2 may have virtual index generated by a function expression. You can use in ydn-db by generator in index schema, for example:
stores: [
{
name: 'contacts',
keyPath: 'id',
autoIncrement: true,
indexes: [{
name: '$emails',
multiEntry: true,
generator: function(record) {
return record.contactMethods.map(function(x) {return x.value};
})
}]
}
]
One thing to note though, the generated field $emails will appear when you load the data. It likely will be removed from the record so as to match with v2 spec.
We are using this generator index heavily in multiple projects, so I will fix bug.
Indexing id and email address in full text search is convenient, but does not make sense because phonetic base full text search will be index them as it is without normalization.

Categories