The tutorial at http://www.asp.net/web-forms/tutorials/ajax-control-toolkit/getting-started/creating-a-custom-ajax-control-toolkit-control-extender-vb gives a nice example of a custom extender based on a textbox and a button. Basically the button remains disabled until at least one character is typed into the textbox. If the text is removed from the textbox the button is disabled again.
I am trying to modify this so that the extender is based on a textbox and panel. Again I want the panel to become visible when text is present in a textbox.
This is how I amended code...
Type.registerNamespace('CustomExtenders');
CustomExtenders.ShowHidePanelBehavior = function (element) {
CustomExtenders.ShowHidePanelBehavior.initializeBase(this, [element]);
this._targetPanelIDValue = null;
}
CustomExtenders.ShowHidePanelBehavior.prototype = {
initialize: function () {
CustomExtenders.ShowHidePanelBehavior.callBaseMethod(this, 'initialize');
// Initalization code
$addHandler(this.get_element(), 'keyup',
Function.createDelegate(this, this._onkeyup));
this._onkeyup();
},
dispose: function () {
// Cleanup code
CustomExtenders.ShowHidePanelBehavior.callBaseMethod(this, 'dispose');
},
// Property accessors
//
get_TargetPanelID: function () {
return this._targetPanelIDValue;
},
set_TargetPanelID: function (value) {
this._targetPanelIDValue = value;
},
_onkeyup: function () {
var e = $get(this._targetPanelIDValue);
if (e) {
var visibility = ("" == this.get_element().style.value);
e.visibility = 'visible';
}
}
}
CustomExtenders.ShowHidePanelBehavior.registerClass('CustomExtenders.ShowHidePanelBehavior', Sys.Extended.UI.BehaviorBase);
When run the panel will not appear. No errors are produced.
Where have I gone wrong...
Try this code:
_onkeyup: function () {
var panel = $get(this.get_TargetPanelID());
if (panel) {
var visibilityValue = ("" == this.get_element().value) ? "hidden" : "visible";
panel.style.visibility = visibilityValue;
}
}
Related
The scenario is this modal window:
The inputs 2, 3 and 4 with an .on('change', function () {}); makes an AJAX call to a specified controller, that update the recod values and reload the value 1.
So the right, but not functional way is to:
click the input 1 and set the value
focusout it by clicking outside the input
AJAX reload value 1 updated
click input 2 and set the value
focus out it by clicking outside the input
AJAX reload value 1 updated
The user click confirm that call another controller that make some checks and change the status of an object (from Draft to Confirmed)
The problem
If I try this way:
click the input 1 and set the value
focusout it by clicking outside the input
AJAX reload value 1 updated
click input 2 and set the value
Click confirm button that call another controller and trigger the input change
Now, with this way the problem occurs because the confirm method doesn't receive yet the update from last onchange trigger and the check is not correct.
Is there a way to manage multiple AJAX from different triggers like onchange and onclick?
Something like if the below onclick is triggered:
// Trigger for button confirm inside timesheet sheet modal
$(document).on('click', 'button.js_confirm_timesheet_sheet', function (ev) {
var $button = $(this);
var wizard_id = $button.data()['wizardId'];
var sheet_id = $button.data()['sheetId'];
var values = {
'wizard_id': wizard_id,
'sheet_id': sheet_id,
};
confirm_sheet_distribution_hours(values);
});
Check if the click come from an input focus out, if yes trigger the onchange first and after the onclick
Maybe this solution can be a bad way to do this.
Little, triggers recap:
The inputs have an onchange trigger that writes data to backend object with an AJAX call that recompute values and return the new one
The confirm button check if everything is ok with an AJAX call and change the backend object status
The other workaround maybe can be to declare an object that keeps track of each changed input boxes and clear it on each AJAX success return.
Something like:
var changedData = {};
function update_wizard_data_and_modal(values, $input_elem, event) {
changedData[key] = $input_elem;
ajax.jsonRpc("/my/controller/path", "call", values)
.then(function (new_modal_values) {
$input_elem.removeClass('input-value-error');
if (!jQuery.isEmptyObject(new_modal_values)) {
if (new_modal_values.error_msg) {
var $content = $(new_modal_values.error_msg);
$content.modal({
backdrop: 'static',
keyboard: false
});
$content.appendTo('body').modal();
// Show error class
$input_elem.val('00:00');
$input_elem.addClass('input-value-error');
}
// Update the header values with hours to be distribuited
$('#header-wizard-values').html(new_modal_values.header_values);
// Update the hours to get payed available
$('.js_hours_to_get_payed').html(new_modal_values.hours_get_payed_values);
// Clear the changedData object
for (var member in changedData) delete changedData[member];
}
});
}
function confirm_sheet_distribution_hours(values) {
if jQuery.isEmptyObject(changedData){
ajax.jsonRpc("/confirm/controller/path", "call", values)
.then(function (response) {
if ('error' in response) {
//response in this case is the modal error template
$(response.error).appendTo('body').modal();
} else {
// Close modal and refresh the grid for current period
$('#modal_timesheet_sheet_confirm').modal('hide');
var sheet_item_data = {
'year': response.year,
'month': response.month,
};
update_grid_and_bars_values(sheet_item_data);
}
});
} else {
// TODO: trigger the change for element inside object and confirm
}
}
$(document).on("change", "input.distribution-input", function (ev) {
var $input = $(this);
var sheet_id = $('input[name="sheet_id"]').val();
var wiz_line_id = Number($input.attr('id').match(/\d+/)[0]);
var row_wizard_data = $input.closest('div.row').data();
var leave_type_id = row_wizard_data['leaveTypeId'];
var wizard_id = row_wizard_data['wizardId'];
var values = {
'sheet_id': Number(sheet_id),
'wizard_id': wizard_id,
'wiz_line_id': wiz_line_id,
'leave_type_id': leave_type_id,
'input_value': $input.val(),
};
var is_good_formatted = check_string_time_format($input, {});
if (is_good_formatted) {
update_wizard_data_and_modal(values, $input, ev);
}
});
// Trigger for button confirm inside timesheet sheet modal
$(document).on('click', 'button.js_confirm_timesheet_sheet', function (ev) {
ev.preventDefault();
ev.stopPropagation();
var $button = $(this);
var wizard_id = $button.data()['wizardId'];
var sheet_id = $button.data()['sheetId'];
var values = {
'wizard_id': wizard_id,
'sheet_id': sheet_id,
};
confirm_sheet_distribution_hours(values);
});
As suggested by Taplar I used a similar approach.
Here the javascript that manages the "onchange" of a wizard in the Odoo Frontend.
// Variable used for the last input changed when user click the Confirm button
var canConfirm = true;
/* Variable used for keep trace of the number of retry inside method
* confirm_sheet_distribution_hours
* */
var nr_of_try = 0;
function update_wizard_data_and_modal(values, $input_elem, event) {
if (event.type !== 'input') {
ajax.jsonRpc("/controller/path/...", "call", values)
.then(function (new_modal_values) {
canConfirm = true;
$input_elem.removeClass('input-value-error');
if (!jQuery.isEmptyObject(new_modal_values)) {
if (new_modal_values.error_msg) {
var $content = $(new_modal_values.error_msg);
$content.modal({
backdrop: 'static',
keyboard: false
});
$content.appendTo('body').modal();
// Show error class
$input_elem.val('00:00');
$input_elem.addClass('input-value-error');
}
// Update the header values with hours to be distribuited
$('#header-wizard-values').html(new_modal_values.header_values);
// Update the hours to get payed available
$('.js_hours_to_get_payed').html(new_modal_values.hours_get_payed_values);
}
});
} else {
canConfirm = false;
}
}
function set_the_amount_on_wizard($input, values, event) {
if (event.type !== 'input') {
ajax.jsonRpc("/controller/path/...", "call", values)
.then(function (response) {
canConfirm = true;
if ('error' in response) {
//response in this case is the modal error template
$(response.error).appendTo('body').modal();
// Reset input value (backend reset the TransientModel value)
$input.val('00:00')
}
});
} else {
canConfirm = false;
}
}
function confirm_sheet_distribution_hours(values) {
if (canConfirm) {
ajax.jsonRpc("/controller/patH/...", "call", values)
.then(function (response) {
if ('error' in response) {
//response in this case is the modal error template
$(response.error).appendTo('body').modal();
} else {
// Close modal and refresh the grid for current period
$('#modal_timesheet_sheet_confirm').modal('hide');
var sheet_item_data = {
'year': response.year,
'month': response.month,
};
update_grid_and_bars_values(sheet_item_data);
}
});
} else {
/*Try six times to confirm the sheet (Until the onchange doesn't write
* new values the AJAX call doesn't set canConfirm as True
* */
if (nr_of_try <= 5) {
setTimeout(function () {
nr_of_try++;
confirm_sheet_distribution_hours(values);
}, 500);
}
}
}
//Trigger that monitorate hours distribution change
$(document).on("input change", "input.distribution-input", function (ev) {
var $input = $(this);
var sheet_id = $('input[name="sheet_id"]').val();
var wiz_line_id = Number($input.attr('id').match(/\d+/)[0]);
var row_wizard_data = $input.closest('div.row').data();
var leave_type_id = row_wizard_data['leaveTypeId'];
var wizard_id = row_wizard_data['wizardId'];
var values = {
'sheet_id': Number(sheet_id),
'wizard_id': wizard_id,
'wiz_line_id': wiz_line_id,
'leave_type_id': leave_type_id,
'input_value': $input.val(),
};
var is_good_formatted = check_string_time_format($input);
if (is_good_formatted) {
update_wizard_data_and_modal(values, $input, ev);
}
});
//Trigger that monitorate hours distribution change
$(document).on("input change", "input.payment-hour-input", function (ev) {
var $input = $(this);
var row_wizard_data = $input.closest('div.row').data();
var wizard_id = row_wizard_data['wizardId'];
var values = {
'wizard_id': wizard_id,
'input_value': $input.val(),
};
var is_good_formatted = check_string_time_format($input);
if (is_good_formatted) {
set_the_amount_on_wizard($input, values, ev);
}
});
// Trigger for button confirm inside timesheet sheet modal
$(document).on('click', 'button.js_confirm_timesheet_sheet', function (ev) {
var $button = $(this);
var wizard_id = $button.data()['wizardId'];
var sheet_id = $button.data()['sheetId'];
var values = {
'wizard_id': wizard_id,
'sheet_id': sheet_id,
};
// Variable used for retry sheet confirmation until canConfirm is not True
// Max repeat call is 6 times
nr_of_try = 0;
confirm_sheet_distribution_hours(values);
});
In simple words.
When the user is typing on inputs boxes the type input inside on.() set the variable canConfirm to false.
This prevents case when user changes values and click to the Confirm buttons immediately after.
In fact if the user changes some input box and immediately click "Confirm" the AJAX call starts only if the flag is true, if not the method calls it's self six times every 500 ms.
Let me know if there is some better way to doing that.
Thanks
PS: I will try a better approach with a DTO backend that clone data from model and manage updates like onchange cache.
Inspired by: https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Messenger.html
this is similar to a previous post where I wanted to sync my data source when the user changes rows in the grid (exactly as Access saves out a record)
In the post above am am shown how to do this when the user tabs into a new cell as follows...
function refreshFix1() {
kendo.ui.Grid.fn.refresh = (function (refresh) {
return function (e) {
this._refreshing = true;
refresh.call(this, e);
this._refreshing = false;
}
})(kendo.ui.Grid.fn.refresh);
kendo.ui.Grid.fn.current = (function (current) {
return function (element) {
// assuming element is td element, i.e. cell selection
if (!this._refreshing && element) {
this._lastFocusedCellIndex = $(element).index();
this._lastFocusedUid = $(element).closest("tr").data("uid");
// Added this For navigation mode
this._lastNavigationCell = this.tbody.find("tr:last td:last");
}
return current.call(this, element);
}
})(kendo.ui.Grid.fn.current);
kendo.ui.Grid.fn.refocusLastEditedCell = function () {
if (this._lastFocusedUid) {
var row = $(this.tbody).find("tr[data-uid='" + this._lastFocusedUid + "']");
var cell = $(row).children().eq(this._lastFocusedCellIndex);
this.editCell(cell);
}
};
The above gives us a function we can call (refocusLastEditedCell) after we sync the data source, and seems to work great.
I now want to do the same for when the grid is in Navigation mode. Following the above example, and the doco here , I added the following...
// Call this to go back to a cell in *navigation* mode
kendo.ui.Grid.fn.refocusLastNavigatedCell = function () {
var self = this;
if (this._lastNavigationCell) {
// try see if calling "async" using setTimeout will help
setTimeout (function(){
console.log("going back to navigation cell");
self.current(this._lastNavigationCell);
self.table.focus();
}, 10)
}
};
I then have the following code to call sync on the datasource...
vm.gridData.sync();
if (vm.editMode){
/ Go back to edit cell
grid.refocusLastEditedCell()
} else{
// Go back to navigation cell
grid.refocusLastNavigatedCell();
};
}
(full example here)
Unfortunately I do not seem to be going back to the same cell, it again just jumps to the top left cell.
Any ideas how to get it to work in this situation would be greatly appreciated.
Thanks in advance for any help!
You need to use the row uid and cell index as done in the original code; the <td> element you're trying to focus doesn't exist anymore once the grid gets rerendered. So you could do this:
kendo.ui.Grid.fn.current = (function (current) {
return function (element) {
// assuming element is td element, i.e. cell selection
if (!this._refreshing && element) {
this._lastFocusedCellIndex = $(element).index();
this._lastFocusedUid = $(element).closest("tr").data("uid");
// For navigation mode
var cell = current.call(this, element);
this._lastNavigationCellIndex = $(cell).index();
this._lastNavigationCellUid = $(cell).closest("tr").data("uid");
}
return current.call(this, element);
}
})(kendo.ui.Grid.fn.current);
and use it:
kendo.ui.Grid.fn.refocusLastNavigatedCell = function () {
if (this._lastNavigationCellUid) {
var row = $(this.tbody).find("tr[data-uid='" +
this._lastNavigationCellUid + "']");
var cell = $(row).children().eq(this._lastNavigationCellIndex);
this.current(cell);
}
};
You've got so many customizations now, you may want to extend the grid itself instead of replacing its methods one by one.
By the way, you could probably integrate all of this in the refresh method, so you don't have to explicitly call it:
kendo.ui.Grid.fn.refresh = (function (refresh) {
return function (e) {
this._refreshing = true;
refresh.call(this, e);
this._refreshing = false;
if (!this.options.editable) {
this.refocusLastNavigatedCell();
} else {
this.refocusLastEditedCell()
}
}
})(kendo.ui.Grid.fn.refresh);
I didn't understand why you want to refocus
this.tbody.find("tr:last td:last");
so I changed it for the (demo).
I created a side menu in css and want to use the the tranform: translateX() property to slide it on and off on the page. I'm using a checkbox to toggle the menu on and off. So I thought I can use javascript to make it function, but it's not working. Can you edit the transform property through javascript?
here's the code...
window.onload = function () {
var toggle_btn = document.getElementById('toggle_btn');
var slide_menu = document.getElementById('slide_menu');
var checked = function() {
toggle_btn.checked = true;
};
if (checked === true) {
slide_menu.style.transform = "translateX(0px)";
} else {
slide_menu.style.transform = "translateX(-200px)";
}
};
Try:
window.onload = function () {
var toggle_btn = document.getElementById('toggle_btn'),
slide_menu = document.getElementById('slide_menu');
slide_menu.style.transform = toggle_btn.checked=== true ? "none" : "translateX(-200px)";
};
BTW - you don't need javascript for this: http://jsfiddle.net/w5te8qqx/1/
I am revisiting this code I made a year ago with the help of another person. Unfortunately I don't have contact with them anymore to get more help. Basically It dynamically adds classs to the tb and b nodes of a document coming from namesToChange. Now what I am trying to do is append some text to the div with class dtxt node but still use this code below. I am using the code $('td.pn_adm_jeff').children('div.dtxt').append('zzz'); and it works but it constantly appends more than once as seen in the photo below. How do I go about making it add once and stop?
Photo
http://img6.imageshack.us/img6/5392/7c23ddb145954aefadb1b9f.png
Code
function customizefields(a) {
$('td b').each(function () {
name = $(this).text();
if (name.indexOf(" ") != -1) {
name = name.substring(0, name.indexOf(" "))
}
if (a[name]) {
this.className = a[name].class;
this.parentNode.className = a[name].img
}
})
$('td.pn_adm_jeff').children('div.dtxt').append('zzz');
}
var namesToChange = {
'Jeff' :{'class':'pn_adm','img':'pn_adm_jeff'}
};
setInterval(function () {
customizefields(namesToChange)
}, 1000);
Update
var needsUpdate = true;
function customizefields(a) {
$('td b').each(function () {
name = $(this).text();
if (name.indexOf(" ") != -1) {
name = name.substring(0, name.indexOf(" "));
}
if (a[name]) {
this.className = a[name].class;
this.parentNode.className = a[name].img;
}
});
if (needsUpdate) {
$('td.pn_adm_jeff').children('div.dtxt').append('testing');
needsUpdate = false;
}
}
var namesToChange = {
'jeff' :{'class':'pn_adm','img':'pn_adm_jeff'};
};
setTimeout(function () {
customizefields(namesToChange);
}, 1000);
use setTimeout rather than setInterval (interval is for repeating a timer task, timeout is a single timer task)
To prevent a certain task from occuring more than once in a repeated task, there is a simple fix.
// global variable
var needsUpdate = true;
// now in the timer task
if (needsUpdate) {
$('td.pn_adm_jeff').children('div.dtxt').append('zzz');
needsUpdate = false;
}
Does that work for you?
Define a global variable to hold the input flag
var appended = false;
function appendthestring() {
if(!appended) $('td.pn_adm_jeff').children('div.dtxt').append('zzz');
appended = true;
}
I have some jQuery plugin that changes some elements, i need some event or jQuery plugin that trigger an event when some text input value changed.
I've downloaded jquery.textchange plugin, it is a good plugin but doesn't detect changes via external source.
#MSS -- Alright, this is a kludge but it works:
When I call boxWatcher() I set the value to 3,000 but you'd need to do it much more often, like maybe 100 or 300.
http://jsfiddle.net/N9zBA/8/
var theOldContent = $('#theID').val().trim();
var theNewContent = "";
function boxWatcher(milSecondsBetweenChecks) {
var theLoop = setInterval(function() {
theNewContent = $('#theID').val().trim();
if (theOldContent == theNewContent) {
return; //no change
}
clearInterval(theLoop);//stop looping
handleContentChange();
}, milSecondsBetweenChecks);
};
function handleContentChange() {
alert('content has changed');
//restart boxWatcher
theOldContent = theNewContent;//reset theOldContent
boxWatcher(3000);//3000 is about 3 seconds
}
function buttonClick() {
$('#theID').value = 'asd;lfikjasd;fkj';
}
$(document).ready(function() {
boxWatcher(3000);
})
try to set the old value into a global variable then fire onkeypress event on your text input and compare between old and new values of it. some thing like that
var oldvlaue = $('#myInput').val();
$('#myInput').keyup(function(){
if(oldvlaue!=$('#myInput').val().trim())
{
alert('text has been changed');
}
});
you test this example here
Edit
try to add an EventListner to your text input, I don't know more about it but you can check this Post it may help
Thanks to #Darin because of his/her solution I've marked as the answer, but i have made some small jQuery plugin to achieve the same work named 'txtChgMon'.
(function ($) {
$.fn.txtChgMon = function (func) {
var res = this.each(function () {
txts[0] = { t: this, f: func, oldT: $(this).val(), newT: '' };
});
if (!watchStarted) {
boxWatcher(200);
}
return res;
};
})(jQuery);
var txts = [];
var watchStarted = false;
function boxWatcher(milSecondsBetweenChecks) {
watchStarted = true;
var theLoop = setInterval(function () {
for (var i = 0; i < txts.length; i++) {
txts[i].newT = $(txts[i].t).val();
if (txts[i].newT == txts[i].oldT) {
return; //no change
}
clearInterval(theLoop); //stop looping
txts[i].f(txts[i], txts[i].oldT, txts[i].newT);
txts[i].oldT = $(txts[i].t).val();
boxWatcher(milSecondsBetweenChecks);
return;
}
}, milSecondsBetweenChecks);
}