wix programmatically connect a table to a product page - javascript

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.

Related

Triggering an event from a custom formatter

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?

How to get overall error status on Material UI DataGrid?

I am using Material UI DataGrid component to render EXCEL file. Each Excel file has several column Names and have specific types. For example:
const columns = [
{
"field": "uwgroup",
"headerName": "Group",
"minWidth": 200,
"editable": true
},
{
"field": "Amazing column Name ",
"flex": 1,
"minWidth": 150,
"editable": true
},
{
"field": "Building TSI",
"type": 'number',
"flex": 1,
"minWidth": 150,
"editable": true
},
{
"field": "dev",
"flex": 1,
"minWidth": 150,
"editable": true
}
]
The column Name Building TSI is of type number. And I am adding class name invalid using cellClassName, something like:
classnames({
invalid: !isPositiveNumber(params.value)
})
It works fine and renders class name and indicates error cells. The problem is, I want to count total number of error cells. The reason is, we only allow to save the grid values to the database, if there are no errors in any cells.
Solutions, I have tried so far:
Add state for errorCount and increment errorCount when I add class. This causes several re-renders and exceeds memory limit.
I tried to use document.getElementByClassNames('invalid') and check its length. It works only for the rendered item. That is to say, if excel file has more than 10 rows, it is paginated. The invalid cells count is only done for the currently rendered page.
I tried to use preProcessEditCellProps props to indicate error. However, I could not find anyway to get the total error cells count. Only thing, I could get out of this props is an ability to not allow user to enter incorrect value.
I even tried using localStorage. It has the exact same issue as solution number 2.
I would appreciate if anyone has faced similar scenario. It would be nice to get overall error cells count, so, I can enable to disable SAVE button.
One of the constraints that I have is the excel files are huge and contains on average of 30-40k rows and 25-40 columns. Adding state for each cells becomes less performant.
Thanks in advance!
Having another property in columns and referring to it before exporting for each cell/row can help.
In this example, invoke eligibleForExport function with the columns definiton and the actual data as parameters will give a boolean stating if error exists or not. Can be changed to count errors as well.
const isInvalidBuildingTSI=(value)=>!isPositiveNumber(value);
const isPositiveNumber=(num)=>num>=0;
const eligibleForExport=(columns,data)=>{
return !(data.find(row=>columns.find(column=>row[column.field]
&& typeof column["isValid"] === "function" && column["isValid"](row[column.field]))))
}
const columns = [
{
"field": "uwgroup",
"headerName": "Group",
"minWidth": 200,
"editable": true
},
{
"field": "Building TSI",
"type": 'number',
"flex": 1,
"minWidth": 150,
"editable": true,
"isValid" : isInvalidBuildingTSI,
"cellClassName":isInvalidBuildingTSI(param.value)?"invalid":""
}
];
If the initial data is always valid an easy way to solve your issue would be to follow the DataGrid documentation about clients side validation:
Client-side validation πŸ”—
To validate the value in the cells, first add a
preProcessEditCellProps callback to the column definition of
the field to validate. Once it is called, validate the value provided
in params.props.value. Then, return a new object contaning
params.props and also the error attribute set to true or false. If
the error attribute is true, the value will never be committed.
const columns: GridColDef[] = [
{
field: 'firstName',
preProcessEditCellProps: (params: GridEditCellPropsChangeParams) => {
const hasError = params.props.value.length < 3;
return { ...params.props, error: hasError };
},
},
];
For your scenario this would result in the following:
const columns = [
{
"field": "uwgroup",
"headerName": "Group",
"minWidth": 200,
"editable": true
},
{
"field": "Amazing column Name ",
"flex": 1,
"minWidth": 150,
"editable": true
},
{
"field": "Building TSI",
"type": 'number',
"flex": 1,
"minWidth": 150,
"editable": true,
preProcessEditCellProps(params) {
const invalid = !isPositiveNumber(params.props.value);
return { ...params.props, error: invalid };
}
},
{
"field": "dev",
"flex": 1,
"minWidth": 150,
"editable": true
}
]
There is an important difference with what you currently have. This validation only effects edits. So the initial data has to be valid. The advantage is that you no longer have to use classnames({ invalid: !isPositiveNumber(params.value) }) and the save button can always be enabled, since all committed changes can be assumed to be valid.
If the initial data can be invalid, this is probably not the answer you're looking for.

dependent dropdownlist with json and python

So I am making a dialog panel for my chat bot in django framework. The Dialog panel consists of intent and entities dropdown list and a dialog textarea. The dropdown list will be dependent on my training data which is in json format.
I want the dropdownlist so that if I choose intent, the entities dropdown list create itself automatically and show all the entities related to selected intent.
I have tried and I am able to show intent dropdown but that too had duplicate intents(which i removed using python set function).But I am unable to figure out how to show all entities based on one particular intent.
Help me. Here's my example json:
{"rasa_nlu_data": {
"common_examples": [
{
"text": "hey",
"intent": "greet",
"entities": []
},
{
"text": "yep",
"intent": "affirm",
"entities": []
},
{
"text": "i'm looking for a place to eat",
"intent": "restaurant_search",
"entities": []
},
{
"text": "i'm looking for a place in the north of town",
"intent": "restaurant_search",
"entities": [
{
"start": 31,
"end": 36,
"value": "north",
"entity": "location"
}
]
},
{
"text": "show me chinese restaurants",
"intent": "restaurant_search",
"entities": [
{
"start": 8,
"end": 15,
"value": "chinese",
"entity": "cuisine"
}
]
},
{
"text": "bye",
"intent": "goodbye",
"entities": []
}
]}}
Basically, all you have to do is loop over the items inside common_examples and check if the intent matches the selected value in the dropdown. If it does, add the entities to entities dropdown.
Since you haven't provided much info about your HTML, I'll try to answer with a few assumptions:
You've a select element with id intentDropdown to show intents.
You've a select element with id entitiesDropdown to show entities.
You're using jQuery.
The code contains some comments to explain what it does.
<!-- intents dropdown -->
<select id="intentsDrowpdown">
<!-- intent options-->
</select>
<!-- entities dropdown -->
<select id="entitesDrowpdown"></select>
<!-- Javascript -->
<script>
var data = {"rasa_nlu_data": { ... }}; // the json data
var totalExamples = data.rasa_nlu_data.common_examples.length; // total items inside common_examples
// listen to the event when selected value in
// the intent dropdown changes
$("#intentsDropdown").on('change', function() {
$("#entitiesDropdown").empty(); // clear the previously added entities from entities drowpdown
var selectedIntent = this.value; // currently selected intent
// loop over the items in common_examples
for (var i = 0; i < totalExamples; i++) {
var currentExample = data.rasa_nlu_data.common_examples[i] // current example in the loop
// see if the selected intent matches the
// intent of the current example in the loop
if (currentExample.intent == selectedIntent) {
// if intent matches
// loop over the items inside entities
// of the current example
for (var j = 0; j < currentExample.entities.length; j++) {
// add the option in the dropdown
$("#entitiesDropdown").append($('<option>', {
value: currentExample.entities[j].value,
text: currentExample.entities[j].entity
}));
}
}
}
});
</script>
Finally, I'd like to bring one thing to your notice. Conside the example below:
"entities": [
{
"start": 8,
"end": 15,
"value": "chinese",
"entity": "cuisine"
}
The entities list has one item in it. And that item has 4 sub-items in it. In your question, you haven't made it clear if you want to show all the sub-items in one dropdown option (e.g. start: 8, end: 15, value: chinese, entity: cuisine) or if you want a separate option for each sub-item.
The JS code that I've posted will create a dropdown option like this:
<option value="chinese">cuisine</option>.
If you want to display other items, you can just create another loop and keep adding the items to dropdown.

jQuery DataTables default column value

I am trying to include an invisible column in a jQuery DataTable that can be set to default true in place of a null or undefined encountered in the data source.
Say I am working with a 6-columns DataTable. The first 5 columns are visible. The 6th column is set to not be visible. And it must contain either a true or false, regardless of whether the data source object has the corresponding key. In the column definitions for the DataTable, I tried this but it did not work.
{ "defaultContent": true, "data": "existing", "visible": false }
According to the API, I think defaultContent works only when data is null. Maybe that's why it does not work. I have provided the HTML, JS data and JS code for initializing the DataTable. Notice that once the DataTable is loaded and rendered, I dynamically append a row and re-draw. The data for this row contains the 6th column property and that is gets set just fine.
HTML:
<div id="demo">
</div>
JavaScript (data initialization):
var dataSet = [
{'engine':'Webkit','browser':'Safari 1.2','platform':'OSX.3','version':'125.5','grade':'A'},
{'engine':'Other browsers','browser':'All others','platform':'-','version':'-','grade':'U'}
];
JavaScript (DataTable creation and initialization):
$('#demo').html('<table class="display" id="example"></table>');
$('#example').dataTable( {
"data": dataSet,
"columns": [
{ "sortable" : false, "data": null, "defaultContent": "<button>Select</button>", "title" : "Choose"
},
{ "title": "Engine", "data": "engine" },
{ "title": "Browser", "data": "browser" },
{ "title": "Platform", "data": "platform" },
{ "title": "Version", "data": "version", "class": "center" },
{ "title": "Grade", "data": "grade", "class": "center" },
{ "defaultContent": true, "data": "existing", "visible": false }
],
initComplete: function(oSettings, json) {
console.log('drawing complete');
if (oSettings.fnRecordsTotal() > 1) {
$('#example').DataTable().row.add({
'engine' : 'Presto',
'browser' : 'Opera 7.5',
'platform' : 'Win 95+ / OSX.2+',
'version' : '-',
'grade' : 'A',
'existing' : false
}).draw();
}
}
} );
Here is the JSFiddle for the above example. If you click on the Select button in the first two rows, it returns undefined. The Select button on the third row yields false. I want the first two rows' Select button to yield true.
How do I set a default value for the 6th (invisible) column if no key/value is provided in the data-set object?
It returns undefined because existing not exists in dataSet. dataSet is static, defaultContent does not populate its value to the original dataSet. What you really want is the value of the hidden column, which will contain either defaultContent or dynamically added existing values.
var row = $('#example').DataTable().row( $(this).parents('tr') ),
index = row.index(),
data = row.cell(index, 6).data();
alert(data);
will return true, true and false. Forked fiddle -> http://jsfiddle.net/vsx7uxnf/

jQuery loading data into jqGrid

I'm a first time user of jqGrid, so far I went trough official examples, I'm interested in loading data into grid either using json.
I'm currently looking at, Loading data(JSON Data):
http://trirand.com/blog/jqgrid/jqgrid.html
Here is a bit of javascript that creates grid :
jQuery("#list2").jqGrid(
{
url : '<c:url value="${webappRoot}/getdetails" />',
datatype : "json",
colNames : [ 'id', 'Location', 'Country Code', 'Type', 'Interval',
'Version', 'Last Active', 'Last Login', 'NOTE' ],
colModel : [
{ name : 'id', width : 10 },
{ name : 'location', width : 75 },
{ name : 'countryCode', width : 50 },
{ name : 'type', width : 40 },
{ name : 'interval', width : 30 },
{ name : 'version', width : 45 },
{ name : 'lastactive', width : 50, align : "right" },
{ name : 'lastlogin', width : 50, sortable : false },
{ name : 'note', width : 50, sortable : false}
],
rowNum : 10,
rowList : [ 10, 20, 30 ],
pager : '#pager2',
width: gridWidth,
sortname : 'id',
viewrecords : true,
sortorder : "desc",
caption : "JSON Example"
});
jQuery("#list2").jqGrid('navGrid', '#pager2',
{ edit : false, add : false, del : false});
${webappRoot}/getdetails transforms path to my project like http://localhost/myProject/getdetails, I'm using spring MVC(it might be irrelevant).
When I look in firebug this generates this http request :
GET http://localhost/newProject/getdetails?_search=false&nd=1304638787511&rows=10&page=1&sidx=id&sord=desc
200 OK
135ms
Here is the response :
{
"id": 1,
"location": "office_2782",
"countryCode": "UK",
"quarter": "500",
"version": "v3.05",
"lastactive": "yesterday",
"lastlogin": "today",
"note": "no note",
"type": "read-only"
}
When I navigate to JSON tab it all seems same as this, any idea what I'm doing wrong?
I'm trying to load only one record for start, and I can't get it working, any help is appriciated.
First of all you are not the first person who has problems understanding how the JSON data should be constructed, what the parameters sent from jqGrid to the server mean and so on. The official jqGrid documentation doesn't contain enough introduction, so the first steps of the jqGrid usage can be a little more difficult than one expect.
The problem which exists in your JSON response from the server is that it contains only one item of data instead of an array (or list) of items representing the grid rows. The data should be at least
[
{
"id": 1,
"location": "office_2782",
"countryCode": "UK",
"quarter": "500",
"version": "v3.05",
"lastactive": "yesterday",
"lastlogin": "today",
"note": "no note",
"type": "read-only"
}
]
or better as
{
"total": 1,
"page": 1,
"records": 1,
"rows": [
{
"id": 1,
"location": "office_2782",
"countryCode": "UK",
"quarter": 500,
"version": "v3.05",
"lastactive": "yesterday",
"lastlogin": "today",
"note": "no note",
"type": "read-only"
}
]
}
or even as
{
"total": 1,
"page": 1,
"records": 1,
"rows": [
{
"id": 1,
"row": [ "1", "office_2782", "UK", "500", "v3.05",
"yesterday", "today", "no note", "read-only" ]
}
]
}
or
{
"total": 1,
"page": 1,
"records": 1,
"rows": [
[ "1", "office_2782", "UK", "500", "v3.05", "yesterday", "today",
"no note", "read-only" ]
]
}
The reason of such strange at the first glance JSON data is that jqGrid is designed to support paging, sorting and filtering/searching of data implemented on the server. So the parameters rows=10&page=1&sidx=id&sord=desc from the url sent to the server mean that jqGrid asks the server to get the first page (page=1) of the data with the page having 10 rows per page (rows=10). The data should be previously sorted by id (sidx=id) in the descending order (sord=desc). If you has small number of rows (under some hundert for example) you can use client based sorting, paging and filtering if you add loadonce:true parameter of the jqGrid, but the server based implementation allows you to work with really large dataset having many hundred thousands rows of data with very good performace.
I recommend you to read my this answer where I tried to explain how the additional elements of the server response "total", "page" and "records" will be used. The values of the parameters can be encoded in JSON either as numbers or as strings (on your taste).
If the user clicks on the column header of the 'location' column for example jqGrid will send new request to the server having sidx=location&sord=asc in the url. So it is important to understand, that the server can be asked to provide the data for the grid not once per grid, but many times and the request will contain some parameters chosen by the user who works with the jqGrid.
Defining of jsonReader (and sometimes additional jsonmap parameters for every column) you describe the structure of the server response. Using the information jqGrid read the response and fill the grid.
The demo shows that with the corresponding jsonReader you can read even your original JSON data.
The last advice for you from me would be to consider at the beginning to use loadError event handle which helps to inform the user about the errors reported by the server. In the answer I have shown how it can be implemented in the case of ASP.NET MVC. I don't use spring MVC myself so I can't give you direct examples of how to better implement the error reporting in spring MVC, but the main idea is the same in any server technology: in case of errors the server should respond with the response having an error HTTP status code. Inside of your implementation of the loadError event handle you decode the response and display the information about the error.

Categories