I am looking to assign as an object a Fetch API promise from a local GeoJSON file.
Here is the code
fetch("data/sites.geojson")
.then(function(response) {
return response.json();
})
.then(function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites
}).addTo(map);
});
};
I tried the call back method, as advised here
Saving fetched JSON into variable
(EDIT) New code, but there is still a missing formal parameter
function getData("data/sites.geojson", cb) {
fetch("data/sites.geojson")
.then(function(response) {
return response.json();
})
.then(function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites,
onEachFeature: function (feature, layer) {
layer.on('mouseover', function() {
layer.openPopup(layer.bindPopup("<b>"+feature.properties.nombre+"</b>"))
});
layer.on('mouseout', function() {
layer.closePopup();
});
layer.on('click', function () {
layer.bindPopup("<b>Nombre: </b>"+feature.properties.nombre+"<br><b>Barrio: </b>"+feature.properties.barrio+"<br><b>Tipo: </b>"+feature.properties.tipo+"<br><b>Ubicacion: </b>"+feature.properties.ubicacion+"<br><b>Correo: </b>"+feature.properties.contacto);
});
}
}).addTo(map);
.then(function(result) {
cb(result);
});
});
};
getData("data/sites.geojson", function (data) {
return console.log({data});
});
Most probably just incorrect syntax of your callback function:
// Use either arrow function
getData("data/sites.geojson", (data) => {
return console.log({data});
});
// or standard function
getData("data/sites.geojson", function (data) {
return console.log({data});
});
I found the way to work this out by adding within the fetch function, what I originally wanted to do on the map.
This was to add a L.controlLayer using the geojson as overlay.
This is the code that made it work:
let sites = getData()
.then((function(data) {
L.geoJSON(data, {
pointToLayer: styles_sites,
onEachFeature: function LayerControl(feature, layer) {
var popupText = "<b>" + feature.properties.nombre + "<br>";
layer.bindPopup(popupText);
category = feature.properties.tipo;
// Initialize the category array if not already set.
if (typeof categories[category] === "undefined") {
categories[category] = L.layerGroup().addTo(map);
layersControl.addOverlay(categories[category], category);
}
categories[category].addLayer(layer);
layer.on('mouseover', function() {
layer.openPopup(layer.bindPopup("<b>"+feature.properties.nombre+"</b>"))
});
layer.on('mouseout', function() {
layer.closePopup();
});
layer.on('click', function () {
layer.bindPopup("<b>Nombre: </b>"+feature.properties.nombre+"<br><b>Barrio: </b>"+feature.properties.barrio+"<br><b>Tipo: </b>"+feature.properties.tipo+"<br><b>Ubicacion: </b>"+feature.properties.ubicacion+"<br><b>Correo: </b>"+feature.properties.contacto);
});
}
}).addTo(map);
}));
Actually it comes from one of your answer on another post ghybs.
I have chart when page load. I see normal charts with data:
Ids,Dates,Values
Бар,2018-12-21,224
Бар,2018-12-22,352
Бар,2018-12-23,61
Бар,2018-12-24,379
Бар,2018-12-25,78.2
Бар,2018-12-26,0
Бар,2018-12-27,0
Бар,2018-12-28,0
Бар,2018-12-29,0
Бар,2018-12-30,0
Бар,2018-12-31,0
Бар,2019-01-01,0
Бар,2019-01-02,0
Бар,2019-01-03,0
Бар,2019-01-04,0
Бар,2019-01-05,270
Бар,2019-01-06,0
Бар,2019-01-07,0
Бар,2019-01-08,0
Бар,2019-01-09,0
Бар,2019-01-10,0
Бар,2019-01-11,0
Бар,2019-01-12,0
Бар,2019-01-13,0
Бар,2019-01-14,0
Бар,2019-01-15,0
Бар,2019-01-16,0
Бар,2019-01-17,0
Бар,2019-01-18,0
Бар,2019-01-19,0
Бар,2019-01-20,0
But if I send ajax chart is empty, but have a data, and if I load page with this data all are correct, only ajax destroy chart:
Ids,Dates,Values
Бар,2018-11-21,178
Бар,2018-11-22,256
Бар,2018-11-23,226
Бар,2018-11-24,570
Бар,2018-11-25,266
Бар,2018-11-26,398
Бар,2018-11-27,0
Бар,2018-11-28,15
Бар,2018-11-29,80
Бар,2018-11-30,118
Бар,2018-12-01,41
Бар,2018-12-02,365
Бар,2018-12-03,180
Бар,2018-12-04,187
Бар,2018-12-05,38
Бар,2018-12-06,82
Бар,2018-12-07,390
Бар,2018-12-08,177
Бар,2018-12-09,359
Бар,2018-12-10,236
Бар,2018-12-11,7
Бар,2018-12-12,34
Бар,2018-12-13,478
Бар,2018-12-14,173
Бар,2018-12-15,290
Бар,2018-12-16,453
Бар,2018-12-17,52
Бар,2018-12-18,334
Бар,2018-12-19,0
Бар,2018-12-20,122
My js code:
$(document).ready(function() {
$('.js_dashboard').focus(function() {
allPostDataForOne($(this));
});
$('.js_dashboard').parents('.container').find('.js_dc_components').find('.js_data_graph').each(function(index, value) {
renderBar(dc.barChart("#js_graphic_" + index), d3.csv.parse(d3.select(value).text()));
});
});
function allPostDataForOne(elem) {
var table = elem.parents('.container').find('.js_main_dasboard');
$.ajax({
url: '/../index/total' + $('.js_suffix').val(),
type: 'POST',
data: 'date=' + elem.val(),
success: function(data) {
table.html($($(data)).find('.js_main_dasboard').html());
table.parents('.container').find('.js_dc_components').find('.js_data_graph').each(function(index, value) {
$($(data)).find('.container').find('.js_dc_components').find('.js_data_graph').each(function(index_res, value_res) {
if (index === index_res) {
value = value_res;
}
});
renderBar(dc.barChart("#js_graphic_" + index), d3.csv.parse(d3.select(value).text()));
});
},
})
}
function renderBar(chart, experiments) {
var start_date = new Date($('.js_start_date').val());
var end_date = new Date($('.js_end_date').val());
var format = d3.time.format("%Y-%m-%d");
experiments.forEach(function(x) {
x.Values = +x.Values;
});
var ndx = crossfilter(experiments);
var runDimension = ndx.dimension(function(d) {
return new Date(d.Dates);
});
var runGroup = runDimension.group().reduceSum(function(d) {
return d.Values;
});
chart
.width(750)
.height(300)
.x(d3.time.scale().domain([start_date, end_date]))
.brushOn(false)
.yAxisLabel(experiments[0]['Ids'])
.xAxisLabel("Date")
.dimension(runDimension)
.mouseZoomable(true)
.group(runGroup)
.title(function(d) {
return d.key.getFullYear() + '-' + parseInt(d.key.getMonth() + 1) + '-' + d.key.getDate() + ': ' + d.value;
})
chart.xUnits(function() {
return experiments.length;
});
chart.render();
}
I can't understand how to correctly update charts. I saw many quations but i cant understand how they update transform in my code.
I've spent a lot of time trying to figure out how to go inside anonymous functions in jasmine.
Sample of method:
numerateColumns: function (rows) {
rows.each(function () {
var $row = $(this);
$row.children().each(function (index) {
var $cell = $(this);
$cell.addClass('column-' + (index + 1));
});
});
}
Try to test with:
it("[TEST] Should call each method.", function () {
// setup
var rows = {
each: function () {
return {
children: function () {
return {
replaceWith: function () {
return null;
}
};
}
};
}
};
spyOn(rows, 'each').and.callThrough();
// method under test
module.numerateColumns(rows);
// expectations
expect(rows.each).toHaveBeenCalled();
});
But coverage test shows me that code of method is read only in first line (rows.each).
How to force it to read all the code inside (function() {}) ?
Why you want to test jQuery? It works perfectly, if not - some tests probably it will catch before new version publication.
function numerateColumns($rows) {
$rows.each(function() {
var $row = $(this);
$row.children().each(function(index) {
var $cell = $(this);
$cell.addClass('column-' + (index + 1));
});
});
}
describe('Iterate over columns`', function() {
var mockRows
beforeEach(function() {
mockRows = $('<div><div></div></div>')
})
it("calls .each() on passed jQuery collection", function() {
spyOn($, 'each').and.callThrough();
expect($.each).not.toHaveBeenCalled();
numerateColumns(mockRows);
expect($.each).toHaveBeenCalled();
});
it("adds CSS class to each child", function() {
var column = $(mockRows[0]).find('div');
expect(column.hasClass('column-1')).toBeFalsy()
numerateColumns(mockRows);
expect(column.hasClass('column-1')).toBeTruthy()
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
What you can test - it is extracted business logic that is independent to jQuery itself.
currently my pie chart in d3 shows the sum of numbers,i want percentage instead .
for eg: currently i have a pie chart for how many people submitted application who visited our site. our current pie chart shows like this : people submitted 17,000 and people didn't submitted-10,000
but i need this in percentage also. how can i get that.
please find the pie code below and let me know what changes do i need to make this work. I am new to JavaScript and D3.
ko.bindingHandlers.pieChart = {
init: function (element, valueAccessor) {
var _options = valueAccessor();
var _data = _options.transformer(_options.data);
$(element).css("marginLeft", "auto");
$(element).css("marginRight", "auto");
if (typeof _options.maxWidth != "undefined") {
var _max = _options.maxWidth * 1;
$(element).width(Math.min($(element).parent().width(), _max));
}
if ($(element).find("svg").length > 0 && _data.length == 0) {
$(element).find("svg").empty();
}
if (_data.length > 0 && isNaN(_data[0].value)) {
_data = [];
$(element).find("svg").empty();
}
if ($(element).is(":visible")) {
nv.addGraph(function () {
var _chart = nv.models.growingPieChart()
.x(function (d) {
return d.label
})
.y(function (d) {
return d.value
})
.height($(element).width())
.showLabels(true).showLegend(false)
.tooltipContent(function (key, y, e, graph) {
return '<h3>' + hueUtils.htmlEncode(key) + '</h3><p>' + y + '</p>'
});
var _d3 = ($(element).find("svg").length > 0) ? d3.select($(element).find("svg")[0]) : d3.select($(element)[0]).append("svg");
_d3.datum(_data)
.transition().duration(150)
.each("end", _options.onComplete != null ? _options.onComplete : void(0))
.call(_chart);
if (_options.fqs) {
$.each(_options.fqs(), function (cnt, item) {
if (item.id() == _options.data.widget_id && item.field() == _options.field()) {
_chart.selectSlices($.map(item.filter(), function (it) {
return it.value();
}));
}
});
}
$(element).data("chart", _chart);
var _resizeTimeout = -1;
nv.utils.windowResize(function () {
window.clearTimeout(_resizeTimeout);
_resizeTimeout = window.setTimeout(function () {
_chart.update();
}, 200);
});
$(element).on("forceUpdate", function () {
_chart.update();
});
$(element).height($(element).width());
var _parentSelector = typeof _options.parentSelector != "undefined" ? _options.parentSelector : ".card-widget";
$(element).parents(_parentSelector).on("resize", function () {
if (typeof _options.maxWidth != "undefined") {
var _max = _options.maxWidth * 1;
$(element).width(Math.min($(element).parent().width(), _max));
}
$(element).height($(element).width());
_chart.update();
});
return _chart;
}, function () {
var _d3 = ($(element).find("svg").length > 0) ? d3.select($(element).find("svg")[0]) : d3.select($(element)[0]).append("svg");
_d3.selectAll(".nv-slice").on("click",
function (d, i) {
if (typeof _options.onClick != "undefined") {
chartsUpdatingState();
_options.onClick(d);
}
});
});
}
},
update: function (element, valueAccessor) {
var _options = valueAccessor();
var _data = _options.transformer(_options.data);
var _chart = $(element).data("chart");
if (_chart) {
var _d3 = d3.select($(element).find("svg")[0]);
_d3.datum(_data)
.transition().duration(150)
.each("end", _options.onComplete != null ? _options.onComplete : void(0))
.call(_chart);
if (_options.fqs) {
$.each(_options.fqs(), function (cnt, item) {
if (item.id() == _options.data.widget_id && item.field() == _options.field()) {
_chart.selectSlices($.map(item.filter(), function (it) {
return it.value();
}));
}
});
}
chartsNormalState();
}
else if (_data.length > 0) {
ko.bindingHandlers.pieChart.init(element, valueAccessor);
}
}
};
A fiddle would be useful to test this against (hint hint), but I'm pretty sure you want to change this line:
.y(function (d) {
return d.value
})
to this
.y(function (d) {
return d.value/total
})
You may have to define total. Like I said, without a jsfiddle or at least some indication of the format of your data, it's hard to determine if this is actually what's wrong or how to fix it.
Note: a pie chart of relative percentages will look exactly the same as a pie chart of the original numbers. You might be able to change the label and only the label, as follows:
return '<h3>' + hueUtils.htmlEncode(key) + '</h3><p>' + y + '</p>'
to this
return '<h3>' + hueUtils.htmlEncode(key) + '</h3><p>' + (y/total) + '</p>'
Hopefully both of those should work. You will have to define total, if it isn't already defined elsewhere. If not:
var total = 0;
_data.forEach(function(d){total += d.value});
Good luck!
It would be even more helpful to include information such as the library you are using and a fully reproducible example using a gist, codepen, jsfiddle, etc. I am guessing you are using hue and more specifically growingPieChart. If my guesses are correct, then you can modify your tooltipContent function similar to #Vyross answer that posted while I was typing this.
.tooltipContent(function (key, y, e, graph) {
return '<h3>' + hueUtils.htmlEncode(key) + '</h3><p>' +
(+y) / d3.sum(
d3.select(graph.container).select('g').datum(),
function(d){return d.value}
) +
'</p>'
});
How do I do this asynchronously?
var getData, myFunc;
getData = function() {
var data = "";
$.get("http://somewhere.com/data.xml", function(d) {
data = $("#selector", d).html();
});
return data; // does not work, because async callback not yet fired
};
myFunc = function() {
var data = getData();
// do something with data here
};
I am happy to completely re-factor to achieve what I want. I am just don't know what design pattern achieves this.
Well, you can't. You can return a promise though:
var getData, myFunc;
getData = function () {
var d = $.Deferred();
$.get("http://somewhere.com/data.xml", function (data) {
d.resolve($("#selector", data).html())
});
return d.promise();
};
getData().then(function (data) {
alert(data);
});
demo http://jsfiddle.net/W75Kt/2/