Polymer blocking keyboard input? - polymer

I inherited an Adobe CEP extension at work. Trying to wrap my head around an issue that makes it so absolutely no input from keyboard works on text inputs. To elaborate, absolutely no keyboard input works in Polymer's text inputs. The input get's focused, but if I type anything in them I get the mac error alert sound. The only key that I was able to make work was "tab". Anything else does not work. It's built using Polymer. At first I was unsure what's causing the issue, and since I inherited this project I was confused where to start. After about a day of debugging, I believe it's related to Polymer somehow. The reason for this is, if I remove the Polymer HTML element that renders it, and just put an input there, the input works. It only seems to block input inside the <template> ... </template>. I've looked all over the internet for any clues on what could be causing Polymer to block this input, there's no errors in console or anything, and I've come up short handed.
Does anyone have any insight on this?

I'm facing the same problem. Actually, it is not related to polymer, but to the webcomponents polyfill. If you try the following source code inside an Adobe CEP extension, you will see that you can click inside both the elements, select any text, but you are not able to edit it.
<html>
<head>
<script>
// Force all polyfills on
if (window.customElements) window.customElements.forcePolyfill = true;
ShadyDOM = {
force: true
};
ShadyCSS = {
shimcssproperties: true
};
</script>
<script src="node_modules/#webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
</head>
<body>
<template id="x-foo-from-template">
<input value="from template">
</template>
<script>
let tmpl = document.querySelector('#x-foo-from-template');
customElements.define('x-foo-from-template', class extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({
mode: 'open'
});
shadowRoot.appendChild(tmpl.content.cloneNode(true));
}
});
customElements.define('x-foo-from-dynamic', class extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({
mode: 'open'
});
var inputEl = document.createElement('input');
inputEl.value = "from created element";
shadowRoot.appendChild(inputEl);
}
});
</script>
<x-foo-from-template></x-foo-from-template>
<x-foo-from-dynamic></x-foo-from-dynamic>
</body>
</html>

Faced with the same issue, we finally found documented that Adobe will hand over all keypresses to the host application unless it can determine that an input or dropdown element has focus. I expect this is done using a simple check on document.activeElement. When the Shadow DOM is involved, Adobe would have to do something like
let target = document.activeElement;
while (target.shadowRoot && target.shadowRoot.activeElement) {
target = target.shadowRoot.activeElement;
}
in order to find the underlying <input> element.
Since this is currently not working, we needed to use registerKeyEventsInterest to explicitly have all keypresses be processed by our code.
var csInterface = new CSInterface();
var keyEvents = [];
// All the keyCodes you need, with the modifiers used
keyEvents.push({ keyCode: 0 });
keyEvents.push({ keyCode: 0, metaKey: true });
// ...
csInterface.registerKeyEventsInterest(JSON.stringify(keyEvents));
We actually went ahead and looped 0..255 and registered for all modifiers. With the exception of keyboard based copy-paste, we now have full functionality with our webcomponents (mostly PolymerElement/LitElement based).
https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_8.x/Documentation/CEP%208.0%20HTML%20Extension%20Cookbook.md#register-an-interest-in-specific-key-events

Related

How can I remove or hide an object on the model tree panel in Forge Viewer?

I need to hide (make it go away completely) from the model tree panel in Viewer.
I already tried overriding methods from the Viewer (some other stuff is done that way), but the Tree-related methods and objects are not accessible for extending. It also seems too dangerous to mess with instanceTree data, like removing the dbId from the nodes list.
I'm running on the latest Viewer code (6.5.3), and writing pure javascript extensions.
For example, I tried overriding this function, which is used internally to determine if a node should or not be displayed. It doesn't work, neither does overriding the same function on the ModelStructureTreeDelegate:
Autodesk.Viewing.UI.TreeDelegate.prototype.shouldCreateTreeNode = function (dbId)
{
// original code on the viewer.js is:
// return true;
let itGo = true;
// _objectsHiddenInTree is populated with dbIds of objects to be hidden right after initializing the viewer
_objectsHiddenInTree.forEach(x => {
if (x == dbId){
itGo = false;
}
});
// return false; doesn't work either
return itGo;
};
Is there a way to do this from the Viewer side? I mean, to remove an item from the model tree?
If it's more viable, removing the object from the scene altogether is also a valid option. But I can't remove it from the model before sending to model derivative, it has to be done when opening the Viewer, or before opening the Tree Model panel.
Personally the easiest way would be to access node element via viewer.modelstructure and use styling to hide the node:
<style>
.yourHiddenNodeClass{display:none!important}
</style>
...
<script>
let modelStructureControl = viewer.modelstructure;
modelStructureControl.createUI(); //initialize the panel if it hasn't
let treeViewControl = modelStructureControl.tree;
let modelDelegate = treeViewControl.getDelegate(model.id);
treeViewControl.addClass(modelDelegate, dbid, "yourHiddenNodeClass", false) //hide a node - last boolean to toggle recursiveness
...
treeViewControl.removeClass(modelDeleagate, dbid, "yourHiddenNodeClass", false) //remove your custom class
</script>
And to hide a node completely:
model.visibilityManager.setNodeOff(dbid, true) // true=hide, false=show
Bryan's answer gave me an idea that seems to work for now:
Every element on the tree panel has an atribute 'lmv-nodeid', with the dbId of the object. So I looked for it, and added the 'hidden' attribute to the div:
document.querySelectorAll('[lmv-nodeid="' + objectDbId + '"]')[0].hidden = true;
His answer is still better, though, because there is no guarantee that the attribute will remain on newer versions of the Viewer, whereas the Viewer classes and methods are more stable and future-proof.

Data Bindings across template tags

I'm wondering, is there a possibility to have databindings "out of" a template? Say I have a <template/>-Tag somewhere which I put into the slot of a different component - that component stamps it to its context. Then I want to bind data from the root element to the <template/>-Tag. Also, event bindings (on-x-changed) don't work, because you can't assign a function which is defined in the hosting component. Any ideas?
Example:
... host
{{boundData}}
<binding-component>
<template>
{{boundData}}
</template>
</binding-component>
I don't see changes when I observe boundData in the hosting component. Is there a way to get around this? Or is firing a custom event my only chance?
If you are looking for binding a property outside of polymer something like from index.html you may bind value with element. an example ; index.html
<dom-bind>
<template>
<binding-component bound-data="{{boundData}}"></binding-component>
</template>
</dom-bind>
<script>
// set a value a string, Number or Object etc.
// Optionally wrap this code into a listener ie;
// window.addEventListener('load', e=> { ...below code ... })
var boundData= document.querySelector('dom-bind');
boundData = {} //
</script>
Now in your binding-component element has a property as boundData
hope its helps or provide more code to understand better.
I've made it work the way dom-if does it, too. Like in dom-if (reference), I'm creating a Templatize-instance which then uses forwardHostProp to handle the "inside"-properties
this.__ctor = Templatize.templatize(template, this, {
mutableData: true,
forwardHostProp(prop, value) {
// handling item updates, item being the only property
// from within the binding component
// everything else is automatically bound by templatize
this.set(prop, value);
this.update(this.item);
},
});
this.__instance = new this.__ctor();
this.root.appendChild(this.__instance.root);
This all happens in connectedCallback.
Because the Templatize-instance is passed this, it's bound to the current context as well.
Good luck!

Message boxes in Polymer applications

I am working on my first bigger Polymer application and currently have around 30 components. Most of the components need to be able to display (modal) message boxes. For this I implemented a message box component wrapping paper-dialog (similar to other message box components available).
What I don't like is that in every component which wants to display message boxes I need to define an element
<my-message-box id="message-box"></my-message-box>
and then call it like this
this.$["message-box"].information("Something happened...");
This works but my gut feeling is that a message box should be more like a global service, a singleton maybe. In C# f.e. there exists a static method on the MessageBox class.
Is the above mechanism really the recommended way to do it or are there better solutions to it?
My current approach is to create error-dialog and add it as a sibling to my main-app in index.html:
<body>
<main-app></main-app>
<error-dialog></error-dialog>
<noscript>
Please enable JavaScript to view this website.
</noscript>
</body>
error-dialog's ready() method adds a custom event:
ready() {
super.ready();
this.addEventListener('o_error', e => this._errorListener(e));
}
_errorListener(e) {
this.o_error = e.detail;
this.$.errorDlog.open();
}
Now I can open error-dialog from anywhere with
let msg = ...
const dlog = document.querySelector('error-dialog');
dlog.dispatchEvent(new CustomEvent('o_error', {detail: msg, bubbles: true, composed: true}));

After Input goes Invalid in HTML5 set error message and prevent default error message from kendo in a grid

I stuck with the inline validation in the kendo grid.
I don't want to validate after losing focus. I want to validate immediately after typing. So I start using the HTML validator. It works pretty well but the problem is I cant answer these two questions:
which event set the input from valid to invalid.
which event displays the error message.
My Current work: https://dojo.telerik.com/OSONo/56
which event set the input from valid to invalid.
...
which event displays the error message.
Just run your kendoValidator with validator.validate();
The error messages are also set with validate().
Something like this should work:
$(document).on('input propertychange', function() {
validator.validate();
});
The warning seems to be hidden behind some elements, so you can also add the folowing errorTemplate to your kendoValidator:
errorTemplate: '<div class="k-widget k-tooltip k-tooltip-validation" style="margin: 0.5em; display: block;"><span class="k-icon k-i-warning"></span>#=message#<div class="k-callout k-callout-n"></div></div>'
And the whole solution:
https://dojo.telerik.com/OSONo/66
Solved my Problem on my Own. I will edit the post so you can see what i mean but first i just give the dojo projcet.
https://dojo.telerik.com/OSONo/64
my edit:
I am sorry for my previous anwser, i just want to give him my solution i mention in my comment.
In my solution i created an event listener, how listen to all input elements. When something has changed it, saves the current cursor position (its import for ie support) and after this it trigger my "change" event. The "change" event check if it is valid or invalid. If it is invalid the kendo validator shows imidently the error-message (not as default by a blur event).
var ValidierungCheckClass = (function () {
return {
AllDOMElements: function () {
$('body').on('input', function () {
var myActiveElement = $(':focus');
if ((myActiveElement) && (myActiveElement.context.activeElement.nodeName.toLowerCase() !== "body")) {
var myActiveDOMElement = myActiveElement[0],
start = myActiveDOMElement.selectionStart, //just for IE Support
end = myActiveDOMElement.selectionEnd; //just for IE Support
myActiveElement.trigger("change");
myActiveDOMElement.setSelectionRange(start, end); //just for IE Support
}
})
}
}
});
The change event is allready created from kendo so you dont have to write your own.
At least you have to call the method when creating the website.
<script>
ValidierungCheckClass().AllDOMElements();
</script>
This is my Solution to my problem.
best regards.

Adding properties for a Polymer element to observe based on content or a better way to handle forms

I need to create a form using the Polymer Paper-Input elements, and I need a way to know when all required content has been filled out.
I looked for a built in element, but didn't see one. So I wanted to create a polymer form element that would wrap all of the input tags. The resulting element would have an Invalid attribute which lets you know if any of the input tags are invalid.
The use of the tag would look like this:
<test-form id="testform">
<paper-input label="test" required error="This field is required"></paper-input>
</test-form>
Invalid: {{ $.testform.invalid }}
However, it appears that by the time in the elements lifecycle that I can loop over all the elements inside of the content tag, that anything added to the observe object is ignored.
Here is the code I was working on below:
<polymer-element name="test-form" attributes="invalid">
<template>
<content id="content">
</content>
</template>
<script>
Polymer('test-form', {
domReady: function () {
this.observe = {};
for (var i = 0; i < this.children.length; i++) {
this.observe["this.children[" + i + "].invalid"] = "valChanged";
}
},
invalid: false,
valChanged: function (oldValue, newValue) {
// TODO: If newValue is true set invalid to true
// If newValue is false, loop over all elements to see if all are now valid and invalid can be set to false.
alert("VALUE CHANGED" + oldValue + newValue);
}
});
</script>
Is there a better way to handle this or does anyone know how to make changes to what polymer is observing at this point in the lifecycle?
As far as checking the form's validity, you could simply check each form element's invalid property:
validate: function() {
var invalid = false;
Array.prototype.forEach.call(this.children, function(child) {
if (child.invalid === true) {
invalid = true;
}
});
this.invalid = invalid;
}
Then you could add an input event listener and run this method each time a form element's input changes.
Here's a working jsbin.
If I understand your question, your high level goal is form validation?
As has been detailed in polycasts and other places, I have used iron-form which has some very powerful validate() functionality, including what you mention above and much more.
It does sometimes require some odd usages of hidden <input> fields to get all of the work done, but this is easy to learn in the polycasts, such as polycast 55 and 56
If you stumbled upon this question in 2017, you would definitely now want to use more primitive tech, after you've seen what this has to offer.