I'm having some problem handling the JS onClick event and Dojo.
Waht i'm trying to achieve is to somehow edit data from the first cell inline.
Consider this HTML:
<table class="datatable" id="officestable" name="officestable">
<tr>
<td>xxxxx</td>
<td>
<button id="officeeditbtn3" data-dojo-type="dijit.form.Button" type="button" data-dojo-props="iconClass:'advanEditIcon',onClick: function() { editOfficeFieldInline(this, 3); }">Edit</button>
<button id="officeconfigbtn3" data-dojo-type="dijit.form.Button" type="button" data-dojo-props="iconClass:'advanConfigIcon',onClick: function() { configOffice(this, 3); }">Config</button>
<button id="officeremovebtn3" data-dojo-type="dijit.form.Button" type="button" data-dojo-props="iconClass:'advanRemoveIcon',onClick: function() { removeOffice(this, 3); }">Remove</button>
</td>
</tr>
To handle all this i have the following (relevant) methods in javascript.
function editOfficeFieldInline(buttonElem, officeid) {
var table = document.getElementById("officestable"); //get table
var operationsCell = buttonElem.domNode.parentNode; // get second cell where button are
var trline = operationsCell.parentNode; // get tr element
var dataCell = trline.firstChild; // get cell where data is to be edited
var currentContent = dataCell.innerHTML; // ... self explainable...
var tdcellHTML;
tdcellHTML = "<input id=\"editofficebox\" type=\"text\">"; // adding an input placeholder
dataCell.innerHTML = tdcellHTML; // replace cell content with edit box
// attach dijit to pre-created edit box
var editofficebox = new dijit.form.TextBox({
value: currentContent,
style: {
width: "190px",
}
},
"editofficebox");
addSaveCancelButtons(table,
operationsCell,
function(){
// 'save' actions
// save new data using ajax (working!)
saveOfficeName(officeid, dataCell, currentContent, operationsCell);
},
function(){
// 'cancel' actions
destroyOfficeFieldInline(table, false);
setCellVal(dataCell, currentContent);
}
);
}
function addSaveCancelButtons(table, operationsCell, saveAction, cancelAction) {
operationsCell.innerHTML += "<button id=\"savebutton\">Save</button>";
operationsCell.innerHTML += "<button id=\"cancelbutton\">Cancel</button>";
var savebutton = new dijit.form.Button({
iconClass: "advanSaveIcon",
onClick: saveAction
},
"savebutton");
var cancelbutton = new dijit.form.Button({
iconClass: "advanCancelIcon",
onClick: cancelAction,
},
"cancelbutton");
}
// this is a generic function. parameters are not really needed in this case
function destroyOfficeFieldInline(tableElement, bNew) {
dijit.byId("editofficebox").destroy();
destroySaveCancelButtons();
if (bNew) {
destroyNewRow(tableElement);
}
}
function destroySaveCancelButtons() {
dijit.byId("savebutton").destroy();
dijit.byId("cancelbutton").destroy();
}
function setCellVal(cell, val) {
cell.innerHTML = val;
}
Now, the code works for the first time.
But somehow, if I click "cancel" after clicking "edit", then pressing "edit" again will do nothing. The dynamic fields will not be created again.
I'm guessing that something is not being cleaned up correctly, but i ran out of ideas...
What's wrong with this code?
PS.I'm open to alternative ways to achieve this...
[EDIT]
I found that these seem to be the offending lines of code:
operationsCell.innerHTML += "<button id=\"savebutton\">Save</button>";
operationsCell.innerHTML += "<button id=\"cancelbutton\">Cancel</button>";
As soon as the first one is executed, all buttons within the cell lose their event listeners, namely, onclick.
Anyone knows the reason for this?
May be this one will help you..
first of all you have to store the id of button the the temp variable and you have to use that variable to destroy controls
for example
var temp = dijit.byId('savebutton');
temp.destroy();
i hope this will help you.
Ok. Just figured out what was going on (with the help from Dojo community) so i came back here to share the solution.
The innerHTMl was (supposedly) appended with the new button tags, but in fact it was being replaced, because of the way the += operator works.
When replaced, the original contents were destroyed, and then recreated, but this time without its event listeners / events.
So, the solution is to use the dijit placeAt method:
function addSaveCancelButtons(table, operationsCell, saveAction, cancelAction) {
var savebutton = new dijit.form.Button({
id: "savebutton",
iconClass: "advanSaveIcon",
label: "Guardar",
onClick: saveAction
}).placeAt(operationsCell);
var cancelbutton = new dijit.form.Button({
id: "cancelbutton",
iconClass: "advanCancelIcon",
label: "Cancelar",
onClick: cancelAction,
}).placeAt(operationsCell);
}
Related
I want to ask if it's possible to add new content "outside" of content that has beed added recently.
So, i have custom button which adds some simple HTML.
And what i want to archive is to add the same html but outside of existing one, so in place marked green on my screenshot. I'm looking for a way how to escape from this div, and add new html after existing one.
below screenshot, and code - how javascript button in generated - very simple.
Thanks for advice.
var oferta = '<div class="col-sm-3"><h1>test</h1></div>'
setup: function (ed) {
ed.addButton('example', {
title: 'example.desc',
image: './/',
text: 'Oferta',
icon: true,
onclick: function () {
tinyMCE.execCommand('mceInsertContent', false, oferta);
}
});
},
EDIT: Below how this looks now when i hit button 3 times in row.(every next content is added to existing one.)
Is very easy to do it try to change you code with this example.
setup: function (editor) {
ed.addButton('example', {
title: 'example.desc',
image: './/',
text: 'Oferta',
icon: true,
onclick: function () {
var h1 = editor.dom.create('h1');
h1.innerText = 'test';
var oferta = editor.dom.create('div' ,{'class': 'col-sm-3'});
oferta.appendChild(h1);
var divs = editor.dom.select('div');
if(divs && divs.length > 0){
editor.dom.insertAfter(oferta,divs[divs.length-1])
}else{
tinyMCE.execCommand('mceInsertContent', false,oferta.outerHTML);
}
editor.selection.select(oferta);
editor.selection.collapse(true);
}
});
},
I am making a cart application in Angular using Angular Bootstrap.
When hovering over the cart icon a tooltip should appear. The tooltip's content should change based on if the item is already in the cart or not.
So, here is the html:
<h3><i class="fa fa-shopping-basket" ng-click="add2Cart(item.Name)" tooltip-placement="right" uib-tooltip-html="itemtooltiptext(item.Name)" aria-hidden="true"></i></h3>
Basically, in order to check if the item is already in the cart, I want the tooltip text to resolve from a function. My understanding from the documentation is this is supported as long as the HTML is trusted.
It says,
uib-tooltip-html $ - Takes an expression that evaluates to an HTML string. Note that this HTML is not compiled. If compilation is required, please use the uib-tooltip-template attribute option instead. The user is responsible for ensuring the content is safe to put into the DOM!
So my itemtooltiptext() function is...
$scope.itemtooltiptext = function(name) {
if (localStorage.getItem("cart") === null) {
return $sce.trustAsHtml("Add " + name + " to Cart!");
} else {
var cart = JSON.parse(localStorage.getItem("cart"));
for (var i = 0; i < cart.length; i++) {
if (cart[i] == name) {
console.log("already in cart");
return $sce.trustAsHtml(name + "already in Cart!");
}
}
return $sce.trustAsHtml("Add " + name + " to Cart!");
}
}
This results in an
Infinite $digest Loop Error
As detailed here: https://stackoverflow.com/a/19370032
But the problem is I need it to come from a function with the various conditions? So should I be using a template? I don't understand how that would work any better because I still need dynamic text served from the template... so what is the solution?
Thank you.
This is not how you use uib-tooltip-html, apparently it causes an infinite digest loop, fortunately the demo plunk shows you how to do it.
You need to get/calculate your html, bind to some scope variable and bind it into uib-tooltip-html
js
$scope.itemtooltiptext = function() {
$scope.htmlTooltip = $sce.trustAsHtml('I\'ve been made <b>bold</b>!');
};
$scope.itemtooltiptext();
html
<button uib-tooltip-html="htmlTooltip" class="btn btn-default">Tooltip</button>
If you still want to bind a function to your tooltip, you can do like so
<button uib-tooltip="{{itemtooltiptext()}}" class="btn btn-default">Tooltip</button>
Note that this approache will have the function invoked every digest cycle.
I ran into this infinite digest cycle issue where I needed a dynamic tooltip... it caused angular to recalculate it every time as a new value (even though it was the same). I created a function to cache the computed value like so:
$ctrl.myObj = {
Title: 'my title',
A: 'first part of dynamic toolip',
B: 'second part of dynamic tooltip',
C: 'some other value',
getTooltip: function () {
// cache the tooltip
var obj = this;
var tooltip = '<strong>A: </strong>' + obj.A + '<br><strong>B: </strong>' + obj.B;
var $tooltip = {
raw: tooltip,
trusted: $sce.trustAsHtml(tooltip)
};
if (!obj.$tooltip) obj.$tooltip = $tooltip;
else if (obj.$tooltip.raw !== tooltip) obj.$tooltip = $tooltip;
return obj.$tooltip;
}
};
Then in the html, I accessed it like this:
<input type="text" ng-model="$ctrl.myObj.C"
uib-tooltip-html="$ctrl.myObj.getTooltip().trusted">
I have a webgrid that gets populated on page load. In this grid I have an element that has a javascript event handled when it is clicked. Here I simply intend to sent the user to an external site. I also have this tied to a controller. Both are working for the first element. However, when it comes to anything after the first element in the list the javascript does not get called.
WebGrid:
#grid.GetHtml(tableStyle: "table table-striped table-bordered",
headerStyle: "thead-default",
columns: grid.Columns(
grid.Column("post_tran_a", Model.year_a, canSort: false, format: (item) =>
new HtmlString(Html.CheckBox("post_tran_a", (bool)item.post_tran_a.is_reviewed, new { disabled = "disabled" })
+ " " +
Html.ActionLink((string)item.post_tran_a.month, "PostTransmission", Model.controller, new { report_id = item.post_tran_a.report_id, link = item.post_tran_a.link }, new { #id = "external-link", data_url=Url.Action() })
))))
Javascript:
$("#external-link").click(function () {
var url = $("#external-link").attr("data-url");
alert(url);
});
If this approach won't work I'm open to alternative solutions.
Simplest way in your particular case might work like
$("table a").click(function () {
// you need here 'this' it is available by default
// and it points to the object on which click is called
var url = $(this).attr("data-url");
alert(url);
});
But above is too general. It will fail if you have tables having other links a where you do not want to fire the even so better approach is following
Id only works for one element. For a set of elements (e.g. multiple links). You need to use the class and access them by class as well.
I replaced your id with class and accessed it with that name as well.
grid.GetHtml(tableStyle: "table table-striped table-bordered",
headerStyle: "thead-default",
columns: grid.Columns(
grid.Column("post_tran_a", Model.year_a, canSort: false, format: (item) =>
new HtmlString(Html.CheckBox("post_tran_a",
(bool)item.post_tran_a.is_reviewed, new { disabled = "disabled" })
+ " " +
Html.ActionLink((string)item.post_tran_a.month, "PostTransmission",
Model.controller, new { report_id = item.post_tran_a.report_id, link = item.post_tran_a.link },
// See following carefully
new { #class="someuniquecalssname" data_url=Url.Action() })))))
Now the javascript will work fine
$(".someuniquecalssname").click(function () {
var url = $(this).attr("data-url");
alert(url);
});
If you are not willing to add class attribute then, Creating Unique Ids like ex-link1, ex-link2 could be possible in many cases. But they are useless for an event like above
You are using id to select the element, an id must be unique on the page. use either a class or unique ids when setting them in your code #id = "external-link--xxx"
You could also use a different selector in your jquery selector
$("#yourtableid a").click(function () {
var url = $("#external-link").attr("data-url");
alert(url);
});
Is there a way to revert/reset the edited row in the dojo-dgrid ?
I can see the grid.revert() which does clear the dirty items and calls the refresh method, which will refresh the whole grid. I don't want this whole grid-refresh.
Is it possible to reset/revert only that single edited row, upon clicking a Revert/Cancel Icon on the Actions-column (which will be the last column in the grid as mentioned here and here)
If you're wrapping your store with Observable, you can use notify() to update a single row.
For example, you can create the following code for onClick event of your Revert/Cancel button:
renderCell: function(object, data, cell){
var btnRevert = new Button({
label: "Revert",
// ...
onClick: function(evt){
var dirty = that.grid.dirty,
id = object.id;
if(dirty.hasOwnProperty(id)){
// remove dirty data
delete dirty[id];
// ..and notify the store to update
myStore.notify(object, object.id);
}
}
}, cell.appendChild(put("div")) );
return btnRevert;
}
Here is a jsfiddle with an example: revert example
I'm developing a web page using YUI ( YAHOO User Interface Library ) and Velocity, and I have set a datatable with some buttons in one of the columns. My problem is that when I sort any of the columns, the buttons are not rendered anymore, ie that are display as simple input fields. Here part of the (simplified) code:
#set($states=['valid','invalid','empty'])
var buttonsFormatter = function(elLiner, oRecord, oColumn, oData) {
elLiner.innerHTML = '';
#foreach($state in $states)
elLiner.innerHTML = elLiner.innerHTML+'<input id="${state}Button-'+oRecord.getData("id")+'"/>';
#end
};
var myColumnDefs = [
{key:"change",
label: "Actions",
formatter: buttonsFormatter,
sortable: true,
resizeable: true},
];
// Then create datasource, datatable, etc...
// $comment is the object I use to build the datasource
// So here I loop over records to instantiate the buttons
#foreach($comment in $comments_arraylist)
new YAHOO.widget.Button("validButton-"+$comment.getId(),
{label:'<img src="$content.getURI("images/gc.gif")"/>',
title:'Valid',
onclick:{ fn:makeRequest,obj:[$comment.getId(),"Valid"]}
}
);
new YAHOO.widget.Button("invalidButton-"+$comment.getId(),
{label:'<img src="$content.getURI("images/rc.gif")"/>',
title:'Invalid',
onclick:{ fn:makeRequest,obj:[$comment.getId(),"Invalid"]}
}
);
new YAHOO.widget.Button("emptyButton-"+$comment.getId(),
{label:'<img src="$content.getURI("images/close.jpg")"/>',
title:'Reset',
onclick:{ fn:makeRequest,obj:[$comment.getId(),"null"]}
}
);
#end
I have also tried to add the new YAHOO.widget.Button... declaration in the formatter function, but no more success. Does someone has an idea for me? I have to mention that except this issue on sorting, everything works fine.
Thanks
Alexis
Edit: Modification in the button formatter function this way doesn't help:
var buttonsFormatter = function(elLiner, oRecord, oColumn, oData) {
elLiner.innerHTML = '';
#foreach($state in $states)
elLiner.innerHTML = elLiner.innerHTML+'<input type="button" id="${state}Button-'+oRecord.getData("id")+'"/>';
#end
elLiner.innerHTML = elLiner.innerHTML+'<input type="button" id="testButton-'+oRecord.getData("id")+'"/>';
new YAHOO.widget.Button("testButton-"+oRecord.getData("id"),
{label:'<img src="$content.getURI("images/gc.gif")"/>',
title:'test',
onclick:{ fn:makeRequest,obj:[oRecord.getData("id"),"test"]}
}
);
};
The so created 'test' button is never rendered, just appear as a basic empty button input.
Your "buttonsFormatter" function is creating text input elements. I think you need to have a 'type="button"' attribute on the element you are creating there.
I think I understand why you are calling new YAHOO.widget.Button, but you should do that in the formatter function.