I'm trying to initialize my ember text field view to prefill it with the query string. But whenever i add an init function to my view all other defined events stop triggering.
How do i get my events to keep working while using initialization ?
App.SearchBarView = Ember.TextField.extend({
throttle_instance: _.debounce(function(){
var value = this.get('value');
this.update();
}, 1000),
/**
* Initialize the textbox with a set value
*/
init: function(){
var query = "testquery";
if(query && query.length > 0){
this.set('value', query[0]);
this.get('controller').set('query', query[0]);
this.update();
}
},
insertNewline: function() {
this.update();
},
/**
* Handle the keyup event and throttle the amount of requests
* (send after 1 second of not typing)
*/
keyUp: function(evt) {
this.get('controller').set('query', this.get('value'));
this.throttle_instance();
},
/**
* Update the pages results with the query
*/
update: function(){
var value = this.get('value');
this.get('controller').filterByQuery(value);
}
});
You need to call this._super() inside your init method to maintain the view's default behaviour:
...
init: function() {
this._super();
}
...
Hope it helps.
Related
I want to use a simple p:idleMonitor for some logic.
But in my case, i don't want to restart the idlemonitors counter for a mousemove (only for click etc.)
Is this possible with javascript or with something else? The idlemonitor does not have any attribute for that configuration.
Add this code to a JS file that loads after PrimeFaces to MonkeyPatch your IdleMonitor to only stop on keydown and click events.
The key is this line events: "keydown click" // define active events
This is based on PrimeFaces 8.
if (PrimeFaces.widget.IdleMonitor) {
PrimeFaces.widget.IdleMonitor.prototype.init = function(cfg) {
PrimeFaces.widget.BaseWidget.prototype.init.call(this, cfg);
var $this = this;
$(document).on("idle.idleTimer" + this.cfg.id, function() {
if ($this.cfg.onidle) {
$this.cfg.onidle.call($this);
}
$this.callBehavior('idle');
})
.on("active.idleTimer" + this.cfg.id, function() {
if ($this.cfg.onactive) {
$this.cfg.onactive.call($this);
}
$this.callBehavior('active');
});
var opts = {
idle: false, // indicates if the user is idle
timeout: this.cfg.timeout, // the amount of time (ms) before the user is considered idle
events: "keydown click" // define active events
};
$.idleTimer(opts, document, this.cfg.id);
if (cfg.multiWindowSupport) {
var globalLastActiveKey = $this.cfg.contextPath + '_idleMonitor_lastActive' + this.cfg.id;
// always reset with current time on init
localStorage.setItem(globalLastActiveKey, $(document).data('idleTimerObj' + this.cfg.id).lastActive);
$this.timer = setInterval(function() {
var idleTimerObj = $(document).data('idleTimerObj' + $this.cfg.id);
var globalLastActive = parseInt(localStorage.getItem(globalLastActiveKey));
var localLastActive = idleTimerObj.lastActive;
// reset local state
if (globalLastActive > localLastActive) {
// pause timer
$.idleTimer('pause', document, $this.cfg.id);
// overwrite real state
idleTimerObj.idle = false;
idleTimerObj.olddate = globalLastActive;
idleTimerObj.lastActive = globalLastActive;
idleTimerObj.remaining = $this.cfg.timeout;
// resume timer
$.idleTimer('resume', document, $this.cfg.id);
}
// update global state
else if (localLastActive > globalLastActive) {
localStorage.setItem(globalLastActiveKey, localLastActive);
}
}, 2000);
}
}
};
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
Im using django-el-pagination to do lazy loading of entries.
When I click on an entry and then use the browser back button, all of the lazy loading is gone, I tried to add window.history.pushState() but then I only get the current page i.e.?page=4 when I use the browser back button, and all of the entries on top is not loaded.
Is there any way to implement a correct history so that the user is back at the same place when they use the browser back button?
$.endlessPaginate({
paginateOnScroll: true,
paginateOnScrollMargin: 400,
paginateOnScrollChunkSize: 2,
onCompleted: function(context, fragment) {
window.history.pushState(null, null, context.url);
}
});
Edit 1
Here is the JavaScript for the .endlessPaginate function:
'use strict';
(function ($) {
// Fix JS String.trim() function is unavailable in IE<9 #45
if (typeof(String.prototype.trim) === "undefined") {
String.prototype.trim = function() {
return String(this).replace(/^\s+|\s+$/g, '');
};
}
$.fn.endlessPaginate = function(options) {
var defaults = {
// Twitter-style pagination container selector.
containerSelector: '.endless_container',
// Twitter-style pagination loading selector.
loadingSelector: '.endless_loading',
// Twitter-style pagination link selector.
moreSelector: 'a.endless_more',
// Digg-style pagination page template selector.
pageSelector: '.endless_page_template',
// Digg-style pagination link selector.
pagesSelector: 'a.endless_page_link',
// Callback called when the user clicks to get another page.
onClick: function() {},
// Callback called when the new page is correctly displayed.
onCompleted: function() {},
// Set this to true to use the paginate-on-scroll feature.
paginateOnScroll: false,
// If paginate-on-scroll is on, this margin will be used.
paginateOnScrollMargin : 1,
// If paginate-on-scroll is on, it is possible to define chunks.
paginateOnScrollChunkSize: 0
},
settings = $.extend(defaults, options);
var getContext = function(link) {
return {
key: link.attr('rel').split(' ')[0],
url: link.attr('href')
};
};
return this.each(function() {
var element = $(this),
loadedPages = 1;
// Twitter-style pagination.
element.on('click', settings.moreSelector, function() {
var link = $(this),
html_link = link.get(0),
container = link.closest(settings.containerSelector),
loading = container.find(settings.loadingSelector);
// Avoid multiple Ajax calls.
if (loading.is(':visible')) {
return false;
}
link.hide();
loading.show();
var context = getContext(link);
// Fire onClick callback.
if (settings.onClick.apply(html_link, [context]) !== false) {
var data = 'querystring_key=' + context.key;
// Send the Ajax request.
$.get(context.url, data, function(fragment) {
container.before(fragment);
container.remove();
// Increase the number of loaded pages.
loadedPages += 1;
// Fire onCompleted callback.
settings.onCompleted.apply(
html_link, [context, fragment.trim()]);
});
}
return false;
});
// On scroll pagination.
if (settings.paginateOnScroll) {
var win = $(window),
doc = $(document);
doc.scroll(function(){
if (doc.height() - win.height() -
win.scrollTop() <= settings.paginateOnScrollMargin) {
// Do not paginate on scroll if chunks are used and
// the current chunk is complete.
var chunckSize = settings.paginateOnScrollChunkSize;
if (!chunckSize || loadedPages % chunckSize) {
element.find(settings.moreSelector).click();
} else {
element.find(settings.moreSelector).addClass('endless_chunk_complete');
}
}
});
}
// Digg-style pagination.
element.on('click', settings.pagesSelector, function() {
var link = $(this),
html_link = link.get(0),
context = getContext(link);
// Fire onClick callback.
if (settings.onClick.apply(html_link, [context]) !== false) {
var page_template = link.closest(settings.pageSelector),
data = 'querystring_key=' + context.key;
// Send the Ajax request.
page_template.load(context.url, data, function(fragment) {
// Fire onCompleted callback.
settings.onCompleted.apply(
html_link, [context, fragment.trim()]);
});
}
return false;
});
});
};
$.endlessPaginate = function(options) {
return $('body').endlessPaginate(options);
};
})(jQuery);
short answer: no. The whole point of 'endless pagination' is to not reload a (new) page, therefore there is no history.
Using polymer 1.1, I don't want to make a custom element or template for paper-header-panel. How can I assign a handler on the scroll event?
I tried:
bob = Polymer.dom(document).querySelector('paper-header-panel');
bob.scrollHandler = function() {
console.log('ssssssss');
}
To no success.
https://github.com/PolymerElements/paper-header-panel/blob/master/paper-header-panel.html#L186
From documentation
Polymer({
is: 'paper-header-panel',
/**
* Fired when the content has been scrolled. `event.detail.target` returns
* the scrollable element which you can use to access scroll info such as
* `scrollTop`.
*
* <paper-header-panel on-content-scroll="{{scrollHandler}}">
* ...
* </paper-header-panel>
*
*
* scrollHandler: function(event) {
* var scroller = event.detail.target;
* console.log(scroller.scrollTop);
* }
*
* #event content-scroll
*/
and
ready: function() {
this.scrollHandler = this._scroll.bind(this);
this._addListener();
If I understand your question correctly, you want to subscribe the event in js like this -
ready: function () {
this.$.panel.addEventListener("content-scroll", function () {
console.log('ssssssss');
});
}
But I failed to see why you can't do it like shown in the document.
bob = Polymer.dom(document).querySelector('paper-header-panel');
bob.addEventListener("content-scroll", function () {
console.log('ssssssss');
}
I've added a custom button to TinyMCE which brings up a bespoke link-picker. When the user selects some text and clicks the button the dialog appears and when they've picked the url I'm using execCommand('insertHTML', false, "<a href... etc">) on the selection.
This works fine - now, when the link has already been created, and the user wants to edit it, they click the link button again (when the cursor is inside the linked text as normal), but then here is the situation - I don't know how to access the already created link and it's attributes to then load up and populate the dialogue again!
Have search TinyMCE site, Stack, Google in general. Hoping for (and also slightly dreading) a simple answer - but if not, a complex one will be fine!!
If anybody knows the answer or can point me to it, I'd be extremely grateful. Thanks in advance,
Rob
EDIT - bits of my code to explain need
In the TinyMCE init:
setup: function (ed) {
ed.addButton("link", {
title: "Link",
onclick: function (evt) {
Intranet.TextEditor._loadUrlDialog(jQueryTextAreaObject, evt);
}
});
}
The function which is called above:
_loadUrlDialog: function (jQueryTextAreaObject, clickEvent) {
var mce = $(jQueryTextAreaObject).tinymce();
var isSelected = mce.selection.getContent().length != 0 ? true : false;
if (isSelected) {
Intranet.UrlDialog.Fn.LoadDialog("", true, "", function (url, target, title) {
var theTarget = target == false ? "_self" : "_blank";
var link = "" + mce.selection.getContent() + "";
mce.execCommand('insertHTML', false, link); // creates new link
});
}
else {
/// THIS IS THE MISSING BIT!
};
}
You have two ways of achieving this:
When pushing the button you check for the selection parent node. If the node is a link then you can get the link information from the html a-element. To populate your dialogue you will know what to do.
The other option is to add a contextmenu on rightclick, which will provide the necessary functionalities.
Here is the plugin code for this (keep in mind that you will have to add "customcontextmenu" to the plugin-setting of your tinymce).
/**
* editor_plugin_src.js
*
* Plugin for contextmenus.
*/
(function() {
var Event = tinymce.dom.Event, each = tinymce.each, DOM = tinymce.DOM;
tinymce.PluginManager.requireLangPack('customcontextmenu');
/**
* This plugin a context menu to TinyMCE editor instances.
*
* #class tinymce.plugins.customcontextmenu
*/
tinymce.create('tinymce.plugins.customcontextmenu', {
/**
* Initializes the plugin, this will be executed after the plugin has been created.
* This call is done before the editor instance has finished it's initialization so use the onInit event
* of the editor instance to intercept that event.
*
* #method init
* #param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
* #param {string} url Absolute URL to where the plugin is located.
*/
init : function(ed) {
var t = this, lastRng;
t.editor = ed;
// Registiere commands
ed.addCommand('edit_inline_element', function() {
//edit_inline_element(ed, ed.right_clicked_node); //ed.right_clicked_node is the actually clicked node
//call or do whatever you need here
});
// delete link
ed.addCommand('delete_inline_element', function() {
$(ed.right_clicked_node).replaceWith($(ed.right_clicked_node).html());
});
// assign the right clicked node (it is the evt.target)
ed.onClick.add(function(ed, evt) {
if (evt.button == 2) ed.right_clicked_node = evt.target;
});
/**
* This event gets fired when the context menu is shown.
*
* #event onContextMenu
* #param {tinymce.plugins.ContextMenu} sender Plugin instance sending the event.
* #param {tinymce.ui.DropMenu} menu Drop down menu to fill with more items if needed.
*/
t.onContextMenu = new tinymce.util.Dispatcher(this);
ed.onContextMenu.add(function(ed, e) {
if (!e.ctrlKey) {
// Restore the last selection since it was removed
if (lastRng)
ed.selection.setRng(lastRng);
var menu = t._getMenu(ed);
if ((typeof menu).toLowerCase() == 'object')
{
menu.showMenu(e.clientX, e.clientY);
Event.add(ed.getDoc(), 'click', function(e) {
hide(ed, e);
});
Event.cancel(e);
}
// sonst Standardmenu anzeigen
}
});
ed.onRemove.add(function() {
if (t._menu)
t._menu.removeAll();
});
function hide(ed, e) {
lastRng = null;
// Since the contextmenu event moves
// the selection we need to store it away
if (e && e.button == 2) {
lastRng = ed.selection.getRng();
return;
}
if (t._menu) {
t._menu.removeAll();
t._menu.destroy();
Event.remove(ed.getDoc(), 'click', hide);
}
};
ed.onMouseDown.add(hide);
ed.onKeyDown.add(hide);
},
_getMenu: function(ed){
var t = this, m = t._menu, se = ed.selection, col = se.isCollapsed(), el = se.getNode() || ed.getBody(), am, p1, p2;
if (m) {
m.removeAll();
m.destroy();
}
p1 = DOM.getPos(ed.getContentAreaContainer());
p2 = DOM.getPos(ed.getContainer());
m = ed.controlManager.createDropMenu('contextmenu', {
offset_x : p1.x + ed.getParam('contextmenu_offset_x', 0),
offset_y : p1.y + ed.getParam('contextmenu_offset_y', 0),
constrain : 1
});
t._menu = m;
if ((typeof ed.right_clicked_node) !== "undefined" && ed.right_clicked_node.nodeName.toLowerCase() == 'a' )
{
m.add({
title: $(ed.right_clicked_node).attr('title'),
});
m.addSeparator();
m.add({
title: 'Edit link',
icon: 'edit_inline_element',
cmd: 'edit_link'
});
m.add({
title: 'Delete link',
icon: 'delete_inline_element',
cmd: 'delete_link'
});
t.onContextMenu.dispatch(t, m, el, col);
return m;
}
else {
// kein Menu anzeigen
return 0;
}
}
});
// Register plugin
tinymce.PluginManager.add('customcontextmenu', tinymce.plugins.customcontextmenu);
})();