Initiate D3.js data map with new data - json

I'm going with the basic example in here except I need to be able to update the map based on new data (a json file.) I couldn't find a way to load the data directly inside Datamap object, so I'm loading it with D3.json and using the command to update the map. For some reason the popupTemplate function is receiving null data object and I don't know how to fix it.
What's the best way to do this?
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://datamaps.github.io/scripts/datamaps.world.min.js"></script>
<script src="http://datamaps.github.io/scripts/topojson.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>
<script>
var data;
var map = new Datamap({
element: document.getElementById('container'),
fills: {
HIGH: '#afafaf',
LOW: '#123456',
MEDIUM: 'blue',
UNKNOWN: '#FFFFFF',
defaultFill: 'green'
},
geographyConfig: {
popupTemplate: function(geo, data) {
console.log(data)
return ['<div class="hoverinfo"><strong>',
'Number of things in ' + geo.properties.name,
': ' + data[geo.id].numberOfThings,
'</strong></div>'].join('');
}
}
});
map.legend();
d3.json("path/to/data.json", function(error, json) {
if (error) return console.warn(error);
data = json;
map.updateChoropleth(data);
});
</script>
This is my json file:
{
"IRL": {
"fillKey": "LOW",
"numberOfThings": "2002"
},
"USA": {
"fillKey": "MEDIUM",
"numberOfThings": "10381"
}
}
To make it easier to debug, I put on jsfiddle

I did not have problems loading the data directly inside Datamap. In any case, I also simulated a data update...the color gets updated but not the value (numberOfThings). In the example in the tutorial, it is not clear this can be done although it would make sense that one should be able to update values.
I am leaving you with the FIDDLE showing the results of my experiment.
A couple of notes:
I believe your popup was not showing because its return string needs
to be data.numberOfThings.
If I am not mistaken when I played with this before, if you don't have data for a country, then the popup is not updated and the same value as for the last country with data is displayed.

#Kiarash, there are two problems here:
You are using the Datamaps from the datamaps.github.io site, which I don't recommend since I can't guarantee reliability (uptime) or backwards compatibility. You are best off downloading it from the Github project page
data won't have a property with the country name, it should be accessed like data.numberOfThings.
Here is a working version of the code you supplied

Related

How can I display dynamic HTML having Vue variables inside?

I am trying to make the page content dynamic. I am using ck-editor in which i added html content and used the same vue variables inside it which i declared in the vue file where i want to show ck-editor data. I found a similar post vuejs - “editing” html inside variable
which works fine if i write the html inside a variable. But in my case, i am saving data in database. It is saving properly with html tags, without converting the tags. When i get data using axios it returns it in form of string. And i used vue variable to display that html.
Here is my code for better understanding:
<div v-html="htmlText"></div>
new Vue({
el: '#app',
created() {
this.getSalesContent();
},
data: {
salesContent: '',
pageName: 'Sales',
salesNumber: '987-586-4511'
},
computed: {
htmlText() {
return `${this.salesContent}`;
//return this.salesContent;
}
},
methods: {
getSalesContent(){
axios.get('api/Sales').then(({ data }) => { // getting data from DB
this.salesContent = data.sales; //data.sales have this.pageName and this.salesNumber variables
});
}
}
});
Here is the example of data saved in db:
<p style="font-weight:bold"><span style="color:red">{{pageName}}</span>,</p>
<p style="font-weight:bold"><span style="color:red">${this.pageName} ${this.pageName}</span></p>
<p style="font-weight:bold">Contact Sales at ${this.salesNumber} {{salesNumber}}</span></p>
I used variables in all possible ways. But on the page they are printing in it the same way i saved it. Here is the output:
screenshot
Can anyone help me make it working.
Thanks in Advance.
According to the docs this does not seem possible:
https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML
Particularly:
The contents of the span will be replaced with the value of the
rawHtml property, interpreted as plain HTML - data bindings are
ignored.
You could as suggested in that answer just use a computed based on what you get from the server.
IMHO since the salesContent is fetched from db, it's a plain String. Thus nor vuejs or vanilla javascript will replace the inline variables with their values. (It may be possible by using eval, but it's totally out of question...) You should manually do that with String replace function. Like the following:
<p style="font-weight:bold"><span style="color:red">{{pageName}}</span>,</p>
<p style="font-weight:bold">Contact Sales at {{salesNumber}}</span></p>
methods: {
getSalesContent(){
axios.get('api/Sales').then(({ data }) => { // getting data from DB
let salesContent = data.sales; //data.sales have this.pageName and this.salesNumber variables
salesContent = salesContent.replace(/{{pageName}}/g, this.pageName)
salesContent = salesContent.replace(/{{salesNumber}}/g, this.salesNumber)
this.salesContent = salesContent
});
}
}

ReactJS fetch JSON API Data -- The correct way?

I have been fighting with ES6 trying to come up with, what should be, a pretty straightforward operation. I want to call JSON API data for Bitcoin from one of the three following websites:
https://cryptowat.ch
https://coinmarketcap.com/
https://www.cryptocompare.com/
All three sites API endpoints go straight to the price I want and I think this may be the problem. There is no array of data, just the specific price. In my example using #3 above, the only object is "USD". That being said, I think I'm overthinking the process as getting into APIs with much more data and arrays of data -- I have accomplished using ReactJS.
Trying to reach a single endpoint that shows up as the "State" in the React DOM Inspector as "USD" and is pulling in the correct price, I cannot get the price to render on the page even though ReactJS is seeing it and capturing it.
My code:
var BitcoinApp = React.createClass({
getInitialState: function() {
return {
"USD": []
}
},
componentDidMount: function() {
var th = this;
this.serverRequest =
axios.get(this.props.source)
.then(function(result) {
th.setState({
USD: result.data.USD
});
})
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<span>
{this.state.USD.map(function(Data) {
return (
<div key={Data.USD} className="testbtc">
<p>{Data.USD}</p>
</div>
);
})}
</span>
)
}
});
ReactDOM.render(<BitcoinApp source="https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD&e=Coinbase" />, document.querySelector("#btcPrice"));
I will mention that I have done a lot of research into this and have found a lot of answers -- all different! Everyone knows the ReactJS docs are severely outdated so finding the right path with ReactJS is difficult to say the least. Also, I'm using "axios" to "GET" the API data as I've read that "fetch" isn't globally supported yet? Is this still the case in 2017?
Using the above method, I can see this in the Inspector:
But when I go over to the "Console" portion of the inspector, I'm told that "this.state.USD.map is not a function".
I feel like I'm right on the cusp of solving this task, but I think I'm getting something wrong with the mapping of the promise.
the problem is that:
th.setState({
USD: result.data.USD
});
is seting not iterable object. I mean that this.state.USD.map is not a function means that USD is not an array (and you can see this in console).
Try this to see what happens:
th.setState({
USD: [result.data.USD]
});
However tho, you wrote:
There is no array of data, just the specific price.
then I think the best solution is to change just the render method and initial state:
render: function() {
return (
<span>
<div className="testbtc">
<p>{this.state.USD}</p>
</div>
</span>
)
}
getInitialState: function() {
return {
"USD": "",
}
},

vue.js json object array as data

aye folks!
I'm currently learning to do stuff with vue.js. unfortunately i'm stuck atm. what i want to do is sending a request to my sample API which responds with a simple json formatted object.
I want to have this object as data in my component – but it doesn't seem to do that for whatever reason.
Ofc i tried to find a solution on stackoverflow but maybe i'm just blind or this is just like the code other people wrote. i even found this example on the official vue website but they're doing the same thing as i do .. i guess?
btw. When i run the fetchData() function in a separate file it does work and i can access the data i got from my API. no changes in the code .. just no vue around it. i'm really confused right now because i don't know what the mistake is.
code:
var $persons = [];
and inside my component:
data: {
persons: $persons,
currentPerson: '',
modal: false
},
created: function() {
this.fetchData()
},
methods: {
fetchData: function () {
var ajax = new XMLHttpRequest()
ajax.open('GET', 'http://api.unseen.ninja/data/index.php')
ajax.onload = function() {
$persons = JSON.parse(ajax.responseText)
console.log($persons[0].fname)
}
ajax.send()
}
},
[...]
link to the complete code
First, make sure that the onload callback is actually firing. If the GET request causes an error, onload won't fire. (In your case, the error is CORS-related, see this post suggested by #Pradeepb).
Second, you need to reference the persons data property directly, not the $persons array that you initialized persons with.
It would look like this (inside your fetchData method):
var self = this;
ajax.onload = function() {
self.persons = JSON.parse(ajax.responseText)
console.log($persons[0].fname)
}

How do I format my AngularJS data model?

Hi I am just beginning with angular and I am struggling to find the answer to what I'm sure is quite a simple thing to do.
I am currently getting the values of some input boxes and pushing them into my scope. This is creating one long 'array' eg:
['data-1','data-2','data-3']
I would like to format my data in the following way instead
$scope.data = [
{
'header1': 'data1-1',
'header1': 'data1-2',
'header1': 'data1-3'
},
{
'header1': 'data2-1',
'header1': 'data2-2',
'header1': 'data2-3'
}
]
This is my function as it currently is.
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
$scope.td.push($(value).val());
});
}
Any help or pointers would be greatly appreciated as I am just getting my head round the angular way
Doing this isn't hard... but before I give you a gun to shoot yourself in the foot, just to say that I think it would be beneficial to explain WHY you want structure in that other format you are mentioning. You seem to have lots of data repetition and that's always a red flag.
Now for the code, you just need to create object before pushing it to the array like:
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
var obj = {
"header1": val + "-1",
"header2": val + "-2"
};
$scope.td.push(obj);
});
}
EDIT:
OK, so you are trying to add new row to the table. First of all, you shouldn't be doing angular.forEach, but rather those input elements in HTML should bind to existing scope object, like:
// obviously use better names than Input1Value
// I am here just giving you example
$scope.bindData = {
Input1Value: null,
Input2Value: null
};
// in HTML you will do
// <input ng-model="bindData.Input1Value" />
// <input ng-model="bindData.Input2Value" />
Now that you've eliminated that nasty angular.forEach you need to have some kind of event handler, for example when user clicks the button you want to add this object to the array to which table is data bound. Just be sure to clone the $scope.bindData object when you add it to array.
$scope.createRow = function(){
var newRowData = $scope.cloneObject($scope.bindData);
$scope.td.push(newRowData);
}
// http://heyjavascript.com/4-creative-ways-to-clone-objects/
// https://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
$scope.cloneObject = function(objToClone) {
var newObj = (JSON.parse(JSON.stringify(objToClone)));
}
To close this answer off - keep in mind, if you ever find yourself directly referencing HTML DOM elements in Javascript with AngularJS - you are doing something wrong. It's a nasty habit to eliminate, especially if you are coming from jQuery background (and how doesn't?), where everything is $("#OhHiThere_ElementWithThisId).
Obviously the main thread on this topic on StackOverflow is this one:
“Thinking in AngularJS” if I have a jQuery background?
However I find that it's too theoretical, so Google around and you may find better overviews like:
jQuery vs. AngularJS: A Comparison and Migration Walkthrough

Read csv data with D3.csv, each column in separate array

I'm new to working D3.js and html/js in general and I'm trying to figure out how to read in a csv file into separate arrays using d3.csv. I have a csv file that looks like this:
cause, prevalence, disability;
cancer, .3, .4;
aids, .5, .5;
malaria, .2, .1;
For now, I'm struggling with creating arrays for "cause", "prevalence", and "disability. Here is the code that I am using:
<html>
<script type="text/javascript" src="../d3/d3.js"></script>
<script type="text/javascript" src="../d3/d3.csv.js"></script>
<script type="text/javascript">
d3.csv('disability.csv', function(csv){
var cause=csv[0];
var prevalence=csv[1];
var disability=csv[2];
for (i=0;i<cause.length;i++)
{
document.write(cause[i] + "<br />");
}
})
</script>
</html>
The document.write portion is simply to test that I have read in the data.
It looks like you mean var cause = csv[0] to refer to the first variable/column, I'm guessing? Without seeing the csv file it's hard to know for sure.
If that's the case, the reason it's not working is javascript arrays unfortunately don't have easy ways of accessing a single variable/column.
csv[0] instead refers to the first row of your csv. In the case of d3's csv module, it'll be a dictionary with keys for each variable (column name) in the csv, e.g.:
console.log(csv[0]);
{ cause: 'A', prevalence: .1, disability: .5 }
So if you want to create separate arrays for each variable/column, you could do something like this:
var cause = [],
prevalence = [],
disability = [];
csv.map(function(d) {
cause.push(d.cause);
prevalence.push(d.prevalence);
disability.push(d.disability);
}
Then you'd be left with the three arrays it looks like you're trying to make. But in d3 you can probably actually just pass the entire csv object as the data to whatever you're creating and then use an accessor function [e.g. .attr('y', function(d) { return d.prevalence; })] to use a specific column/variable in a particular context.