d3 change text bound to data - arrays

Conncerning the code below I have three questions,
which seem to be related to each other:
(1) I try to change the text of a single div, which was previously
bound as part of an array
-> if the way I do it below (changing textContent directly),
how can I get access to the data-array instead (is there an index available
to adress the correct element of the data-array directly)?
(2) what happens if I bind an array of divs and then unbind single divs?
will all divs be unbound afterwards ? or does it result in a mix
of bound or unbound elements: is data[1] still bound, bout data[3]
not?
(3) why do the data not update if I change them (they do not update at all,
neither if I assign something to textContent, nor if I do not:
you can check this, if you click on another number but the 8: also in that case
the array does not update)!
If the questions sound somehow unconnected please
try the code first (click the numbers with the mouse, especially the "-8-"),
this should clarify what I mean a bit!
<!DOCTYPE html>
<html>
<head>
<script src="d3.js"></script>
<style>
div.bar:hover {
border: 1px solid yellow;
}
div.bar {
width: 50px;
}
</style>
</head>
<body>
<div class="labeledDiv">
</div>
<script>
var data = [4,8,16,23,9];
function mousedown() {
var txt = d3.select(this).node().textContent;
alert("text of this node is: " + txt);
if (txt == "-8-") {
d3.select(this).node().textContent = "hit!!!";
}
var id = d3.select(this).node().id;
alert("id IS NOT:" + id);
var x = "";
for (i = 0;i<5;i++) {
data[i] = data[i]+1;
x = x + i + ": " + data[i] + " " ;
}
alert("x: " + x);
}
d3.select(".labeledDiv")
.selectAll("div")
.data(data)
.enter().append("div")
.attr("class","bar")
.text(function(d) { return "-" + d + "-";});
d3.select(".labeledDiv")
.selectAll("div")
.on("mousedown",mousedown);
</script>
</html>

(1) node has no property id (unless I set it manually)
(2) changing data-array needs an update function to be called:
function update() {
d3.select(".labeledDiv")
.selectAll("div")
.text(function(d,i) { return "text: " + d + " " + data[i]; });
};
function mousedown() {
//... same as above
// and then after changing data:
update();
}

Related

Trying to get array to log if its empty to the console

I am working with a map and data. When you click on a state, it pulls the appropriate data from the JSON and displays the stores in an unordered list. If a state does not have data (Wyoming is one for example), it should be logging to the console that it is empty. So when you click on Wyoming, it should realize the array is empty and log that its empty to the console.
$("#paginateView").hide();
$("#stateButton").hide();
$("#mapWhereToBuy").usmap({
click: function(event, data) {
$.ajax({
url: 'https://165.227.69.79:8443/alanric/armaster/state/' + data.name + '?callback=functionCall',
dataType: "jsonp",
cache: true,
jsonpCallback: "functionCall",
success: function(json){
console.log(json);
var storeNames = '<ul class="list-group">';
$.each(json, function (i, item) {
var stores = item.cname;
if (stores && stores.length) {
storeNames += '<li class="content list-group-item">' + stores + '</li>';
} else {
console.log("Sorry, we do not carry products in your state.");
}
});
storeNames +='</ul>';
$('#clicked-state').html(storeNames);
$("#paginateView").show();
$("#stateButton").show();
$("#stateButton").html('You Selected:' + ' ' + data.name);
pageSize = 15;
showPage = function(page) {
$(".content").hide();
$(".content").each(function(n) {
if (n >= pageSize * (page - 1) && n < pageSize * page)
$(this).show();
});
}
showPage(1);
$("#paginateView li a").click(function() {
$("#paginateView li a").removeClass("current");
$(this).addClass("current");
showPage(parseInt($(this).text()))
});
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.cento.com/js/raphael.js"></script>
<script src="https://www.cento.com/js/jquery.usmap.js"></script>
<div id="mapWhereToBuy"></div>
<h1>Search By State</h1>
<h4>Click on a state to see store locations</h4>
<hr>
<div id="clicked-state"></div>
Some Array methods do not recognize empty arrays, like map(). But map() will always return something even an empty array. First example is .map() with an array of numbers the second example is with an empty array.
Demo
var array1 = [];
var result1 = array1.map(function(obj, idx) {
var ID = idx;
return idx;
});
console.log(result1);
var array2 = [0, 0, 1];
var result2 = array2.map(function(obj, idx) {
var ID = idx;
return idx;
});
console.log(result2);
I was able to get it to work by using this "json.length === 0". If there is a better alternative please let me know thanks!

Passing through variable on ng-compile returns undefined

var suggestionElement = document.createElement('div');
vm.suggestActivate = function(keyCode) {
if(keyCode === 32) {
if(vm.summaryData) {
var words = vm.words;
var suggestions = "<div></div>";
var targetElement = event.srcElement;
var targetElementModel = targetElement.getAttribute("ng-model");
for(var i = 0; i < 5; i++) {
$log.debug(targetElementModel);
suggestions += '<div ng-click="vm.appendSuggestion(targetElementModel)" style="padding: 10px; border: 1px solid #B0B0B0;" onMouseOver="this.style.backgroundColor=\'#D8D8D8\'" onMouseLeave="this.style.backgroundColor=\'#F3F3F3\'">' + words[Math.floor(Math.random() * words.length)] + '</div>';
}
suggestionElement.innerHTML = suggestions;
suggestionElement.setAttribute('style', 'background: #F3F3F3; position: relative; top: -3.9em; width: 25%');
suggestionElement.style.display = 'block';
angular.element(targetElement.parentNode).append($compile(suggestionElement)($scope));
}
}
else {
suggestionElement.style.display = 'none';
}
};
vm.appendSuggestion = function(model) {
$log.debug(model);
}
In the above segment of code in the vm.appendSuggestion method, why is model returned as undefined? If I log it during the for loop, it returns a value? I want to return a value in the vm.appendSuggestion method as well. However, I noticed when it is hardcoded, it does not return as undefined.
When you click on the div element to which you have assigned the ngClick directive, Angular tries to find targetElementModel on the $scope and it does not exist there. You cannot pass a value to it like that.
A solution might be to expose a list of target elements to the view and do something like '<div ng-click="vm.appendSuggestion(' + targetElementIndex + ')" .... But this is just an example - you might find a more appropriate solution for your problem.

How do I get clientHeight of parent element when `ng-cloak` is active

I have the following in the linker for my directive....
pre: function preLink($scope, e) {
var element = d3.select(e[0]);
var height = element.node().parentNode.clientHeight;
element
.style({
"border": "7px solid black",
"min-height": height+"px",
"background-image" : "url('http://s3.amazonaws.com/dostuff-production/property_assets/23107/yellow-stripes.png')"
});
element.select(".board")
.style("transform", function(d){
var ch = d3.select(this).node().clientHeight;
return "translate("+0+"px, "+(height/2-ch/2)+"px)"
})
}
And then in my code I have
<jg-body ng-cloak>
The problem is that element.node().parentNode.clientHeight; is 0 thanks to the ng-cloak. Is there a way to defer the link function till after the ng-cloak is removed?
Update
Based on feedback I tried this...
compile: function compile() {
return {
pre: function preLink($scope, e) {
var element = d3.select(e[0]);
$timeout(function() {
var height = element.node().parentNode.clientHeight;
element
.style({
"border": "7px solid black",
"min-height": height + "px",
"background-image": "url('http://s3.amazonaws.com/dostuff-production/property_assets/23107/yellow-stripes.png')"
});
element.select(".board")
.style("transform", function (d) {
var ch = d3.select(this).node().clientHeight;
return "translate(" + 0 + "px, " + (height / 2 - ch / 2) + "px)"
})
})
}
}
}
However, var height = element.node().parentNode.clientHeight; is still 0 and removing ng-cloak fixes it. I created an example plnker here
Would a $timeout or something work?
Yes , it should. Also note that you are checking the height inside the pre link which runs before directive will be compiled. Before it is compiled it will have no content
You should move the code to post link and probably use $timeout there depending on what jg-body templating does. Also if it relies on asynchronous data to render and then get height you have another issue to deal with that would need more code shown to sort out

AngularJS NG-CSV files not downloading

I am fairly new to AngularJS, i am trying to generate .csv files from an Array using ng-csv.
Now i have tried everything but the files are not generated, i even tried the most simple example i could see on the internet.
I do not see any errors in the error console but still no files are generated.
I am working under windows with XAMPP.
<!DOCTYPE html>
<html ng-app="APP">
<head>
<meta charset="UTF-8">
<title>angular-count-to example</title>
<style type="text/css">
.element{cursor:pointer;float:left;background-color:#ccc;padding:4px;}
</style>
</head>
<body ng-controller="ExampleController">
<p>{{data}}</p>
<button type="button" ng-csv="data" filename="test.csv">Export</button>
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>
<script type="text/javascript" src="bower_components/angular-sanitize/angular-sanitize.min.js"></script>
<script type="text/javascript" src="bower_components/ng-csv/src/ng-csv/ng-csv.js"></script>
<script>
angular.module('APP',["ngSanitize","ngCsv"]).
controller('ExampleController', function ($scope) {
$scope.filename = "test";
$scope.data = [{a: 1, b:2}, {a:3, b:4}];
});
</script>
</body>
</html>
Above is the simplest example i tried, however even this is not working.
Try out a pure HTML5 Solution. This was a code block i did a while ago. Try customizing for yourself by excluding useless paramaters
function (JSONData, ReportTitle, ShowLabel, reportType, reportName) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
angular.forEach(arrData, function (data, index) {
if (data.date != undefined)
data.date = dateFormat(data.date)
});
var CSV = '';
//Set Report title in first row or line
CSV += ReportTitle + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
row += index + ';';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
//var temp = arrData[i][index].toString().replace('.', ',');
//arrData[i][index] = temp;
row += '"' + arrData[i][index] + '";';
}
row = row.split('.').join(",");
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g, "_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
It looks like you are linking to the wrong ng-csv.js. The ng-csv.js file in the bower_components/ng-csv/src/ng-csv/ folder doesn't contain all of the code, you need to use the build version. Try this:
<script type="text/javascript" src="bower_components/ng-csv/build/ng-csv.js"></script>

Loop thru 100 background-images onClick

a) I have 100 body-background images
b) and a single link
The background image should change to the next one in the lot every time the link is clicked, going thru the lot one by one (would be nice if the name/number of current image also is displayed).
The images are all in a dedicated directory. jQuery is already loaded.
Anyone know where I can find the code to do this simply?
All the best...
var allImages = ["path/to/image2", "path/to/image1"];
$(document).ready(function() {
$("#theLink").click(function() {
var newImageLink = allImages.pop();
$("body").css("background-image", "url(" + newImageLink + ")");
});
});
You don't necessarily need to hard code the path to your images that way. You can name your images something like image_xxx.png, 1 - 100 and refer to that name in one place. That way if you ever need to change the name, you can easily do it in one place. Furthermore, make sure that the href attribute of the anchor tag is set to # to prevent page reloads on each click.
Here's a more complete solution that also does range checking and it wraps around when we reach our limit:
<script language="javascript" type="text/javascript">
<!--
var ctr = 0;
$(document).ready( function () {
nextBg();
});
function nextBg()
{
++ctr;
if( ctr <= 100 )
{
$("body").css( "background-image", "url(images/image_" + ctr + ".png)" );
$("#message").text("Current image: " + "image_" + ctr + ".png");
if( ctr == 100 )
ctr = 0;
}
}
//-->
</script>
</head>
<body>
<div>
<span id="message"></span><br />
<a href="#" onclick="nextBg();" />Next</a>
</div>
</body>
(added counter to gs' answer)
var allImages = ["path/to/image2", "path/to/image1"];
var counter = 0;
$(document).ready(function() {
$("#theLink").click(function() {
var newImageLink = allImages.pop();
$("body").css("background-image", "url(" + newImageLink + ")");
$("#displayhere").html("<b>"+ counter +"</b>");
counter = counter + 1;
});
});

Resources