Reputation: 502
I have an element with a child element that acts as a button. When I press 'Enter' in the parent element's input box, I want to trigger the child's _runNavigation method. What is the best way to create a custom trigger where the parent fires an event to the child element?
I have tried created an EventListener in my child element:
<...>
<button type="button" class="btn btn-success" on-click="_runNavigation">{{result}}</button
<...>
Polymer({
is: 'search-button',
properties: {
searchQuery: {
type: String,
}
},
ready : function (){
document.addEventListener('fire', this._runNavigation);
},
navigate: function(url) {
var win = window.open(url, '_blank');
if (win != null) {
win.focus();
}
},
_runNavigation: function() {
var text = this.searchQuery;
this.navigate("https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=" + text);
},
});
And firing the event when enter is pressed when the text box is in focus:
<...>
<input id="input" type="text" value="{{searchQuery::input}}" on-keydown="checkForEnter" on-change="_inputChanged" class="form-control" placeholder="Search">
<...>
checkForEnter: function(e) {
// check if 'enter' was pressed
if (e.keyCode === 13) {
// enter pressed!
console.log("ENTER!");
this.fire('fire', {searchQuery: this.searchQuery});
}
}
While this will fire an event that is picked up by the child element, 'this.navigate' will not run because 'this' is now the document. I have tried changing the event listener to this.addEventListener('fire', this._runNavigation); to add it to the element itself, but then the element does not detect the trigger from the parent element.
Upvotes: 2
Views: 2846
Reputation: 138276
If you had no other choice but to use document.addEventListener
from within your Polymer element, you'd have to set the context of this._runNavigation
with bind()
:
ready: function() {
document.addEventListener('fire', this._runNavigation.bind(this)); // avoid!
}
While this would work in your example, it listens to the fire
event on the entire document, so if any other element outside the heirarchy of your form fired that event, it would trigger your element's handler, which might be undesirable. For example:
<x-form></x-form> <!-- listens to all 'fire' events -->
<script>
setTimeout(function() {
// unrelated 'fire'
document.dispatchEvent(new Event('fire'));
}, 1000);
</script>
As you might've guessed, Polymer provides the API to fire an event on a child element...
To dispatch an event to a child, you'd set a couple options when calling fire()
.
fire(type, [detail], [options])
. Fires a custom event. Theoptions
object can contain the following properties:
node
. Node to fire the event on (defaults to this).
bubbles
. Whether the event should bubble. Defaults to true.
cancelable
. Whether the event can be canceled with preventDefault. Defaults to false.
In your case, the bubbles
and node
options would be useful:
this.fire('fire', { searchQuery: this.searchQuery }, { bubbles:false, node: this.$.searchButton });
Then, in your search-button
element, you'd use:
this.addEventListener('fire', this._runNavigation); // bind() not necessary here
Note in this demo that the fire
event does not bubble up to the document (no warning logged in event handler).
Upvotes: 4