How to bind custom event handler from vue-touch-events to a custom component in Vue.js - vue.js

I'm currently implementing a classical Minesweeper game in Vue.js which is working fine so far https://github.com/franktopel/defuse, Demo http://connexo.de/defuse/.
Now I would like to add touch support because as of now, to mark a field as "contains a mine" it is required that you right-click the field. Right-clicking is obviously not available on touch devices, so I would like to add longtap support. I'm using native events click and click.right from the parent component for each field, so the field does not handle the events, but the parent component that instantiates these fields does.
I've found this https://github.com/jerrybendy/vue-touch-events and added it to my project, yet it seems I cannot use this on the component tag (see https://github.com/franktopel/defuse/blob/master/src/components/Defuse.vue):
<m-field
v-for="field in row"
:field="field"
:key="`${field.x},${field.y}`"
#click.native="open(field)"
#click.right.native.prevent="toggleBombMarker(field)"
v-touch:longtap="toggleBombMarker(field)"
></m-field>
because that marks all fields without any user interaction and the console keeps producing
You may have an infinite update loop in a component render function.
This is how my field objects (which I'm passing to the field component) are created:
let Field = function (x, y) {
this.x = x
this.y = y
this.isOpen = false
this.hasBomb = false
this.isMarked = false
this.numNeighbourBombs = null
}
module.exports = Field
I have also tried emitting a custom event from inside my field component, yet I don't know how I can pass the triggering field to the event handler from there.
Can anyone push me in the right direction?

According to the vue-touch-events docs, the v-touch directive doesn't work in the same way as v-on; v-touch must be given a function to execute, whereas the toggleBombMarker(field) expression probably returns undefined.
If you want to pass extra parameters to the v-touch handler, your handler must return a function like this:
methods: {
toggleBombMarker(field) {
return () => {
// your handler code here
}
}
}

Related

Quasar: Is there any way to get the value typed into a QSelect with use-input before it gets removed on blur?

Per the docs you can add the attribute use-input to a QSelect component to introduce filtering and things of that nature: https://quasar.dev/vue-components/select#Native-attributes-with-use-input.
However, if you type something into one of these fields and click outside of it, the text gets removed.
Is there any way to grab that text in Vue before it gets removed and do something with it?
Since v1.9.9 there is also a #input-value event described in the q-select api.
As the api says it's emitted when the value in the text input changes. The new value is passed as parameter.
In the examples there's a filter function, so there you can save it in a data variable:
methods: {
filterFn (val, update, abort) {
update(() => {
this.myDataVariable = val;
})
}
}

VueJs 2 - Force Checkbox DOM value to respect data value

I cannot for the life of me figure this out.
Fiddle at: https://jsfiddle.net/s8xvkt10/
I want
User clicks checkbox
Then based on separate conditions
Checkbox calculatedCheckedValue returns a data property /v-model of true/false
And the checked state reflects the calculatedCheckedValue
I can get:
The calculatedCheckedValue calculates and interpolates properly
But I fail at:
Having the :checked attribute render the calculatedCheckedValue properly in the DOM
e.g. If a false checkbox is clicked and the calculatedCheckedValue still returns false, the checkbox toggles onscreen between checked and unchecked
I’ve tried:
Using a v-model with a custom set that does the calculation and sets the local state which the custom get returns
Imitating a v-model using #change.prevent.stop="updateCalculatedValue" and :checked="calculatedValue"
Assuming the #change happens after the #click (which i think is wrong) and using #click.prevent.stop="updateCalculatedValue" and :checked="calculatedValue"
The model is working and rendering the calculated value as string in a DOM span,
but the checked value doesn't seem to respect the model value
Can someone please help me out?
I've solved the problem at: https://jsfiddle.net/pc7y2kwg/2/
As far as I can tell, the click event is triggered even by keyboard events (src)
And happens before the #change or #input events (src)
So you can click.prevent the event, but you need to either prevent it selectively or retrigger the event after the calculation.
//HTML Template
<input :checked="calculatedValue5" #click="correctAnswer" type="checkbox">
//VUE component
data() {
return {
calculatedValue5: false,
};
},
methods: {
correctAnswer(e){
const newDomValue = e.target.checked;
this.calculatedValue5 = this._calculateNewValue(newDomValue);
if(this.calculatedValue5 !== newDomValue){
e.preventDefault();
}
}
}
I think this is preventing checkbox value from updating before the data value, which seems to break the reactivity. I'm not sure the technical reason why, but the demo does work

Aurelia: Programmatically changing reference value doesn't change model

We have a situation where we utilize a component in multiple web pages (a note editor). This note editor takes a value from an input element on the page and places that value into a component where the user can modify it. The user types in the note editor and clicks Submit. The note editor component then passes the new value back to the input on the original page.
We are using the "ref" to pass the value back and forth. Everything works fine except that the model doesn't update when we set the value of the ref from the note editor component. We find we need to type once in order for it update the model. Here is a simple Gist to illustrate our example:
This is just a simple example of programmatically setting the ref's value.
Note how "The value of my input is" stays as "Hello" but the input field changes to "Value Changed!!!!" when you press the button.
https://gist.run/?id=fab025d6b99a93f9951b1a6e20efeb5e
A few things to note:
1) We'd like to use Aurelia's "ref" instead of an "id" or "name".
2) We've tried to pass the model instead of the "ref". We can get the value of the model successfully and put it in the note editor, but the model doesn't update when we pass it back.
UPDATE:
We have an answer (thanks!). Here is the code we tried to pass the model (so we wouldn't even need to use the ref). This failed for us.
Here is the View of the page.html
<TextArea value.bind="main.vAffectedClients" style="width:94%;" class="editfld" type="text" rows="6"></TextArea>
<input class="butn xxsBtn" type="button" value="..." click.trigger="OpenNoteDivAurelia(main.vAffectedClients)" />
Here is the View-Model of the page.js
import {NoteEditor} from './SmallDivs/note-editor';
...
#inject(NoteEditor, ...)
export class PageName {
constructor(NoteEditor, ...)
{
this.note = NoteEditor;
...
}
OpenNoteDivAurelia(myTargetFld)
{
this.note.targetFld = myTargetFld;
this.note.vHidTextArea.value = myTargetFld;
this.note.show();
}
}
This part opens our component (note-editor) and successfully places the targetFld value inot our TextArea in the component.
Here is the View-Model when placing the value BACK to page.js/page.html
CloseNote(populateFld)
{
if (populateFld)
{
//This is the line that doesn't seem to work
this.targetFld = vHidTextArea.value;
}
this.isVisible = false;
}
This last function "CloseNote" is the one that doesn't work. The model (which we believe is pointed at this.targetFld) does not get the value of the textarea. It does not error, it simply does not do anything.
The events that Aurelia attaches to be notified of an input changing don't fire when you programmatically set the value property on an input. It's simple enough to fire one of these events yourself, though.
export class App {
mymodel = {"test":"Hello"};
changeValByRef(myinput)
{
myinput.value = "Value Changed!!!!";
myinput.dispatchEvent(new Event('change'));
}
}
You actually don't need to pass the ref in to the function, anytime you use the ref attribute, a property is added to your VM w/the name you use in the ref attribute. So the above example could have been accomplished using this.myinput instead of the passed in object, since they're the same.
Here's a working example: https://gist.run/?id=7f96df1217ac2104de1b8595c4ae0447
I'd be interested to look at your code where you're having issues passing the model around. I could probably help you figure out what's going wrong so you don't need to use ref to accomplish something like this.

ReactTestUtils.Simulate can't trigger event bind by addEventListener?

Here is the example:
http://jsfiddle.net/hulufei/twr4thuh/7/
It just worked when bind onClick in virtual dom(like line 18), but If I comment line 18 and comment off line 8 to bind click with addEventListener, it failed.
So what's the problem?
TestUtils triggers events within react's synthetic event system, so the native event that addEventListener listens for is never going to be triggered. You will need to use the native click method on the element in your test:
var events = Events();
ReactTestUtils.renderIntoDocument(events);
events.refs.button.getDOMNode().click();
events.state.event.should.equal('click');
Additionally, you've misspelled clickHandler in your addEventListener definition.
jsfiddle
You can also simplify adding your event listener by reusing your prop definition:
componentDidMount: function () {
this.refs.button.getDOMNode().addEventListener('click', this.clickHandler);
},
Note:
Is there a reason why you want to use addEventListener instead of just passing an onClick attribute for your button? Unless there's a specific and good reason otherwise, i'd suggest doing things the react way when handling events for sanity :)
Edit
I originally mentioned that I did not know what TestUtils' SimulateNative.click did not trigger the event. I was wrong in thinking that it ever would since it would be simulating a native click event within the react even system. #thilo pointed me in the right direction :)
I had many problems while testing addEventListener, and I got the following conclusion.
You can create the events listener with pure javascript, jquery, but when running the tests with Jest I always had a problem.
The rendering of ReactTestUtils does not work directly with the document, and when we do:
For example, our events were added in the document, when rendering with ReactTestUtils it creates a div and renders it in the div, This way I could not get Simulate to trigger the call.
My first solution was to use jquery to create the listener and to test I did the render manually by appending the div in document.body, and triggered the events with the dispachEvent of javascript. But I thought the code was dirty, not the best way to work.
I made a sample code by adding the event and testing it with Jest, also have a test teaching to get all the listener that were created.
You can find the code here: https://github.com/LVCarnevalli/create-react-app/tree/master/src/components/datepicker
Component:
componentDidMount() {
ReactDOM.findDOMNode(this.datePicker.refs.input).addEventListener("change", (event) => {
const value = event.target.value;
this.handleChange(Moment(value).toISOString(), value);
});
}
Test:
it('change empty value date picker', () => {
const app = ReactTestUtils.renderIntoDocument(<Datepicker />);
const datePicker = ReactDOM.findDOMNode(app.datePicker.refs.input);
const value = "";
const event = new Event("change");
datePicker.value = value;
datePicker.dispatchEvent(event);
expect(app.state.formattedValue).toEqual(value);
});
Links:
window.addEventListener not triggered by simulated events: https://github.com/airbnb/enzyme/issues/426
Creating and triggering events: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

ComponentQuery for a parent with ExtJS4?

Is there a way to query "up"? I'm in a Component and want to register listeners to it's parents events with control(). This requires a Query which gets me the parent of my main view.
In ExtJS4, you can use 'up()' from an Ext Element.
The params are a string of the parent element you wish to find e.g:
var parentEl = Ext.get('childID').up('div.parentClass');
If you provide some details about the structure of your components/elements I can give a specific example which should fit.
EDIT: To show going 'up' from a component
var myComponent = // however you've got it
var theParentEl = myComponent.getEl().up('div.parentClass');
Usually up('PARENTCLASS') is enough for what you're trying to do. Here is what I do all over the code so elements generates event for the form they are in:
items: [
...
{ xtype: 'checkbox', listeners: {
change: function() { this.up('window').fireEvent('checkboxchanged'); }
}}
...
]
As I understand, you want to listen to events dispatched by a component's parent from the child component's controller control function specifically.
There is not a query selector like 'parent < component' which you can put in the child controller's control function to listen to parent events.
Normally I would just add the parent view to the child's controller, then you could listen to it's events. But I assume you are not doing this because you are trying to delegate to different controllers or something.
You could fire an event in the child component whenever that parent event occurs. Inside the parent controller you could do it like this:
var child = parent.down('child');
child.fireEvent('myOwnEventName', arg1, arg2, arg3, etc);
Then you would add a handler for 'myOwnEventName' in the child controller's control function to run the logic you wanted for it.
If the parent doesn't have a controller then you should just add the parent component as a view in the child's controller.
The Sencha help says "Member expressions from candidate Components may be tested. If the expression returns a truthy value, the candidate Component will be included in the query:" in the http://docs.sencha.com/ext-js/4-0/#!/api/Ext.ComponentQuery help.
Took me a while to realize I can do the following in my controller:
this.control({
'window{down("testcomp")}[down]': { beforedestroy: this.doNotCloseIfUnsaved }
});
Using the {} operation, we can call any arbitrary code. Really bad from an efficiency standpoint, but it got the job done. I had to add the [down] because it runs component queries from right to left, so we have to be sure down() exists before we try running it (on every component). Thus, I was able to attach an event to whatever window holds my component.
Of course, other functions can be used too, like child() instead of down() if you want to ensure it is the immediate child rather than just anywhere below.