Dynamically add attributes as object properties - javascript

I'm working on bootstrap-multiselect, I'm trying to add data attributes in the dataprovider method.
Current
var options = [
{label: 'Option 1', title: 'Option 1', value: '1', selected: true},
{label: 'Option 2', title: 'Option 2', value: '2'}];
In the code it maps these an <option> tag like so:
$tag = $('<option/>').attr({
value: option.value,
label: option.label || option.value,
title: option.title,
selected: !!option.selected,
disabled: !!option.disabled
});
Desired
var options =[
{
"label": "Item 1",
"value": 1,
"selected": false,
"attributes": [
{
"some-attribute": 10001
},
{
"another-attribute": "false"
}
]
}
]
So it will render on the HTML element as data-some-attribute="10001" data-another-attribute="false".
I started out adding this to the code (which I know won't work):
$tag = $('<option/>').attr({
value: option.value,
label: option.label || option.value,
title: option.title,
selected: !!option.selected,
disabled: !!option.disabled,
forEach(option.attributes, function(attribute){
})
});
The problem of course is you can't add a loop as an objects properties.
Once this is working I can add a pull request to the repository. I did ask a question on the repo but decided to try and tackle it myself Issue #592
Any ideas?

I would suggest changing attributes from an array to an object, since attribute names should be unique. It also simplifies how you would get the data attributes on the element.
var attributes = {
value: option.value,
label: option.label || option.value,
title: option.title,
selected: !!option.selected,
disabled: !!option.disabled
};
for (key in option.attributes) {
attributes['data-' + key] = option.attributes[key];
}
$tag = $('<option/>').attr(attributes);
If you wanted to keep it as an array, you can do the following:
var attributes = {
value: option.value,
label: option.label || option.value,
title: option.title,
selected: !!option.selected,
disabled: !!option.disabled
};
for (var i = 0; i < option.attributes.length; i++) {
var key = Object.keys(option.attributes[i])[0],
val = option.attributes[i][key];
attributes['data-' + key] = val;
}
$tag = $('<option/>').attr(attributes);
Doing this, however, provides no benefit and introduces complexity. If each object can have multiple keys, the code will need to change further.

You need to create the element first then add the attributes to it.
So your code should be like this:
var options = [{
"label": "Item 1",
"value": 1,
"selected": false,
"attributes": [{
"some-attribute": 10001
}, {
"another-attribute": "false"
}]
}]
console.log(options.length);
$.each(options, function(option) {
var $tag = $('<option/>').attr({
value: options[option].value,
label: options[option].label || options[option].value,
title: options[option].title,
selected: options[option].selected,
disabled: options[option].disabled
});
console.dir(option);
$.each(options[option].attributes, function(att) {
$tag.attr("data" + Object.keys(att)[0], att[0])
});
$("#mySelect").append($tag);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="mySelect">
</select>

Related

In tabulator how to use selected data for bottom row calcs

I have a fairly simple table, and am currently using a bottom calculator formatter:
export let myTable = new Tabulator("#my-table", {
columns:[
{title:"ID", field:"id", headerSort:false, visible:false, responsive:2},
{formatter:"rowSelection", titleFormatter:"rowSelection", align:"center", bottomCalc:"sum", hozAlign:"center", headerSort:false, cellClick:function(e, cell){
cell.getRow().toggleSelect();
}},
{title:"Name", field:"address", width:300, bottomCalc:"count"},
{title:"My Data", field:"mydata", bottomCalc:avNoOutsiders},
],
});
export let avNoOutsiders = function(values, data, calcParams){
// filter outliers
let myArray = filterOutliers(values);
// filter any null or falsy values
let av = average(myArray);
return av
}
The code isn't super important, but what I'd like to be able to do is allow the user to de-select a row to exclude the value from this calculation.
The problem is, I don't understand how to access the isSelected() function here, I think it's just the row() I can access it. I can access the values (all the column values) but there's no selection data there, I can access the data - the whole table, but there's no way of determining which row it is, or whether it is selected or not.
My current direction of thinking is either
using bottomCalcParams. I don't understand how I would do this. This function returns a getRow() is not a function error:
function cellIsSelected(cell){
selected = cell.getRow().isSelected()
return {isSelected:selected};
}
or
Writing individual functions for each bottom calc. This doesn't work as I can't call the table inside the table calcs - var selectedRows = table.getSelectedRows() causes a circular error if I try to put that into a column calc function. I can reference the table inside the table.
Any ideas how I can access the row selection data to make a column calculation?
There might be an easier way, but one way to achive that would be to create a hidden placeholder column which can be updated to true/false upon row selection/deselection. Then you can use the values of this hidden column in your bottomCalc function to exclude rows that are not selected. Here is an example where selecting a row will re-trigger bottom calculation and average the age of all selections:
const dataSet1 = [
{ id: 1, name: 'Billy Bob', age: '21', gender: 'male' },
{ id: 2, name: 'Mary May', age: '5', gender: 'female' },
{ id: 3, name: 'Christine Lobowski', age: '42', gender: 'female' },
{ id: 4, name: 'Brendon Philips', age: '80', gender: 'male' },
]
const calcAvg = (values, data, calcParams) => {
let selected = data.filter((row) => row.isSelected)
values = selected.map((i) => i.age)
let avg = values.reduce((a, b) => Number(a) + Number(b), 0) / values.length
return avg ? avg : ''
}
const table = new Tabulator('#table', {
data: dataSet1,
columns: [
{
formatter: 'rowSelection',
titleFormatter: 'rowSelection',
cellClick: (e, cell) => {
cell.getRow().toggleSelect()
}
},
{ title: 'Name', field: 'name' },
{ title: 'Age', field: 'age', bottomCalc: calcAvg },
{ title: 'Gender', field: 'gender' },
{ title: '', field: 'isSelected', visible: false } // Selection placeholder column
]
})
const selection = (row) => {
row.update({ isSelected: row.isSelected() })
table.recalc()
}
table.on('rowSelected', selection)
table.on('rowDeselected', selection)
<html>
<head>
<link href="https://unpkg.com/tabulator-tables#5.2.2/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables#5.2.2/dist/js/tabulator.min.js"></script>
</head>
<body>
<div id="table"></div>
</body>
<html>
#Tim's answer got me 95% of the way there.
Note that some of his responses don't work on Tabulator v4.9:
const selection = (row) => {
row.update({ isSelected: row.isSelected() })
table.recalc()
}
table.on('rowSelected', selection)
table.on('rowDeselected', selection)
I thought that figuring out a way round this might take longer than updating, so I plunge and updated to 5.2.2, which was less painful than expected. I had multiple columns to calculate, so rather than running a different function for each one, I passed the field name into the function via the bottomCalcParams, thus:
const calcAvg = (values, data, calcParams) => {
let selected = data.filter((row) => row.isSelected)
// Add your calcParams here:
values = selected.map((i) => i[calcParams.field])
let avg = values.reduce((a, b) => Number(a) + Number(b), 0) / values.length
return avg ? avg : ''
}
const table = new Tabulator('#table', {
data: dataSet1,
columns: [
{
formatter: 'rowSelection',
titleFormatter: 'rowSelection',
cellClick: (e, cell) => {
cell.getRow().toggleSelect()
}
},
{ title: 'Name', field: 'name' },
{ title: 'Age', field: 'age', bottomCalc: calcAvg, bottomCalcParams:{field:"age"} },
{ title: 'Remaining Teeth', field: 'teeth', bottomCalc: calcAvg, bottomCalcParams:{field:"teeth"} },
{ title: 'Personality Quirks', field: 'quirks', bottomCalc: calcAvg, bottomCalcParams:{field:"quirks"} },
{ title: 'Gender', field: 'gender' },
{ title: '', field: 'isSelected', visible: false } // Selection placeholder column
]
})

Kendo UI Grid - Custom command button disabled depending on boolean property

How can I set the class to disabled for a custom command on a Kendo grid depending on the boolean value of a property?
I want to use this approach to make the button disabled:
https://docs.telerik.com/kendo-ui/knowledge-base/disable-the-grid-command-buttons
Javascript:
{ command: { name: "custom", text: "Exclude", click: excludeCategorization }, title: " ", width: "60px" }
I want to add a condition like this using the property IsEnabled but if possible using the k-state-disabled class
#= IsEnabled ? disabled="disabled" : "" #
I don't believe you can assign classes conditionally through a template, however you can use the dataBound event to crawl through the rows and manipulate the classes. I would start with all of them disabled and then enable the ones that need to be active, but you can build your own logic. Here's an example:
<div id="grid"></div>
<script>
var grid;
$("#grid").kendoGrid({
dataBound:function(e){
var grid = $("#grid").data("kendoGrid");
var items = e.sender.items();
items.each(function (index) {
var dataItem = grid.dataItem(this);
$("tr[data-uid='" + dataItem.uid + "']").find(".excludeCategorization").each(function( index ) {
if(dataItem.isEnabled)
{
$(this).removeClass('k-state-disabled')
}
});
})
},
columns: [
{ field: "name" },
{ field: "enabled" },
{ command: [{ className: "k-state-disabled excludeCategorization", name: "destroy", text: "Remove" },{ className: "k-state-disabled", name: "edit", text: "Edit" }] }
],
editable: true,
dataSource: [ { name: "Jane Doe", isEnabled: false },{ name: "John Smith", isEnabled: true } ]
});
</script>
Here's a link to a Dojo: https://dojo.telerik.com/ubuneWOB

Adding an item to the top of a Dojo Select dijit, but can't select the second item

I have two Select dijits that are based off the same data store. The first dijit is the required response and the second dijit is an optional response. For the second dijit, I want to add the additional item "None" to the top of the list. However, when I do that, I cannot select the second item in the list. In this JSBin, if you select "General lakebed mapping" in the second dijit, the returned value is the added item "None".
require(["dijit/form/Select",
"dojo/data/ObjectStore",
"dojo/store/Memory",
"dojo/domReady!"
], function (Select, ObjectStore, Memory) {
var data = [
{ id: 0, label: 'General lakebed mapping' },
{ id: 1, label: 'Bathymetry/Digital Elevation Model' },
{ id: 2, label: 'Ferrous object detections/magnetic anomalies' },
{ id: 3, label: 'Ground-truth data' },
{ id: 4, label: 'Lakebed color' },
{ id: 5, label: 'Lakebed surface type, hardness/smoothness/slope' },
{ id: 6, label: 'Sub-bottom geology' }
];
var store = new Memory({
data: data
});
var os = new ObjectStore({ objectStore: store });
var s = new Select({
store: os,
sortByLabel: false
}, "target");
s.startup();
data.unshift({ id: -1, label: 'None' })
store.setData(data);
var s1 = new Select({
store: os,
sortByLabel: false
}, "target1");
s1.startup();
s1.on("change", function () {
console.log("my value: ", this.get("value"))
});
})
Do not use the value 0 as an id. It is a falsey value in JavaScript and I suspect that the Select dijit source treats it somewhere as false and fails. Just use another value in its place.

For Loop Iteration Inside of Javascript Variable (Django)

I am trying to implement a dropdown menu with selectors inside of django. I have made good progress in finding a piece of software that looks great!
http://nelrohd.github.io/bootstrap-dropdown-checkbox/
However, i am getting stuck on how to make it more dynamic.
The end goal is to have a filter option that consists of a list of applications that i can select and unselect in order to change what is outputted to the screen.
The way the software works is that it takes in a single variable through a jQuery selector:
var myData = [{id: 1, label: "Test" }];
$(".myDropdownCheckbox").dropdownCheckbox({
data: myData,
title: "Dropdown Checkbox"
});
Right now i can only use hardcoded properties for the vars
var data =[
{ id: "1", label: "Option 1", isChecked: true },
{ id: "2", label: "Option 2", isChecked: true },
{ id: "3", label: "Option 3", isChecked: true},
{ id: "4", label: "Option 4", isChecked: true },
{ id: "5", label: "Option 5", isChecked: true },
{ id: "6", label: "Option 6", isChecked: true},
{ id: "7", label: "Option 7", isChecked: true },
{ id: "8", label: "Option 8", isChecked: true },
{ id: "9", label: "Option 9", isChecked: true },
{ id: "10", label: "Option 10", isChecked: true },
];
I would like to find a way to make these properties iterable and able to pull a list from the django database: (something like:)
var data=[
for( var i = 0; i<=10, i++)
{id: (i), label: (list(i)), isChecked: true},
]
I have tried to use django template tags, but cant seem to get that working either. My question is: is it possible to iterate over a for loop to create properties for a variable?
Have you tried:
var data = [];
for (var i = 0; i <= 10; i++) {
data[data.length] = { id: (i + 1), label: 'Option ' + (i + 1), isChecked: true };
}
If you generate the list in Django, you can do like:
I will assume you have a list of arbitrary objects you want to serialize in Javascript. When you are ready to pass it to the response context, convert it with stuff like this: mark_safe(json.dumps([{'id': element.id, 'label': element.label, 'isChecked': element.is_checked} for element in mylist])). This is just an example since I don't know which objects you want to render.
Assuming such safe and dumped json is sent to the foo context variable:
var data = {{ foo }};
if you want to generate the options in pure javascript, there's nothing djangoish on it. A simple js for loop like #AlexandreThebaldi answer will work.
I would double check to make sure you had the Django tags set up correctly. It should look like this by passing in the set of options as your context:
{% for opt in options %}
{id: {{ forloop.counter }}, label: {{ opt }}, isChecked: true}
{% endfor %}

How to dynamically add and remove GROUPS to KendoUI Scheduler

I am using KendoUI Scheduler control and here is initialization code
$("#Scheduler").kendoScheduler({
dataSource: [],
selectable: true,
height: 500,
editable: false,
eventTemplate: $("#event-template").html(),
views: [
"day",
{ type: "week", selected: true },
"month",
"agenda"
],
resources: [
{
field: "resourceviewid",
name: "ResourceViews",
dataColorField: "key",
dataSource: [
{ text: "Appointments", value: 1, key: "orange" },
{ text: "Delivery Specialist", value: 2, key: "blue" },
{ text: "Orientation Specialist", value: 3, key: "green" }
]
}
],
group: {
resources: ["ResourceViews"],
orientation: "horizontal"
}
});
Here "Appointments" group is default, it will be available always
I have check box in my screen
<div id="divResourceView">
<label><input checked type="checkbox" value="1" />Delivery Specialist</label>
<label><input checked type="checkbox" value="2" />Orientation Specialist</label>
</div>
On change event I wrote below code to get selected values from checkbox and updating GROUP datasource of KendoUI scheduler as below
$("#divResourceView :checkbox").change(function (e) {
var checked = $.map($("#divResourceView :checked"), function (checkbox) {
return parseInt($(checkbox).val());
});
});
var scheduler = $("#Scheduler").data("kendoScheduler");
var arrayOfStrings = checked.toString().split(",");
for (var i = 0; i < arrayOfStrings.length; i++)
{
if(arrayOfStrings[i] == 1)
{
var data = [{ text: "Delivery Specialist", value: 2, color: "blue" }];
scheduler.resources[1].dataSource.data(data);
}
if (arrayOfStrings[i] == 2) {
var data = [{ text: "Orientation Specialist", value: 3, color: "green" }];
scheduler.resources[2].dataSource.data(data);
}
}
scheduler.refresh();
But it removes all groups and add only one. I want to see both groups when arrayOfStrings has values "1,2",
I can see all groups during initialization But it disappears when i check the check box.
Images for reference
During Initialization
After
As you can see clearly, Delivery Specialist is missing in scheduler control
Found some link: http://www.telerik.com/forums/add-filter-to-resource-datasource
But not sure what they talking about? seems like refresh issue.
I have done this I believe you're right and your issue is to do with refreshing, I use the following to refresh it.
var scheduler = $("#scheduler").data("kendoScheduler");
scheduler.view(scheduler.view().name);
hope this helps through it is stupidly late (I'm currently looking up how to do this lazerly

Categories