Skip to content

Commit

Permalink
Add DOM listeners before attaching the element (#5195)
Browse files Browse the repository at this point in the history
Fixes #5152
  • Loading branch information
Legioth authored and Denis Anisimov committed Mar 7, 2019
1 parent d2b3c90 commit e2cc245
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,9 @@ private native void bindPolymerModelProperties(StateNode node,
private native void hookUpPolymerElement(StateNode node, Element element)
/*-{
var self = this;
var originalPropertiesChanged = element._propertiesChanged;
if (originalPropertiesChanged) {
element._propertiesChanged = function (currentProps, changedProps, oldProps) {
$entry(function () {
Expand All @@ -295,16 +295,16 @@ private native void hookUpPolymerElement(StateNode node, Element element)
originalPropertiesChanged.apply(this, arguments);
};
}
var tree = node.@com.vaadin.client.flow.StateNode::getTree()();
var originalReady = element.ready;
element.ready = function (){
originalReady.apply(this, arguments);
@com.vaadin.client.PolymerUtils::fireReadyEvent(*)(element);
// The _propertiesChanged method which is replaced above for the element
// doesn't do anything for items in dom-repeat.
// Instead it's called with some meaningful info for the <code>dom-repeat</code> element.
Expand All @@ -313,7 +313,7 @@ private native void hookUpPolymerElement(StateNode node, Element element)
// which changes this method for any dom-repeat instance.
var replaceDomRepeatPropertyChange = function(){
var domRepeat = element.root.querySelector('dom-repeat');
if ( domRepeat ){
// If the <code>dom-repeat</code> element is in the DOM then
// this method should not be executed anymore. The logic below will replace
Expand All @@ -327,12 +327,12 @@ private native void hookUpPolymerElement(StateNode node, Element element)
// if dom-repeat is found => replace _propertiesChanged method in the prototype and mark it as replaced.
if ( !domRepeat.constructor.prototype.$propChangedModified){
domRepeat.constructor.prototype.$propChangedModified = true;
var changed = domRepeat.constructor.prototype._propertiesChanged;
domRepeat.constructor.prototype._propertiesChanged = function(currentProps, changedProps, oldProps){
changed.apply(this, arguments);
var props = Object.getOwnPropertyNames(changedProps);
var items = "items.";
for(i=0; i<props.length; i++){
Expand All @@ -352,7 +352,7 @@ private native void hookUpPolymerElement(StateNode node, Element element)
if( currentPropsItem && currentPropsItem.nodeId ){
var nodeId = currentPropsItem.nodeId;
var value = currentPropsItem[propertyName];
// this is an attempt to find the template element
// which is not available as a context in the protype method
var host = this.__dataHost;
Expand All @@ -363,7 +363,7 @@ private native void hookUpPolymerElement(StateNode node, Element element)
while( !host.localName || host.__dataHost ){
host = host.__dataHost;
}
$entry(function () {
@SimpleElementBindingStrategy::handleListItemPropertyChange(*)(nodeId, host, propertyName, value, tree);
})();
Expand All @@ -374,7 +374,7 @@ private native void hookUpPolymerElement(StateNode node, Element element)
};
}
};
// dom-repeat doesn't have to be in DOM even if template has it
// such situation happens if there is dom-if e.g. which evaluates to <code>false</code> initially.
// in this case dom-repeat is not yet in the DOM tree until dom-if becomes <code>true</code>
Expand All @@ -389,7 +389,7 @@ private native void hookUpPolymerElement(StateNode node, Element element)
element.addEventListener('dom-change',replaceDomRepeatPropertyChange);
}
}
}-*/;

private static void handleListItemPropertyChange(double nodeId,
Expand Down Expand Up @@ -1153,16 +1153,21 @@ private void remove(JsArray<EventRemover> listeners, BindingContext context,

private EventRemover bindDomEventListeners(BindingContext context) {
NodeMap elementListeners = getDomEventListenerMap(context.node);
elementListeners.forEachProperty((property,
name) -> bindEventHandlerProperty(property, context));
elementListeners.forEachProperty((property, name) -> {
Computation computation = bindEventHandlerProperty(property,
context);

// Run eagerly to add initial listeners before element is attached
computation.recompute();
});

return elementListeners.addPropertyAddListener(
event -> bindEventHandlerProperty(event.getProperty(),
context));
}

private void bindEventHandlerProperty(MapProperty eventHandlerProperty,
BindingContext context) {
private Computation bindEventHandlerProperty(
MapProperty eventHandlerProperty, BindingContext context) {
String name = eventHandlerProperty.getName();
assert !context.listenerBindings.has(name);

Expand All @@ -1181,6 +1186,7 @@ private void bindEventHandlerProperty(MapProperty eventHandlerProperty,

context.listenerBindings.set(name, computation);

return computation;
}

private void removeEventHandler(String eventType, BindingContext context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2019 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.uitest.ui;

import com.vaadin.flow.component.dependency.HtmlImport;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.uitest.servlet.ViewTestLayout;

@Route(value = "com.vaadin.flow.uitest.ui.DomListenerOnAttachView", layout = ViewTestLayout.class)
@HtmlImport("frontend://com/vaadin/flow/uitest/ui/DomListenerOnAttach.html")
public class DomListenerOnAttachView extends AbstractDivView {
public DomListenerOnAttachView() {
Div status = new Div();
status.setText("Waiting for event");
status.setId("status");

Element element = new Element("event-on-attach");
element.addEventListener("attach", event -> {
status.setText("Event received");
});

getElement().appendChild(element, status.getElement());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!--
~ Copyright 2000-2018 Vaadin Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
~ use this file except in compliance with the License. You may obtain a copy of
~ the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
~ License for the specific language governing permissions and limitations under
~ the License.
-->
<!-- Using an absolute URL is a bad practice, but the nesting depth here makes it incovenient traverse up with ../.. -->
<link rel="import" href="/frontend/bower_components/polymer/polymer-element.html">

<dom-module id="event-on-attach">
<template>
Fires an event when attached
</template>
<script>
class EventOnAttach extends Polymer.Element {
static get is() { return 'event-on-attach' }

connectedCallback() {
super.connectedCallback();

this.dispatchEvent(new CustomEvent('attach'));
}
}
customElements.define(EventOnAttach.is, EventOnAttach);
</script>
</dom-module>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2018 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.uitest.ui;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;

import com.vaadin.flow.testutil.ChromeBrowserTest;

public class DomListenerOnAttachIT extends ChromeBrowserTest {

@Test
public void filtering() {
open();

String status = findElement(By.id("status")).getText();
Assert.assertEquals("Event received", status);
}
}

0 comments on commit e2cc245

Please sign in to comment.