Disable tooltip for 0 value in NVD3 multiBarHorizontalChart - javascript

I'm using nvd3 for a multiBarHorizontalChart,i need guidance on how to disable tooltip for 0 values.
.tooltip(function(key, x, y, e) {
return '<h3>' + key + ' ' + e.point.label + '</h3>' + '<p>' + y + '</p>';
})
This function i am using to display tooltip value.

Unfortunately, I didn't find any non-hacky way to do this.
The not-so-elegant way would be to replace original handler for event tooltipShow with the custom one, that will check the value:
var originalTooltipHandler = chart.dispatch.on("tooltipShow");
chart.dispatch.on("tooltipShow", function (e) {
// check whatever you like here
if (e.value != 0) {
// and call original handler only when needed
originalTooltipHandler.apply(this, arguments);
}
});
But this is not enough if your chart is updated (like, you expect resize event or allow changing displayed data by clicking on legend, so almost in all cases), because when chart.update() is executed, it will restore original tooltipShow handler. To workaround that we have to override chart.update() too:
nv.addGraph(function () {
chart = nv.models.multiBarHorizontalChart()
// ... parameters setting here
// before data was set, `chart.update()` is not available
d3.select('#chart1 svg')
.datum(long_short_data)
.call(chart);
// now we can override
filterTooltips(chart, function (e) {
return e.value !== 0;
});
// ...
return chart;
function filterTooltips(chart, predicate) {
var originalUpdate = chart.update;
chart.update = function () {
// call to original `update()` will reset filter
originalUpdate.apply(this, arguments);
// set custom filter again
filterTooltips(chart, predicate);
};
var originalTooltipHandler = chart.dispatch.on("tooltipShow");
chart.dispatch.on("tooltipShow", function (e) {
if (predicate(e)) {
originalTooltipHandler.apply(this, arguments);
}
});
}
});
See live demo.

Related

Handle cell click event on Angular Gantt

I am trying to customize the behaviour of Angular Gantt.
On cell click : I have to get the current cell value & corresponding header value.
I gone thru the documentation present in this url : https://angular-gantt.readthedocs.io/en/latest/
but couldn't get the required event.
Is there any event present to achieve this functionality. Any help is much appreciated.
I tried following code
mainApp.controller("TestController", function ($scope, TestService) {
$scope.registerApi = function (api) {
api.tasks.on.change($scope, onTaskChange); //working
//TO handle the cell click & corresponding header value
api.core.getDateByPosition($scope, getHeader)
//api.core.on.ready($scope, getDateByPosition) //not working
//api.core.on.rendered($scope, getDateByPosition) //not working
}
var onTaskChange = function (selected) {
$scope.currCell = selected.model;
console.log("onTaskChange: " + selected.model.name);
};
var getHeader= function (getHeader) {
// I should get the current clicked cell header value. But getting error
};
}
Answering my own question as someone may find it useful.
I am able to achieve this from this link
https://angular-gantt.readthedocs.io/en/latest/configuration/customize/
Following is the code which I used:
$scope.registerApi = function (api) {
api.directives.on.new($scope, function (dName, dScope, dElement, dAttrs, dController) {
if (dName === 'ganttTask') {
dElement.bind('click', function (event) {
debugger;
$scope.RowName1 = dScope.task.row.model;
$scope.currentTask = dScope.task.model;
});
}
else if (dName === 'ganttRow')
{
dElement.bind('click', function (event) {
debugger;
$scope.RowName = dScope.row.model.name;
$scope.Header = api.core.getDateByPosition(event.offsetX, true)
});
}
});

how to make the jquery function load before one ajax function finish

How do I fire one event before the previous function completed its function?
I have the following AJAX code :
var BrainyFilter = {
//...
init: function (opts) {},
changeTotalNumbers: function (data) {
jQuery(BrainyFilter.filterFormId).find('.bf-count').remove();
jQuery(BrainyFilter.filterFormId).find('option span').remove();
jQuery(BrainyFilter.filterFormId).find('select').removeAttr('disabled');
jQuery('.bf-attr-filter').not('#bf-price-container').find('input, option')
.attr('disabled', 'disabled')
.parents('.bf-attr-filter')
.addClass('bf-disabled');
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
jQuery('.bf-attr-' + data[i].id + ' .bf-attr-val').each(function (ii, v) {
if (jQuery(v).text() == data[i].val) {
var parent = jQuery(v).parents('.bf-attr-filter').eq(0);
var isOption = jQuery(v).prop('tagName') == 'OPTION';
var selected = false;
if (isOption) {
jQuery(v).removeAttr('disabled');
selected = jQuery(v)[0].selected;
} else {
parent.find('input').removeAttr('disabled');
selected = parent.find('input')[0].checked;
}
parent.removeClass('bf-disabled');
if (!selected) {
if (!isOption) {
parent.find('.bf-cell').last().append('<span class="bf-count">' + data[i].c + '</span>');
} else {
jQuery(v).append('<span> (' + data[i].c + ')</span>');
}
}
}
});
}
jQuery('.bf-attr-filter input[type=checkbox]').filter(':checked')
.parents('.bf-attr-block').find('.bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text();
jQuery(v).text(t);
});
// since opencart standard filters use logical OR, all the filter groups
// should have '+' if any filter was selected
if (jQuery('.bf-opencart-filters input[type=checkbox]:checked').size()) {
jQuery('.bf-opencart-filters .bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text().replace('+', '');
jQuery(v).text(t);
});
}
}
// disable select box if it hasn't any active option
jQuery(BrainyFilter.filterFormId).find('select').each(function (i, v) {
if (jQuery(v).find('option').not('.bf-default,[disabled]').size() == 0) {
jQuery(v).attr('disabled', 'true');
}
});
},
//...
} // close the BrainyFilter
I also have another jQuery file running to get the bf-count value using $('.bf-count').text().
When the page load, the bf-count value is empty. Since the code above inject the bf-count, I will need to wait until it finishes the for loop in order to get the bf-count value.
What is the best way to approach this?
without knowing how the second js file is loaded, I can only give you a guesstimate suggestion.
If you want to run the second js file code after the page is fully loaded, you can wrap the code in:
jQuery(window).load(function(){
//your code here. runs after the page is fully loaded
});
jQuery documentation: http://api.jquery.com/load-event/
"The load event is sent to an element when it and all sub-elements have been completely loaded. This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object."

JQuery custom events 'on' non-document element when not triggered from a DOM element?

I can't seem to get JQuery custom events to work as advertised when the trigger is not bound to a specific DOM element. I am trying to understand how to implement a model-view-controller pattern in which the model has absolutely no DOM-related code, yet triggers custom events for the view.
The model's setValue() method trigger's a custom 'ValueChanged' event using '$.event.trigger'.
In the view, 'on' is used to bind both a 'keyup' and the custom 'ValueChanged' event to an input element. The keyup handler calls the model's setValue() method, but the input element's ValueChanged handler never gets called.
I can bind the document to handle the ValueChanged, but that handler has no reference to the input -- it also gets called twice. I would really like the input element's handler to be called so I get a reference to the input element.
In the code example I show two Views that get created by the $(document).ready function, but ideally I would like to be able to generate additional views programmatically (this is just an example of the problem).
More than likely the problem is with my misunderstanding of some crucial concept.
Any help would be much appreciated.
<html>
<head>
<script src="jquery-1.10.2.js"></script>
<script>
function Model (value){
this.value = value;
}
Model.prototype.setValue = function (value){
console.log('Model.setValue: ', value);
oldValue = this.value;
this.value = value;
$.event.trigger({
type: 'ValueChanged',
oldValue: oldValue,
newValue: this.value
});
}
Model.prototype.getValue = function (){
return this.value;
}
function View (container, model){
if(typeof container == 'string')
this.container = document.getElementById(container);
else
this.container = container;
this.input = document.createElement('input');
this.input.model = model;
this.container.appendChild(this.input);
this.span = document.createElement('span');
$(this.span).text('initial value');
this.container.appendChild(this.span);
// set the input component to show the number
$(this.input).val(this.input.model.getValue());
ref = this;
// this one gets called twice
$(document).on('ValueChanged', function (e){
console.log('ValueChanged (document): ' + e.newValue + ' ' + e.target);
});
// this one never gets called
$(this.input).on('ValueChanged', function (e){
console.log('ValueChanged (input): ' + e.newValue);
// set the input component to show the number
$(ref.span).text(e.newValue);
});
$(this.input).on('keyup', function(e) {
if (e.which == 13 || e.keyCode == 13) {
//set the new value in the model; 'this' is the input component that got the keyup event
this.model.setValue(this.value);
//e.preventDefault();
}
});
}
$(document).ready(function() {
view1 = new View('container1', new Model(1));
view2 = new View('container2', new Model(2));
console.log('view1.input.model.value: ' + view1.input.model.getValue() + ', view2.input.model.value: ' + view2.input.model.getValue());
console.log('view1.input.value: ' + view1.input.value + ', view2.input.value: ' + view2.input.value);
});
</script>
</head>
<body>
<div id='container1'>
</div>
<div id='container2'>
</div>
</body>
</html>
I have arrived at a solution with the correct behavior, shown below, but it requires the Model to keep an array of the Views (and also each View must call the model's attachView() method :(
This version also shows multiple views for the same model, which is an important feature of model-view-controller.
Is there any way to achieve the same result but without the Model needing any information about the Views?
Thanks!
<html>
<head>
<script src="jquery-1.10.2.js"></script>
<script>
function Model (value){
this.value = value;
this.views = [];
}
Model.prototype.attachView = function (view){
this.views.push(view);
}
Model.prototype.setValue = function (value){
console.log('Model.setValue: ', value);
oldValue = this.value;
this.value = value;
for(var i = 0; i < this.views.length; i++){
$(this.views[i]).trigger({
type: 'ValueChanged',
oldValue: oldValue,
newValue: this.value
});
}
}
Model.prototype.getValue = function (){
return this.value;
}
function View (container, model){
this.container = document.getElementById(container);
this.input = document.createElement('input');
this.input.model = model;
model.attachView(this);
this.container.appendChild(this.input);
this.span = document.createElement('span');
$(this.span).text('initial value');
this.container.appendChild(this.span);
// set the input component to show the model's number
$(this.input).val(this.input.model.getValue());
$(this).on('ValueChanged', function (e){
console.log('ValueChanged: ' + e.newValue);
// set the span to show the number
$(this.span).text(e.newValue);
});
$(this.input).on('keyup', function(e) {
if (e.which == 13 || e.keyCode == 13) {
//set the new value in the model; 'this' is the input component that got the keyup event
this.model.setValue(this.value);
}
});
}
$(document).ready(function() {
view1 = new View('container1', new Model(1));
var model2 = new Model(2);
view2 = new View('container2', model2);
view3 = new View('container3', model2);
console.log('view1.input.model.value: ' + view1.input.model.getValue() + ', view2.input.model.value: ' + view2.input.model.getValue());
console.log('view1.input.value: ' + view1.input.value + ', view2.input.value: ' + view2.input.value);
});
</script>
</head>
<body>
<div id='container1' />
<div id='container2' />
<div id='container3' />
</body>
</html>

JS Hooking Custom Events

I have a client side script for my .NET control. The script hooks all the :checkbox items to trigger an event called OnOptionItemSelected.
function ControlScript() {
this.OnOptionItemSelected = function (e) { /* do something */ }
this.Configure = function () {
// hook change event of items in the input control
$('#' + this.ControlID).find(":radio, :checkbox")
.on("change", this, this.OnOptionItemSelected);
}
}
(Some pieces of code removed)
In another place on the page, I get the client side script for a particular control and need to hook the OnOptionItemSelected event. How would I hook this event?
var script = GetScriptForControl(ID);
if (script)
// hook script.OnOptionItemSelected to a custom function ????
I will suppose that the GetScriptForControl return a instance of ControlScript. So what i would do is:
if(script) {
oldOptionItemSelected = script.OnOptionItemSelected
script.OnOptionItemSelected = function(ev) {
/* before OptionItemSelected code HERE */
oldOptionItemSelected.call(script,ev);
/* after OptionItemSelected code HERE */
};
/* rebind the events */
script.Configure();
}
I actually found an answer that better suits my needs:
function ControlClientSide()
{
this.CheckBoxClicked = function() { }
this.Configure = function() {
$('#checkBox100').change(function() { $(this).triggerHandler("CheckBoxClicked"); }.bind(this));
};
this.Configure();
};
var x = new ControlClientSide();
$(x).on("CheckBoxClicked", function() { alert( this.constructor.name + ' Hi'); })
.on("CheckBoxClicked", function() { alert( this.constructor.name + ' Hi2'); });
The only thing I cant figure out is how to get "this" to be the checkbox in the "CheckBoxClicked" event handlers. Currently it is set to the ControlClientSide function.
See example: http://jsfiddle.net/KC5RH/3/

Cannot call method 'setData' of undefined

EDIT 1
Using the method dataTransfer.setData make it works !
http://jsfiddle.net/UH7Sf/1/
d3.event.dataTransfer.setData('type',type);
I'm facing a issue using methods setData and getData within d3.event
I have to div with a draggable img element in the top one, and some drop capabilities on the second one.
d3.select('img').on('dragstart', function() {
var type = d3.select(this).attr('type');
console.log("Drag starts with "+ type);
d3.event.transfer.setData('type',type);
});
d3.select('.drop').on('drop', function() {
d3.event.preventDefault();
var type = d3.event.transfer.getData('type');
console.log("Drag ends with "+ type);
});
d3.select('.drop').on('dragover', function() {
d3.event.preventDefault();
});
Here is the fiddle : http://jsfiddle.net/UH7Sf/
There is another solution to do this (transfering data within the drag&drop) using d3 ?
There is no object inside event called transfer. One thing you can do is try event.setData(), but I don't think that's what you want either, because the dragstart and drop events are two different event objects.
You can instead have a separate local variable to use. E.g.
var draggedType = null;
d3.select('img').on('dragstart', function() {
var draggedType = d3.select(this).attr('type');
console.log("Drag starts with "+ draggedType);
});
d3.select('.drop').on('drop', function() {
d3.event.preventDefault();
console.log("Drag ends with "+ draggedType);
// do something with draggedType here
draggedType = null; // reset draggedType to null when done, just in case
});

Categories

Resources