yui datatable column dot notation displays nothing - yui

YUI DataTable doesn't display values for 'dot.notation' keys (though there seems to be a weird trick that works). What's the best way to remedy this? I want a 'correct' answer, not the current one I have where I flatten nested objects AND retain the nested objects (both must be present for this to currently work).
example data (3rd datum works because of weird duplication trick)
var table = new Y.DataTable({
columns: ['key', 'dot.notation'],
data: [{
// broken
key: 'value',
'dot.notation': 5
}, {
// broken
key: 'value',
dot: {
notation: 5
}
}, {
// displays
key: 'value',
'dot.notation': 5,
dot: {
notation: 5
}
}]
});
http://jsfiddle.net/dirkraft/ERk2d/

Using DataSchema is the correct way to handle this. I believe that the dotted key version used to work but then changes in version 3.5 stopped this working
YUI().use('datatable', 'datasource','datasource-jsonschema', function (Y) {
var ds = new Y.DataSource.Local({
source: [{
// broken
key: 'value',
'dot.notation': 5
}, {
// broken
key: 'value',
dot: {
notation: 5
}
}, {
// displays
key: 'value',
'dot.notation': 5,
dot: {
notation: 5
}
}]
});
ds.plug({fn: Y.Plugin.DataSourceJSONSchema, cfg: {
schema: {
resultFields: [
"key",
{
key:'foo',
locator:'dot.notation'
}
]
}
}});
var table = new Y.DataTable({
columns: ['key', 'foo'],
caption: 'Better Now'
});
table.plug(Y.Plugin.DataTableDataSource, {
datasource: ds
});
table.render('#lolol');
table.datasource.load();
});
http://jsfiddle.net/ERk2d/3/

Related

mongoose is converting objects to arrays whithout throwing

Let's says we have this model
const Model = mongoose.model('Model', new mongoose.Schema({
values: [{ key: String }]
}));
So we would have documents looking like that :
{
_id: someId,
values: [
{ key: 'v1' },
{ key: 'v2' },
]
}
Now if I perform this update =>
Model.findByIdAndUpdate(someId, { values: { key: 'v3' } })
This does not throw even if the values property is not an array. However it updates the document resulting in
{
_id: someId,
values: [
{ key: 'v3' },
]
}
I would like this kind of errors to throw instead of silently deleting every items in my values property :D My mongoose projects have default configuration (strict modes are enabled).
I know i could update with $set, which would not lead to that behavior. However if i have to use $set, i think disabling updates outside an operator would be a good option too.

tabulator autocomplete that updates another cell

I'm trying to use tabulator as follows:
Pull a set of key->values from a database that contains items similar to:
'JohnSmith' -> 'Name is john smith'
'AndrewSmith' -. Name is 'andrew smith'
column1
contains an 'autocompete' list that pulls from the keys above. Entering say 'smith' would allow me to select one of the two keys
column2
on selecting the correct option, the cell would be populated with the correct 'Name is...' value
I've tried a number of options, including:
using a 'cellEdited' callback on the autocomplete cell
adding a mutator that updates the second cell
adding an onrendered function to a formatter
I suspect I'm just missing something via javascript, but hoping someone can point me in the right direction.
You haven't specified which Tabulator version you are using but starting with version 5.0 cellEdited callback has changed to an event. Therefore, you can now use:
table.on("cellEdited", function() {
//do something
})
Here is a working example:
var people = [{
key: 'John Smith',
value: 'Name is john smith'
},
{
key: 'Andrew Smith',
value: 'Name is andrew smith'
}
]
var tableData = [{
name: "Oli",
nameIs: "Name is Oli Bob"
}]
var table = new Tabulator("#example-table", {
height: '100%',
layout: "fitColumns",
data: tableData,
columns: [{
title: "Name",
field: "name",
editor: 'autocomplete',
editorParams: {
showListOnEmpty: true,
freetext: true,
allowEmpty: true,
searchFunc: (term, values) => {
const matches = []
people.forEach(person => {
const name = person.key
const nameIs = person.value
if (name.toLowerCase().startsWith(term.toLowerCase())) {
matches.push(name)
}
})
return matches
},
values: false
}
},
{
title: "Name Is",
field: "nameIs"
}
]
})
table.on("cellEdited", (cell) => {
const name = cell.getValue()
const row = cell.getRow()
const nameIs = people.filter(p => p.key == name)[0].value
row.update({
"nameIs": nameIs
})
})
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<div id="example-table"></div>

How do I build a dynamic query in Mongoose?

I need to build a query using the following sample data:
{
brandFilters:[ { brand: 'Amana', checked: true },
{ brand: 'Whirlpool', checked: true } ],
priceFilters:[ { low: 100, high: 200, label: '$100 - $200', checked: true },
{ low: 200, high: 300, label: '$200 - $300', checked: true } ],
rating: null }
The first segment of the query builds the brands criteria as shown here;
let brands = [];
let productQuery = Product.find();
if(filters.brandFilters.length) {
for(let filter of filters.brandFilters) {
brands.push(filter.brand)
}
productQuery.where('brand').in(brands)
console.log('brands', brands)
}
The second segment is supposed build the price criteria as shown here:
if(filters.priceFilters.length) {
for(let filter of filters.priceFilters) {
productQuery.where('price').gte(+filter.low).lte(+filter.high);
}
}
The intention is to add a "where" clause for each price filter as shown in the data above. The problem is multiple where clauses for the price criteria are not being added. Each successive price filter in the for loop overwrites the last. So instead of having multiple "where" clauses for my price filters, the query only contains one for the last iteration of the for loop.
I am trying to get to a query that would look like this:
productQuery.where('brand').in(['Amana', 'Whirlpool'])
productQuery.where('price').gte(100).lte(200)
productQuery.where('price').gte(200).lte(300)
So the query you're trying to build should look like below:
{
"$and":[
{"$or":[
{"price":{"$gte":100,"$lte":200}},
{"price":{"$gte":200,"$lte":300}}
]
},
{"brand":{"$in":["Amana","Whirlpool"]}}
]
}
So it contains $or to include multiple price ranges. To do that you can build that query as plain JavaScript object and then pass it to find method, try:
let query = {
'$and': []
};
var priceQuery = [];
let brands = [];
if(filters.priceFilters.length) {
for(let filter of filters.priceFilters) {
priceQuery.push({ 'price': { $gte: +filter.low, $lte: +filter.high } });
}
query['$and'].push({ '$or': priceQuery });
};
if(filters.brandFilters.length) {
for(let filter of filters.brandFilters) {
brands.push(filter.brand)
}
query['$and'].push({ 'brand': { '$in': brands } });
}
and then
let products = await Product.find(query);

Adding a column to a dstore backed dgrid

I have a grid with five columns - username, email, enabled, locked and remove.
Username, email, enabled and locked are sourced from the server. Remove is a client-side element used to indicate that a row should be removed.
I would like to either inject the default value of remove in the store on the client side as the grid content is loading, or set it as the user interacts with the CheckBox widget.
How can I catch the code which is requesting the objects from the server and add another column?
Or, is there a better way to do this.
var TrackableRest = declare([Rest, SimpleQuery, Trackable]);
var store = new TrackableRest({target: '/api/users', useRangeHeaders: true, idProperty: 'username'});
aspect.after(store, "fetch", function (deferred) {
return deferred.then(function (response) {
response.remove = false;
return json(response);
})
});
var grid = new (declare([OnDemandGrid, Selection, Editor]))({
collection: store,
className: "dgrid-autoheight",
columns: {
username: {
label: core.username
},
email: {
label: core.email
},
enabled: {
label: core.enabled,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
locked: {
label: core.locked,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
remove: {
editor: CheckBox,
editorArgs: {"checked": false},
editOn: "click",
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
},
renderCell: libGrid.renderGridCheckbox
}
},
selectionMode: "none"
}, 'grid');
In addition, I don't want to send the remove column to the server.
My final implementation was to code the remove column like so:
remove: {
editor: CheckBox,
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
}
}
The code to perform the removes is as follows:
var removeBtn = new Button({
label: core.remove
}, 'user-remove-btn');
removeBtn.startup();
removeBtn.on("click", function (event) {
var markedForDeletion = query(".dgrid-row .remove-cb input:checked", "user-grid");
if( markedForDeletion.length > 0 ) {
lib.confirmAction(core.areyousure, function () {
markedForDeletion.forEach(function (node) {
var row = grid.row(node);
store.remove(row.data.username);
});
});
}
});
Thus the remove column became a client-side only control that was handled by the grid and the event handler.

Highmaps Group Countries and Hover, Follow link on Click

As the topic states, I would like to group countries to create my own "areas". It works nearly, but I don't know whats wrong.
Here is my map: http://jsfiddle.net/wiesson/oktajn6e
It is mostly derived from the examples, but it does not work. If I set "allAreas" to false, it is okay but I would like to display all other countries, too!
Any ideas?
$(function () {
// Instanciate the map
$('#container').highcharts('Map', {
chart: {
borderWidth: 0
},
title: {
text: 'Group Hover'
},
legend: {
enabled: true
},
plotOptions: {
map: {
allAreas: true,
joinBy: ['iso-a2', 'code'],
mapData: Highcharts.maps['custom/world']
},
series: {
states:{
normal: {
animation: false
}
},
point: {
events: {
mouseOver: function(){
var ser = this.series;
var data = ser.data;
$.each(data, function(){
this.setState("hover")
});
},
mouseOut: function(){
var ser = this.series;
var data = ser.data;
$.each(data, function(){
this.setState()
});
}
}
}
}
},
series: [{
name: 'Nordic Countries',
data: $.map(['IS', 'NO', 'SE', 'FI', 'DK'], function (code) {
return {
code: code
};
}),
}, {
name: 'Some of central Europe',
data: $.map(['DE', 'AT', 'GB', 'FR'], function (code) {
return {
code: code
};
}),
}]
});
});
This solution should fix your problem: http://jsfiddle.net/oktajn6e/5/
But let me explain what happens in your code:
With both series you create a full world map with all areas. So if you activate both series, the second series covers the complete first series.
It means, the blue areas get covered by series' two grey areas.
I managed to solve it this way:
series: [{
allAreas: true,
mapData: Highcharts.maps['custom/world'],
showInLegend: false,
}, {
allAreas: false,
name: 'Nordic Countries',
joinBy: ['iso-a2', 'code'],
mapData: Highcharts.maps['custom/world'],
data: $.map(['IS', 'NO', 'SE', 'FI', 'DK'], function (code) {
return {
code: code
};
}),
}, {
allAreas: false,
name: 'Some of central Europe',
joinBy: ['iso-a2', 'code'],
mapData: Highcharts.maps['custom/world'],
data: $.map(['DE', 'AT', 'GB', 'FR'], function (code) {
return {
code: code
};
}),
}]
By creating each series individually and setting "allAreas:false" we can simply render it on the first series, where we only show the full map.

Resources