Say we have a yui3 form:
YUI().use("gallery-form", function (Y) {
var checkbox = new Y.CheckboxField({
name : "myCheckbox",
value : "check",
label : "Test Checkbox"
});
var f = new Y.Form({
boundingBox: '#form',
action : 'test.php',
method : 'post',
children : [
checkbox ,
{name : 'submitBtn', type : 'SubmitButton', value : 'Submit'}
]
});
f.render();
});
I.e. form has a lot of checkboxes; I would like every checkboxes on this form to onchange="fn(this)".
Here's a small example of what i need.
NB. checkbox in the second line shouldn't be modify. I'm looking for something like:
form.all('input type=checkbox').on('change', fn(checkbox));
_
// where fn is:
function fn(el){ console.log(el.checked); }
This one is kind of criptic. I think the expected way is to do form.on('*:change', fn) and filter the event propagation, but I can't find a way to avoid duplicate calls to the callback.
So you can resort to the Form's change UI event and do:
form.on('change', function (e) {
var checkbox = e.domEvent.target;
console.log(checkbox.get('checked'));
});
Related
I have both rowclick and rowdblclick handlers for a tabulator table, I'd like to debounce the rowclick handler so I don't get two rowclick's then a rowdblclick firing off whenever I dblclick on a row, is there a built-in method to do this? I'm aware that I can use rxjs and create a subject and debounce, but I would like to use a built in debounce if it exists.
I have a very similar issue - global row/cellClick also fire when column based cellClick fire.
My work around is to place e.stopImmediatePropagation() into the column based cellClick function. This also still allows the rowDblClick event to pass upwards/downwards etc (bubbling?). However, this is probably the reverse of what you need, unless you remove the need for a double click by putting in an event column?
var editIcon = function(cell, formatterParams, onRendered){ //plain text value
return "<i class='fa fa-edit'></i>";
};
var table = new Tabulator("#table", {
columns:[
{title:"Name", field:"name"}, //etc
{formatter:editIcon, headerSort:false, width:40, align:"center", cellClick:function(e, cell){
// do whatever
e.stopImmediatePropagation(); //prevent table wide rowClick() from also triggering
},
],
rowClick:function(e, row){
//all rows/cells will inherit this function, however the cell level cellClick function will take the first bite of the event before bubbling up to rowClick
},
});
Don't know if this helps, probably some more elegant way, but sort of works for me.
This is a standard JavaScript click event behaviour rather than anything specific to Tabulator
Any time you bind a click and double click handler the click handler will be triggered first.
I would suggest that you use a set timeout to detect if the double click has happened, you then make the double click event clear the timeout preventing the click action from happening:
var debounce = null; //hold debounce timer
var table = new Tabulator("#table", {
rowClick:function(e, row){
debounce = setTimeout(function(){
//do something
}, 100)
},
rowDblClick:function(e, row){
clearTimeout(debounce);
},
});
What I ended up doing in the end is using an EventEmitter and doing a .emit and passing the id from the row that was clicked on. Then in my pipe for my subscription to the EventEmitter I did a .distinct, eliminating the second click on the same row when double clicking.
export class XYZComponent implements AfterViewInit {
ngAfterViewInit(): void {
this.tabX = new Tabulator('#xyz', {
columns: [
// 1
{
title: 'clickable column',
field: 'X'
headerSort: false,
// visible: false,
width: '5rem',
cellDblClick: this.itemDblClick.bind(this),
cellClick: this.itemClick.bind(this),
},
//...
]
}
);
}
private itemClick(e, item) {
// both cells and rows have a getData function
this.onItemSelect(item.getData());
}
private itemDblClick(e, item) {
// both cells and rows have a getData function
this.onItemEdit(item.getData());
}
}
export class ABCComponent implements AfterViewInit {
ngAfterViewInit(): void {
this.selectItemSubject
.pipe(
takeWhile(() => this.active)
, distinctUntilChanged() // this will eliminate the second click
)
.subscribe(item => {
// load additional data for item
});
this.editItemSubject.pipe(
takeWhile(() => this.active)
)
.subscribe((item) => {
// do whatever to edit the item
});
}
}
Relative newbie; forgive me if my etiquette and form here aren't great. I'm open to feedback.
I have used create-react-native-app to create an application using PouchDB (which I believe ultimately uses AsyncStorage) to store a list of "items" (basically).
Within a TabNavigator (main app) I have a StackNavigator ("List screen") for the relevant portion of the app. It looks to the DB and queries for the items and then I .map() over each returned record to generate custom ListView-like components dynamically. If there are no records, it alternately displays a prompt telling the user so. In either case, there is an "Add Item" TouchableOpacity that takes them to a screen where they an add a new item (for which they are taken to an "Add" screen).
When navigating back from the "Add" screen I'm using a pattern discussed quite a bit here on SO in which I've passed a "refresh" function as a navigation param. Once the user uses a button on the "Add" screen to "save" the changes, it then does a db.post() and adds them item, runs the "refresh" function on the "List screen" and then navigates back like so:
<TouchableOpacity
style={styles.myButton}
onPress={() => {
if (this.state.itemBrand == '') {
Alert.alert(
'Missing Information',
'Please be sure to select a Brand',
[
{text: 'OK', onPress: () =>
console.log('OK pressed on AddItemScreen')},
],
{ cancelable: false }
)
} else {
this.createItem();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsScreen');
}
}
}
>
And all of this works fine. The "refresh" function (passed as onGoBack param) works fine... for this screen. The database is called with the query, the new entry is found and the components for the item renders up like a charm.
Each of the rendered ListItem-like components on the "List screen" contains a react-native-slideout with an "Edit" option. An onPress for these will send the user to an "Item Details" screen, and the selected item's _id from PouchDB is passed as a prop to the "Item Details" screen where loadItem() runs in componentDidMount and does a db.get(id) in the database module. Additional details are shown from a list of "events" property for that _id (which are objects, in an array) which render out into another bunch of ListItem-like components.
The problem arises when either choose to "Add" an event to the list for the item... or Delete it (using another function via [another] slideout for these items. There is a similar backward navigation, called in the same form as above after either of the two functions is called from the "Add Event" screen, this being the "Add" example:
async createEvent() {
var eventData = {
eventName: this.state.eventName.trim(),
eventSponsor: this.state.eventSponsor.trim(),
eventDate: this.state.eventDate,
eventJudge: this.state.eventJudge.trim(),
eventStandings: this.state.eventStandings.trim(),
eventPointsEarned: parseInt(this.state.eventPointsEarned.trim()),
};
var key = this.key;
var rev = this.rev;
await db.createEvent(key, rev, eventData);
}
which calls my "db_ops" module function:
exports.createEvent = function (id, rev, eventData) {
console.log('You called db.createEvent()');
db.get(id)
.then(function(doc) {
var arrWork = doc.events; //assign array of events to working variable
console.log('arrWork is first assigned: ' + arrWork);
arrWork.push(eventData);
console.log('then, arrWork was pushed and became: ' + arrWork);
var arrEvents = arrWork.sort((a,b)=>{
var dateA = new Date(a.eventDate), dateB = new Date(b.eventDate);
return b.eventDate - a.eventDate;
})
doc.events = arrEvents;
return db.put(doc);
})
.then((response) => {
console.log("db.createEvent() response was:\n" +
JSON.stringify(response));
})
.catch(function(err){
console.log("Error in db.createEvent():\n" + err);
});
}
After which the "Add Event" screen's button fires the above in similar sequence to the first, just before navigating back:
this.createEvent();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsDetails');
The "refresh" function looks like so (also called in componentDidMount):
loadItem() {
console.log('Someone called loadItem() with this.itemID of ' + this.itemID);
var id = this.itemID;
let totalWon = 0;
db.loadItem(id)
.then((item) => {
console.log('[LOAD ITEM] got back data of:\n' + JSON.stringify(item));
this.setState({objItem: item, events: item.events});
if (this.state.events.length != 0) { this.setState({itemLoaded: true});
this.state.events.map(function(event) {
totalWon += parseInt(event.eventPointsEarned);
console.log('totalWon is ' + totalWon + ' with ' +
event.eventPointsEarned + ' having been added.');
});
};
this.setState({totalWon: totalWon});
})
.catch((err) => {
console.log('db.loadItem() error: ' + err);
this.setState({itemLoaded: false});
});
}
I'm at a loss for why the List Screen refreshes when I add an item... but not when I'm doing other async db operations with PouchDB in what I think is similar fashion to modify the object containing the "event" information and then heading back to the Item Details screen.
Am I screwing up with Promise chain someplace? Neglecting behavior of the StackNavigator when navigating deeper?
The only other difference being that I'm manipulating the array in the db function in the non-working case, whereas the others I'm merely creating/posting or deleting/removing the record, etc. before going back to update state on the prior screen.
Edit to add, as per comments, going back to "List screen" and the opening "Item Details" does pull the database data and correctly shows that the update was made.
Further checking I've done also revealed that the console.log in createEvent() to print the response to the db call isn't logging until after some of the other dynamic rendering methods are getting called on the "Item Details" screen. So it seems as though the prior screen is doing the get() that loadItem() calls before the Promise chain in createEvent() is resolving. Whether the larger issue is due to state management is still unclear -- though it would make sense in some respects -- to me as this could be happening regardless of whether I've called my onGoBack() function.
Edit/bump: I’ve tried to put async/await to use in various places in both the db_ops module on the db.get() and the component-side loadItem() which calls it. There’s something in the timing of these that just doesn’t jive and I am just totally stuck here. Aside from trying out redux (which I think is overkill in this particular case), any ideas?
There is nothing to do with PDB or navigation, it's about how you manage outer changes in your depending (already mounted in Navigator since they are in history - it's important to understand - so componentDidMount isn't enough) components. If you don't use global state redux-alike management (as I do) the only way to let know depending component that it should update is passing corresponding props and checking if they were changed.
Like so:
//root.js
refreshEvents = ()=> { //pass it to DeleteView via screenProps
this.setState({time2refreshEvents: +new Date()}) //pass time2refreshEvents to EventList via screenProps
}
//DeleteView.js
//delete button...
onPress={db.deleteThing(thingID).then(()=> this.props.screenProps.refreshEvents())}
//EventList.js
...
constructor(props) {
super(props);
this.state = {
events: [],
noEvents: false,
ready: false,
time2refreshEvents: this.props.screenProps.time2refreshEvents,
}
}
static getDerivedStateFromProps(nextProps, currentState) {
if (nextProps.screenProps.time2refreshEvents !== currentState.time2refreshEvents ) {
return {time2refreshEvents : nextProps.screenProps.time2refreshEvents }
} else {
return null
}
}
componentDidMount() {
this._getEvents()
}
componentDidUpdate(prevProps, prevState) {
if (this.state.time2refreshEvents !== prevState.time2refreshEvents) {
this._getEvents()
}
}
_getEvents = ()=> {
//do stuff querying db and updating your list with actual data
}
In my chrome extension I'm adding two context items "Get link" and "Get Image". The main difference being when setting them both up they have the "context" of link and image respectively. But when right clicking on an image that is acting as a link you get the option of both:
when either of those are clicked the data that comes into the listener seems to be identical, I need to be able to differentiate the two to know if the context is that of an image or a link to handle them differently. Here is my code:
chrome.runtime.onInstalled.addListener(function() {
var context = "image";
var title = "Copy Image";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.runtime.onInstalled.addListener(function() {
var context = "link";
var title = "Copy link";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(onClickHandler);
function onClickHandler(info, tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "imageAdded", subject: info.srcUrl}, function(response) {
});
If you want know which menu item was clicked, you can get the id value of the clicked context menu item in the menuItemId property of the object passed into the onClicked handler:
function onClickHandler(info, tab) {
console.log(info.menuItemId);
//...
}
Take a look at Parameter of onClicked callback, you could differentiate the image/link via mediaType
One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements.
I got an MenuItem like this:
new sap.ui.unified.MenuItem({
text: "ID",
submenu: new sap.ui.unified.Menu({
items: [this.oIdMenuButton = new sap.ui.unified.MenuItem({
text: "IDs anzeigen/ausblenden",
icon: "resources/images/check.png",
select: this._onShowHideIdRequest
})]
})
})
And an EventListener like this:
_onShowHideIdRequest: function (oControlEvent) {
}
This code is inside a component. The problem that now occurs is this: I can't access the component as itself. Because when I call this. I access the MenuItem, which fired the Event. How can I access methods outside of this EventListener method? I know that there is sap.ui.getCore().byId(id) but normally I don't know the id of my component. And also I can't store the id, because, I can't access the id inside the EventHandler.
Modify call to _onShowHideIdRequest with this line of code, it will give access to Component.
this._onShowHideIdRequest.bind(this);
You can pass a wide amount of arguments to select [1]
// a function that will be called
function
// same as above
[ function ]
// the function will be called, value of this is the context-object
[ function, context ]
// the function will be called, the value of this is the context-object, the data will be passed to the function as argument
[ data, function, object ]
example:
var ctx = {foo: "bar"};
new MenuItem({
select: [
function () {
console.log(this.foo);
}, ctx
]
});
should result in a logged "bar"
I'm loading several Selectize select inputs in one page, like this:
var selectizeInput = [];
$('.select-photo-id').each(function (i) {
var selectedValue = $(this).val();
selectizeInput[i + 1] = $(this).selectize({
'maxOptions': 100,
'items': [selectedValue],
'onType': function (input) {
$.post("admin/ajax/search_photos_for_postcards",
{input: input},
function (data) {
$(this).addOption(data);
$(this).refreshOptions();
}, 'json');
}
});
});
The event onType makes a function call that returns a list of new options which I want to make available right away in the Selectize input. Is there any way to call the Selectize instance from there? As you can see from the code, I tried accessing it with $(this), but it fails. I also tried with $(this).selectize, but it's the same. Which is the correct way to do it?
I managed to fix it:
'onType': function (input) {
var $this = $(this);
$.post("admin/ajax/search_photos_for_postcards",
{input: input},
function (data) {
$this[0].addOption(data);
$this[0].refreshOptions();
}, 'json');
}
You probably want to use the load event provided by the Selectize.js API as seen in the demos. Scroll until you find "Remote Source — Github" and then click "Show Code" underneath it.