[Edit]:
I ended up just using Vuex and it works fine with this method.
I am having trouble with catching an event.
If I look in the vue dev tools, the event is being emitted but the parent function is not being activated. I have this problem any time I try to use emit throughout this whole project. I am trying to avoid Vuex because this is a Vue/Inertia.js/Laravel stack with all login data and vars being passed from controllers and Laravel session, so I feel like adding Vuex would be redundant.
I'm sorry I can't post the full code because it is for work and I signed an NDA, but if more parts are needed I can provide snippets.
PARENT (<Collection>)
<CollectionFooter v-if="itemsSelected > 0"
:itemsSelected="itemsSelected" #modifySelected="modifyItemsSelected(operation)" />
data(){
return {
itemsSelected: 0
}
},
methods: {
modifyItemsSelected(operation){
if(operation === "add") {
this.itemsSelected += 1;
} else {
this.itemsSelected -= 1;
}
console.log(this.itemsSelected)
}
},
CHILD (<CollectionItemCard>)
<button v-if="active" color="sm-white" #click="changeState">added</button>
<button v-else color="sm-yellow" #click="changeState">add</button>
methods:{
changeState(){
this.active = !this.active;
let operation;
if(this.active) {
operation = "add";
} else {
operation = "remove";
}
this.$emit('modifySelected', operation);
}
},
vue dev tools screen
As zcoop98 mentioned above,
moving #modifySelected="modifyItemsSelected(operation)" to the <CollectionItemCard> component was the key.
I had to have the listener on the same component which emitted. Not just any parent element.
TL;DR – The problem is the way you've written your inline handler.
Change modifyItemsSelected(operation) to just "modifyItemsSelected" or "modifyItemsSelected($event)", and you should be good to go. Detailed explanation as to why is below.
Vue Event Handler Rundown
There's two ways to handle events using v-on in Vue: Inline Handlers, and Method Handlers. It's really easy to miss the difference, but it's important.
Inline Handlers
Inline handlers are probably what you're most familiar with, since it's exactly what you're using in the example.
This handler type is exactly what it sounds like: you pass a bit of JavaScript inline to v-on, and Vue runs this code when the event is handled.
For example:
<button #click="alert('Hi!')">Click Me!</button>
When the click event is emitted by this button, the code alert('Hi!') is run, just as it's written.
This is the key to your problem– it runs just as its written, references and all.
In your event handler:
<CollectionFooter
...
#modifySelected="modifyItemsSelected(operation)"
/>
You've told Vue to run modifyItemsSelected(operation) when modifySelected is handled. The problem is that you've given the inline function an argument which doesn't exist.
Instead of reading in the payload from the event as a variable called operation, Vue interprets this syntax as a call to modifyItemsSelected with some existing variable named operation.
Vue goes looking for operation in the template context, and then in your component context, and when it doesn't find it, it throws an error, killing execution of the handler.
Method Handlers
Method handlers are also just what they sound like: instead of passing an inline snippet of JavaScript to v-on, you pass it a defined method name, only, which Vue binds the handler to directly. A crucial difference between this type and inline is that you don't include arguments in the call– Vue handles the argument passing implicitly.
This means that your provided example simply becomes:
<CollectionFooter
...
#modifySelected="modifyItemsSelected"
/>
The payload will be passed as an argument (or arguments, you can pass as many as you'd like into $emit()) to modifyItemsSelected for it to use.
A Note on $event
A final thing worth mentioning is some special Vue syntax: $event. This is a placeholder you can use in inline handlers to signify the event payload.
Using #event="myHandler($event)" will pass the event payload as the first argument to myHandler. This is especially useful when you need some crucial data only accessible from the template in your event handler (eg. in a v-for loop), because it allows you to pass both the event payload and custom data into your handler function at once. (Eg. #event="myHandler($event, element.index)")
This means there's another way to format your event handler and have it work (purely a matter of preference in this case):
<CollectionFooter
...
#modifySelected="modifyItemsSelected($event)"
/>
Snippet Example
Finally, here's an example to illustrate this information dump.
Three buttons, all calling the same method a different way. The given method simply prints a message and the (stringified) event to console.
Clicking the button with your current handler throws an error into the console, without printing anything (the development version of Vue is nice enough to give a warning and still print, but the production version isn't so kind). The other two print successfully.
new Vue({
el: '#app',
methods: {
foo(e) {
console.log('Handled!');
console.log('Event:', JSON.stringify(e));
},
},
});
.as-console-wrapper { max-height: 85px !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<button #click="foo(bar)">Inline Handler:</button><span><code> #click="foo(bar)"</code> (Your example code)</span>
<hr>
<button #click="foo">Binded Handler:</button><span><code> #click="foo"</code> (Alternative #1)</span>
<br><br>
<button #click="foo($event)">Inline Handler w/ $event:</button><span><code> #click="foo($event)"</code> (Alternative #2)</span>
</div>
Related
Comming from a c# background, I just want to create an event in a certain point of my code, soas to be dispatched elsewere, meaning that if in some part of the code there has been a subscription, this delegate function is called.
So I tried to do:
function myFunction() {
console.log("delegated call achieved!");
}
const myEvent = new Event('onMyConditionIsMet', myFunction, false);
//at this point the program the subscription takes place
function whatever1() {
//...not meaningfull code
myEvent.addEventListener('onMyConditionIsMet');
//myEvent += myFunction; c# way subscription in case it makes sense
}
//at this point in the program, event subscription is checked and
//delegate func run in case there has been a subscription
function whatever2() {
//...not meaningfull code
myEvent?.invoke(); // ?.invoke(); would be the c# way to do it.
}
All the examples I found are related to DOM events, but my case would be for events I create myself, think these are called synthetic events.
Another assumption I make in this question is that there would be no arguments in the delegate call function, so, just to be clear with the naming, it would be a delegate with no arguments. Just pointing this because in c# events are just delegate funcs with no arguments, so a specific type of delegate. Not sure if this works the same way in Javscript.
What would be the approach to do this? (Meaning creating a simple event instance, subscribing, and executing the delegated code if there is any subscription)?
I think the functionality you are looking for can be best obtained by using OOP/Classes.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#prototype_methods
Edit: see this also - "event" is deprecated, what should be used instead?
I want to know when a DOM element generated by Ractive is ready. In my case, I want to use jquery to attach an autocomplete function onto the element. Ideally it would go something like this:
Template:
{{#list}}
<input type="text" proxy-load="attach-typeahead">
{{/list}}
Javascript:
ractive.on("attach-typeahead", function(event){
$(event.node).typeahead(...);
})
But the event never fires even though I remeber seeing proxy-load mentioned somewhere in the documentation. What's the proper way to do what I'm trying to do? Thanks.
Codler's answer is spot on - transitions can be used to attach behaviour to nodes (and detach it, with outro).
As of the latest (0.3.8) version, there's another method, which behaves similarly but is slightly more streamlined for this purpose: decorators.
The documentation hasn't been written yet (my bad), but you can see a typeahead decorator here. A decorator is simply a function that gets called as soon as a node is added to the DOM, and which returns an object with a teardown() method that gets called as soon as the node is removed from the DOM.
You can make a decorator globally available like so:
Ractive.decorators.foo = function ( node ) {
// do some setup work with the node here...
return {
teardown: function () {
// do any necessary cleanup here
}
};
};
Or you can specify per-instance decorators, as in the fiddle.
Another decorator example here, this time a sortable list.
The proxy-events are mentioned here in the documentation of ractive. Your example doesn't work because the input element does not have a native load event.
All the ractive functions have a complete function callback that fires when the rendering has completed. Maybe you can use that.
You can use the intro attribute. It is a transition in ractive. When the DOM are created, intro will be called.
You can find more info here https://github.com/RactiveJS/Ractive/wiki/Transitions
In my plugin I need to be able to fire event(s) once the plugin was loaded.
I don't want to use the built in mechanism (adding it in the object params) since I need to be able to control the parameters which are sent along with the event firing.
The problem is that when I try to fire the event in onPluginReady it just doesn't fire.. While debugging I noticed that the m_proxies is empty (in JSAPIImpl::FireEvent), but if I try the same code for firing the event in the onMouseDown method then it works well.
This is my createJSAPI code:
FB::JSAPIPtr plugin::createJSAPI()
{
this->jsApi = JSApiPtr(new pluginAPI(FB::ptr_cast<plugin>(shared_from_this()), m_host));
return this->jsApi;
}
And this is the onPluginReady code:
void plugin::onPluginReady()
{
this->getRootJSAPI();
this->jsApi->fireMyEvent(this->myId);
}
and the event isn't fired, though this does:
bool plugin::onMouseDown(FB::MouseDownEvent *evt, FB::PluginWindow *)
{
this->jsApi->fireMyEvent(this->myId);
return false;
}
Why is that?
Thanks.
onPluginReady is likely to be called before your onLoad callback from the param tag gets called; that means your event handlers aren't attached yet. That's the reason that FireBreath provides the onload param callback -- it gives you a place to attach event handlers and find out that things are loaded.
Edit to clarify from comments:
The callback will be provided with a single parameter which contains a reference to your root JSAPI object. Note that in this case it is not the object or embed tag, just the JSAPI object, so you can use any methods or properties from there.
I've noticed that the call to the createJSAPI method in my plugin is only called after I somehow try to interact with the actual DOM element.
Is there a way to make it happen before any javascript interaction is happening?
In the documentation for getRootJSAPI it states:
It is not recommended to call this from the constructor or before
setHost is called, as many JSAPI objects need the BrowserHost and a
weak_ptr to the Plugin class to function correctly
So when is it appropriate to call this method? in onPluginReady or onWindowAttached?
Thanks.
Edit
This is my createJSAPI code:
FB::JSAPIPtr plugin::createJSAPI()
{
this->jsApi = JSApiPtr(new pluginAPI(FB::ptr_cast<plugin>(shared_from_this()), m_host));
return this->jsApi;
}
And this is the onPluginReady code:
void plugin::onPluginReady()
{
this->getRootJSAPI();
this->jsApi->fireMyEvent(this->myId);
}
and the event isn't fired, though this does:
bool plugin::onMouseDown(FB::MouseDownEvent *evt, FB::PluginWindow *)
{
this->jsApi->fireMyEvent(this->myId);
return false;
}
Why is that?
As for the build in onload mechanism, I need my own, since I need to pass some parameters to that fired event.
Thanks.
You can call this method during or after onPluginReady -- that's one of the main purposes of the function.
EDIT:
To answer your further question, onPluginReady is likely to be called before your onLoad callback from the param tag gets called; that means your event handlers aren't attached yet. That's the reason that FireBreath provides the onload param callback -- it gives you a place to attach event handlers and find out that things are loaded.
Orbeon version: Orbeon Forms 3.8.0.201005270113
I have the following code in a Javascript file. This code is executed, but it seems like the model in the XBL is not found.
ORBEON.xforms.Document.dispatchEvent("model-name", "event-name");
Here is the model in the XBL. There are several models in the XBL. I don't see any message, so it seems as though the model isn't found. I don't see any errors in the logs.
<xforms:model id="model-name" xxforms:external-events="event-name">
<xforms:action ev:event="event-name">
<xforms:message>Test</xforms:message>
</xforms:action>
</xforms:model>
Does anyone know if there is some trick to getting a dispatch to work from Javascript to XBL?
Thanks very much!
UPDATED:
Another thing that could be the problem (maybe?) is that calling the javascript from the XBL using instance(this) isn't working. I wonder if the instance of the class isn't tied to a component instance, therefore it can't find the model?
Here's the call to the javascript from the xbl that doesn't invoke the init method:
<xxforms:script>YAHOO.xbl.fr.myTest.instance(this).init();</xxforms:script>
Here's the call that does invoke the init() method:
<xxforms:script>YAHOO.xbl.fr.myTest.prototype.init();</xxforms:script>
Here's the javascript:
YAHOO.namespace("xbl.fr");
YAHOO.xbl.fr.myTest = function() {};
ORBEON.xforms.XBL.declareClass(YAHOO.xbl.fr.myTest, "xbl-fr-myTest");
YAHOO.xbl.fr.myTest.prototype = {
},
init: function() {
alert('test');
},
valueChanged: function() {
},
};
AFAIK you can't address the XBL-internal model directly from outside, because of its strong encapsulation.
Instead, you'll have to dispatch the event to the xbl component node. For example, if you want an instance of the fr:currency XBL to handle a certain event, you'll have to dispatch the event to that fr:currency element that's part of your XForm.
Inside the XBL, you can define xbl:handlers to act upon that event, triggering some JavaScript action or something else.