Highcharts: how to remove selected annotation (which was dynamically created)? - highcharts

I have this code https://jsfiddle.net/delux123/62Lmskbx/11/ where user can draw circle, segment and arrow-segment. When each annotation is selected, there is a "delete" button available, which should normally delete the selected annotation.
But this does not works! I tried the following ways for removing them:
1 ) using thischart.currentAnnotation.destroy() will delete the annotation (works for all three types) but then all the further actions stops from working
document
.querySelectorAll('.highcharts-popup-annotations .deletebutton')[0]
.addEventListener(
'click',
function() {
thischart.currentAnnotation.destroy(); //this stops further drawing
thischart.annotationsPopupContainer.style.display = 'none';
}
);
2 ) using deletion by ID thischart.removeAnnotation(thischart.currentAnnotation.options.id) does not works, since the annotations are created dynamically and they do not have IDs assigned to them.
document
.querySelectorAll('.highcharts-popup-annotations .deletebutton')[0]
.addEventListener(
'click',
thischart.removeAnnotation(thischart.currentAnnotation.options.id); //this requires elements to have IDs (which they do not have)
thischart.annotationsPopupContainer.style.display = 'none';
}
);
For the second approach, I even tried to intersect the drawing and to assign a random string as an ID (since the reason that deletion by id does not works, is because the ID is undefined). So under navigation -> bindings I added the object:
circleAnnotation: {
start: function(e) {
var navigation = this.chart.options.navigation;
return this.chart.addAnnotation(
Highcharts.merge({
id: randomStr() //this is a method that generates random string
},
navigation
.annotationsOptions,
navigation
.bindings
.circleAnnotation
.annotationsOptions
)
);
}
}
The both approaches are not working.

You can pass a direct annotation object to the removeAnnotation method:
thischart.removeAnnotation(thischart.currentAnnotation);
Live demo: https://jsfiddle.net/BlackLabel/5ycf3s4o/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Chart#removeAnnotation

Related

SAPUI5 Table Cell Custom Control Binding

I was wondering if someone could shed some light on the issue I am having. I can't seem to get the data model updated when I use my custom input control in a cell. When using the standard sap.m.Input, updates to the data model are performed properly.
Here is the Plunker: https://plnkr.co/edit/GxE6F8DbW9DHqWjpfdO0
I have overrriden the getter for the 'value' properly to get the data from the entry field. Debugging in Chrome shows that this function is not called which I believe is the reason the data model is not updated.
getValue: function() {
var me = this;
var ef = sap.ui.getCore().byId(me.sId + '-ef');
return ef.getProperty('value');
}
Basically, there is a table with two rows and four columns. The first column uses my custom input. The second column has a custom button with an aggregation (additionalParameters) containing the bound data used in the first column. The third column uses a standard sap.m.Input and lastly the fourth column is again a custom button with the 'additionalParameters' aggregation bound to the data in the third column. When any of the buttons are pressed it fires an event which in turn updates the 'Sample' input field.
So, when I type something in the first column, tab out and press the respective 'PB1' button, only the original data from the model appears in the 'Sample' input field. If I type something in the third column, tab out of the field and press the respective 'PB2' button, the 'Sample' input field is properly set by the newly entered data implying the data model has been updated.
Edit
I can get the value from the Input no problem. Maybe I can clarify. The 'MyCustomInput' in cell 1 has 'value' bound to 'list>colOne'. The 'MyCustomButton' in cell 2 has the 'text' of the first item in aggregation 'additionalParameters' bound to the same 'list>colOne'. Any text entered into MyCustomInput does not update the model and thus does not update the 'additionalParameters' item. If I do the exact same thing but use a standard Input instead of MyCustomInput it works. As can be seen with the Input in Cell 3 and the button in Cell 4.
Thank you
May be you can provide more information. We have a simple example here
http://jsbin.com/yukujag/edit?html,js,output and it works on with getValue and setValue
sap.ui.define(['sap/m/Input', 'sap/m/Button', 'sap/m/VBox'],
function(Input, Button, VBox) {
Input.extend('MyInput', {
value: 'abc',
renderer: {},
getValue: function() {
return this.value;
},
setValue: function(v) {
this.value = v;
},
});
var oInput = new MyInput();
var oBox = new VBox({
items: [
oInput,
new Button({
text: 'Test',
press: function() {
alert(oInput.getValue());
}
})
]
})
oBox.placeAt('content');
});
Thanks
-D

angular ui-grid selecting all under grouping

https://jsfiddle.net/4byyuqtc/1/
I'm looking to have the ui-grid select all "children" under a grouping when the grouping line is selected. In this case Kit Kat(1), Mr. Goodbar(1), Krackel(2) and ultimately selecting the actual records (the non bold lines). One would expect that when selecting a parent in a grouping all it's children would get selected as well.
Currently when selecting the 1 grouping above the actual records in the data (the non bold lines) it does select those actual records with the following code:
$scope.gridApi.selection.on.rowSelectionChanged($scope, function (rowChanged) {
console.log(rowChanged.treeLevel);
if (typeof (rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
// this is a group header
children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
console.log(children);
children.forEach(function (child) {
if (rowChanged.isSelected) {
$scope.gridApi.selection.selectRow(child.entity);
} else {
$scope.gridApi.selection.unSelectRow(child.entity);
}
});
}
});
I'm not experienced enough with ui-grid at this point to figure out how to cycle through children of the selected line and select all of them.
[EDIT]
With Paul's code below it doesn't select the groupings but it's closer. This screenshot is me selecting the first 337 record. Notice it selects that record and all the lowest child records (which is good because ultimately those are the ones that matter) but visually the grouped records (MFG and Item Desc group) aren't selected and need to be as the user won't ever open the lowest data records so they need to see the groups selected.
I checked the documentation and I don't think there's any exposed API Method. You could recursively select/deselect rows as a solution. Please try out the example below.
$scope.gridApi.selection.on.rowSelectionChanged($scope, function (rowChanged) {
console.log(rowChanged.treeLevel);
if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
selectChildren(children, rowChanged.isSelected);
}
});
function selectChildren(gridRows, selected) {
if (gridRows && gridRows.length > 0) {
gridRows.forEach(function (child) {
if (selected) {
$scope.gridApi.selection.selectRow(child.entity);
} else {
$scope.gridApi.selection.unSelectRow(child.entity);
}
var children = $scope.gridApi.treeBase.getRowChildren(child);
selectChildren(children, selected); //recursively select/de-select children
});
}
}
Here's a working Plunkr: http://plnkr.co/edit/XsoEUncuigj9Cad1vP5E?p=preview
Handling automatic deselection is a bit more tricky though as it seems the api doesn't handle that really well.
UPDATE
So I checked the jsFiddle you shared and managed to get it working with a slight tweak.
I modified the selectionHandler to the following:
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi;
$scope.gridApi.selection.on.rowSelectionChanged($scope, function(rowChanged) {
if (rowChanged.treeNode.parentRow) { //Added this parent row selection
rowChanged.treeNode.parentRow.setSelected(rowChanged.isSelected);
}
console.log(rowChanged.treeLevel);
if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
selectChildren(children, rowChanged.isSelected);
}
});
Please see this fork of your code: https://jsfiddle.net/1eg5v77w/
The downside with this is that if you select a low level entry (one without children) it will still select its parent. If you really really want this to work as well, you'll have to access the DOM and make some ugly checks.
$scope.gridApi.selection.on.rowSelectionChanged($scope, function(rowChanged, $event) {
var wasHeaderRowClicked = true;
try { //This can be written more beautifully if you used jQuery. But I would still be against it as it relies on the class of the ui-grid never changing when you update your ui-grid version.
wasHeaderRowClicked = $event
.srcElement
.parentElement
.parentElement
.parentElement
.previousElementSibling
.firstChild
.firstChild
.firstChild
.getAttribute('class') === 'ui-grid-icon-minus-squared';
} catch(err) { console.log('Couldnt determine if header row was clicked'); }
if (rowChanged.treeNode.parentRow && wasHeaderRowClicked) {
rowChanged.treeNode.parentRow.setSelected(rowChanged.isSelected);
}
console.log(rowChanged.treeLevel);
if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
selectChildren(children, rowChanged.isSelected);
}
});
Here is the fiddle: https://jsfiddle.net/Lf8p7Luk/1/
I'd also like to add, thanks to this post, that according to the UI-Grid documentation: Group header rows cannot be edited, and if using the selection feature, cannot be selected. They can, however, be exported.
So it is intentional that it's so difficult to get this to work because it's not the intended design. My recommendation would be to alter your logic to either use Tree Levels or get around the selection logic because even though my fork is currently selecting everything, you will most likely run into other issues down the road. For example: I couldn't get automatic deselection to work in the grid when you click on another group header.
If you still have the issue take a look with this..
https://github.com/angular-ui/ui-grid/issues/3911

Drag and Drop with Angular JS and JQuery

Couple of days ago I found this interesting post at http://www.smartjava.org/content/drag-and-drop-angularjs-using-jquery-ui and applied it into my website. However when I progressively using it there is a bug I identified, basically you can not move an item directly from one div to another's bottom, it has to go through the parts above and progress to the bottom. Anyone can suggest where does it goes wrong? The example is at http://www.smartjava.org/examples/dnd/double.html
Troubling me for days already.....
I did this a bit differently. Instead of attaching a jquery ui element inside the directive's controller, I instead did it inside the directive's link function. I came up with my solution, based on a blog post by Ben Farrell.
Note, that this is a Rails app, and I am using the acts_as_list gem to calculate positioning.
app.directive('sortable', function() {
return {
restrict: 'A',
link: function(scope, elt, attrs) {
// the card that will be moved
scope.movedCard = {};
return elt.sortable({
connectWith: ".deck",
revert: true,
items: '.card',
stop: function(evt, ui) {
return scope.$apply(function() {
// the deck the card is being moved to
// deck-id is an element attribute I defined
scope.movedCard.toDeck = parseInt(ui.item[0].parentElement.attributes['deck-id'].value);
// the id of the card being moved
// the card id is an attribute I definied
scope.movedCard.id = parseInt(ui.item[0].attributes['card-id'].value);
// edge case that handles a card being added to the end of the list
if (ui.item[0].nextElementSibling !== null) {
scope.movedCard.pos = parseInt(ui.item[0].nextElementSibling.attributes['card-pos'].value - 1);
} else {
// the card is being added to the very end of the list
scope.movedCard.pos = parseInt(ui.item[0].previousElementSibling.attributes['card-pos'].value + 1);
}
// broadcast to child scopes the movedCard event
return scope.$broadcast('movedCardEvent', scope.movedCard);
});
}
});
}
};
});
Important points
I utilize card attributes to store a card's id, deck, and position, in order to allow the jQuery sortable widget to grab onto.
After the stop event is called, I immediately execute a scope.$apply function to get back into, what Misko Hevery call,s the angular execution context.
I have a working example of this in action, up in a GitHub Repo of mine.

JQGrid ContextMenu - Dynamic Menus

I have a page, which is used for building queries and running them against different entities (Kind of a query builder/generic search).
The results are displayed in JQGrid, so effectively the same grid will be used for rendering results from different entities.
This results grid has to support context menus, which will differ for each entity. So I need a way to change the context menu as per the entity. Each entity may have different number of menu items in context menu and each item may respond in a different manner (sometimes an alert, sometimes an action spawning in a different tab).
Rendering different menus (through li) is not an issue but attaching the methods to the li is proving to be a challenge. Any pointers will be highly appreciated.
I am using jquery.contextmenu-ui.js .
Following is from a sample that I picked from their (JQGrid) site
function initGrid() {
$("#EntityGrid").contextMenu('cMenu'
,{
bindings: { /* I would like to avoid this and pass all the actions to one method*/
'edit': function (t) {
editRow();
},
'add': function (t) {
addRow();
},
'del': function (t) {
delRow();
}
},
onContextMenu: function (event, menu) {
var rowId = $(event.target).parent("tr").attr("id")
var grid = $("#EntityGrid");
grid.setSelection(rowId);
return true;
}
}
);
}
Thanks,
Avinash
You can use onShowMenu callback of contextMenu instead of static binding using bindings. In the same way the menuId used as the first parameter of contextMenu could be the id of dynamically created div with empty <ul>. The onShowMenu has the form
onShowMenu: function (e, $menu) {
// here one can clear `<ul>` child of $menu
// and append it with "<li>" items
return $menu;
}
In the answer you will find an example of the code which build menu dynamically.

jQuery UI autocomplete widget - how to get a reference to the menu?

I want to be able to get a reference to the menu object that autocomplete builds, (so I can get the .attr("id") for example), but I'm not very familiar with jQuery/javascript. In the source, I found this:
https://github.com/jquery/jquery-ui/blob/1-9-stable/ui/jquery.ui.autocomplete.js#L182
so there is an object flying around, I just can't seem to find how to get hold of it.
So, for example, if I've got an input with an autocomplete bound to it like this:
// input = reference to the input text box on the form
input.autocomplete({
select: function(event, ui) {
// how to get the reference here?
// some things I've tried
// return input.menu
// return input.data("menu")
// and a few others but they didn't work either
}
});
I tried looking at the data object itself, but there were so many options I could spend all day looking at it and still not find what I'm looking for.
You can get the widget's reference by looking into dataset assigned to its root element (input). Then fetching menu property (and its underlying element) is kinda trivial. )
select: function(event, ui) {
// that's how get the menu reference:
var widget = $(this).data('ui-autocomplete'),
menu = widget.menu,
$ul = menu.element,
id = $ul.attr('id'); // or $ul[0].id
}
... as this within select function refers to the <input> when this function called as an event handler.
A simpler way to do this:
$(this).autocomplete('widget');
It does the same as:
select: function(event, ui) {
// that's how get the menu reference:
var widget = $(this).data('ui-autocomplete'),
menu = widget.menu,
$ul = menu.element,
id = $ul.attr('id'); // or $ul[0].id
}
It gives the ul list
$(this).autocomplete('widget').attr('id');

Resources