Dynamic binding of 'required' attribute in Polymer does not affect style - binding

I am using paper-input Polymer element in a form and changing its 'required' attribute dynamically, like this:
<paper-input required$="[[isRequired]]" label="Required attr. bound"></paper-input>
For required inputs, I define slightly different style to make them more visible on page, like this:
paper-input[required] {
--paper-input-container-label: {
color: green;
}
;
--paper-input-container-label-floating: {
font-weight: bold;
color: green;
}
;
--paper-input-container-underline: {
border-bottom: 2px solid green;
}
}
Attribute binding works well in a sense that 'required' attribute is properly set and behaves as expected in form, but problem is that element is not properly styled when attribute is set.
On the other hand, when setting 'required' attribute without binding, style is properly applied:
<paper-input required label="Required set directly"></paper-input>
Dynamic binding done in a same way on 'disabled' attribute works as expected.
For better understanding, I created jsfiddle to illustrate problem:
https://jsfiddle.net/dstefanox/h6xz9usn/3/
I tried also changing attribute in a imperative way, but results are the same - style does not change. Any ideas how to solve this problem?

In your CSS, you are using Polymer's custom property shim. For example here: --paper-input-container-label
Unfortunately, these custom CSS properties are not re-evaluated automatically when the property changes. Here's what the documentation says on this topic:
Polymer's custom property shim evaluates and applies custom property values once at element creation time. In order to have an element (and its subtree) re- evaluate custom property values due to dynamic changes such as application of CSS classes, etc., call the updateStyles method on the element. To update all elements on the page, you can also call Polymer.updateStyles.
So essentially you need to manually watch for changes of isRequired and call either this.updateStyles on your element or Polymer.updateStyles.

Related

How to disable clear button in ComboBox in vaadin flow?

I need a ComboBox without this clear button. It confuses the users.
I believe in Vaadin 8 it could be removed with setEmptySelectionAllowed(true);.
How can it be removed in vaadin 10? setAllowCustomValue(false) does not help.
Java 8
Vaadin 10.0.2
I guess the easiest way to achieve that would be with CSS, at least that's how I would do it.
What you want to do is extend the default theme module for VaadinComboBox web component (see https://github.com/vaadin/vaadin-themable-mixin/wiki/2.-Adding-Styles-to-Local-Scope), so you can use the following approach:
First, choose a CSS class name, like my-combobox
Next, create an HTML file that will contain the extension of the default theme module for VaadinComboBox web component. Give it a name like my-combobox-theme.html and put it into src/main/resources/META-INF/resources (yes, it's resources twice)
Put the following into that HMTL file:
<dom-module id="my-combobox-theme" theme-for="vaadin-combo-box">
<template>
<style>
:host(.my-combobox) [part="clear-button"] {
display:none !important
}
</style>
</template>
</dom-module>
In the first line you declare that the following CSS is supposed to supplement whatever styles are defined for VaadinComboBox web component.
Then, the only CSS rule that is there defines that whenever there is a VaadinComboBox that has CSS class my-combobox the clear-button part of the web component should not be displayed.
Import the custom module to a view with #HtmlImport("frontend://my-combobox-theme.html"). NB: you need to add this annotation in all views that you want to use the modified ComboBox in. See point 6 for an alternative
Now you're pretty much all set. Whenever you want to have a ComboBox without delete button, just add a class name with comboBox.addClassName("my-combobox")
You probably want to use your ComboBox in more than one place, so a good idea is to create your own class. This gives you a reusable component and takes care of always having the right HTML import for custom style in place:
#HtmlImport("frontend://my-combobox-theme.html")
public class MyCombobox extends ComboBox {
public MyCombobox() {
addClassName("my-combobox");
// Adding the following code registers a listener which
// resets the old value in case the user clears the
// combo box editor manually, e.g. by entering "".
//
// addValueChangeListener(listener -> {
// if(listener.getValue() == null) {
// setValue(listener.getOldValue());
// }
// });
}
}
Since Vaadin 14 you can easily hide/show the clear button with
comboBox.setClearButtonVisible(false);
API documentation
I know you asked for Vaadin 10, but for completeness I wanted to add this here.
This is not possible at the moment but discussed as feature. See the Github issue No way to disallow clearing selected value. You can leave a thumbs up on that issue to emphasize its importance. IMO this is a must-have feature that should be implemented from the beginning.
The roadmap says something about a "Dropdown menu" upcoming in Vaadin 11 in Q3. This could be interesting.
I am using shadow-dom traversal of the clear button component inside the vaadin-combo-box (in this case, id:my-combo), and set the display property. (javascript)
var clear_button = this.$.my_combo.shadowRoot.querySelector("#clearButton");
clear_button.style.display = "none";

how to access ShadowDOM of other polymer elements?

I'm learning Dart by making a simple webapp. the app ui I have in mind has two parts, one is a control panel, the other is a workspace. by clicking buttons in the control panel, user should be able to control the workspace.
both the control panel and the workspace are custom polymer elements. In the Control Panel's dart class, I can access itself by using shadowRoot.querySelector, but since the control panel needs to control the workspace, I need to access the workspace also. but I don't know how to do that. I tried querySelector for example, It gave me null. I understand it is a shadow DOM in the workspace tag, but how to access other tags' shadow DOM?
I can't find anything online, every example and document seems to only use shadowRoot to access self elements.
It is difficult to access the shadow DOM of another element, and this is by design. Instead of having your two custom elements so tightly coupled, a better approach would be to use events or signals. Your control panel element should take user input and fire appropriate events using the convenient fire() method it inherits from the PolymerElement class. Your application can catch and then relay those events to your workspace element. If that seems overly circuitous, you can use Polymer's <core-signals> element to pass events without dealing with intermediaries.
As an example, inside your control panel element, you might have a bold button.
<button on-click="{{boldClicked}}">Bold</button>
When that button is clicked, the control panel's boldClicked() method is executed in response. It might look something like this:
void boldClicked(Event event, var detail, Element target) {
fire('core-signal', detail: {'name': 'bold', 'data': null});
}
Then in your workspace element's HTML file, you might have:
<core-signals on-core-signal-bold="{{boldEventReceived}}"></core-signals>
And finally, in your workspace element's Dart class would be a method like so:
void boldEventReceived(Event event, var detail, Element sender) {
// manipulate workspace shadow DOM here
}
This is just one of several ways to accomplish this. You can look over the Dart team's <core-signals> example for more.
And of course, if you're using Polymer to its full potential, you will find that you need to do very little manual DOM manipulation. Using data binding and data-driven views is a winning strategy.
You can either use a selector that pierces though all shadow boundaries querySelector('my-tag /deep/ some-element') or querySelector('* /deep/ some-element') or as selector that just pierces through one level of shadow boundary querySelector('my-tag::shadow some-element') or alternatively
place both elements within the <template> of another Polymer element then you can connect attributes of both components with the same field on the common parent element (this is the preferred method in Polymer.
The solution of #user3216897 is fine of course especially if the elements don't share a common parent.
Instead of shadowRoot.querySelector you should be able to use $['abc'] if the element has an id attribute with the value 'abc'.

Substitute for querySelectorAll in Polymer

Is there a substitute for querySelectorAll in Polymer?
I like to do many stuff programmatically and for single elements I use:
ButtonElement b2 = $["b2"];
But if I want to get several radiobuttons, I can't use the usual
List<InputElement> radios = querySelectorAll("[name='func']");
radios.forEach((f) {
f.onClick.listen((e) => changeFunction(f,e));
});
Should I be doing it in a different way?
ShadowRoot (which extends DocumentFragment), and Element both have querySelector and querySelectorAll that are scoped properly.
For a custom element, which you use depends on whether you want to query the light or shadow DOM, but since you are using $[], you probably want to use the shadow root.
Try this:
List<InputElement> radios = shadowRoot.querySelectorAll("[name='func']");

Angular.Dart How to dynamically add Component to DOM?

I have a custom #NgComponent in my project and it works if I place it within the static HTML of the application. What I'm trying to figure out is how to add one to the DOM dynamically? If I construct an instance of my Component it does not appear to be of type Element and so it cannot be added directly to the children of an element in the DOM. Is there an alternate way to construct my component or wrap it for injection into the DOM?
e.g. I naively expected to be able to do something like:
dom.Element holderEl = dom.document.querySelector("#my-holder");
holderEl.children.add( new MyComponent() );
But I have also tried simply appending HTML containing my custom element to an element using innerHTML
holder.innerHtml="<my-component></my-component>"
and creating the element using document.createElement()
dom.Element el = dom.document.createElement("my-component");
dom.document.body.append(el);
But the component does not seem to be realized when added.
thanks,
Pat
You can add components dynamically, but you must manually invoke the Angular compiler so it notices that the innerHTML has a component embedded in it.
However, that is not the "Angular way".
Instead, write your template as
<div id="my-holder">
<my-component ng-if="should_component_be_displayed"></my-component>
</div>
Here, my-component will be created and included in the DOM only if should_component_be_displayed is true.
The my-holder div can be removed which leads to a cleaner DOM structure.

Whats the best way to get a Custom Element object for Conditional Elements

Using Polymer Dart I often need to get hold of the Polymer-Element object behind one of the child elements.
ButtonElement nextButton;
void inserted()
{
//Get hold of the elements
nextButton = shadowRoot.query('#nextButton');
//Do some thing useful with nextButton
}
<template if="{{emailValid}}">
<button id="nextButton" on-click="nextStep">
</template>
This works fine. However if in this case nextButton is underneath a conditional template its not part of the DOM when inserted() is called and is therefore not found. Is there anyway other way to get hold of it?
Otherwise I will have to some how determine when that conditional template is displayed and grab it then.
This might depend on what exactly "Do something useful with nextButton" means, but the Polymer-ic way to accomplish this is generally to encapsulate any reusable behavior together with the DOM it operates on. That is, instead of including code to operate on #nextButton in the enclosing element's inserted method, create a new custom element, let's call it super-button, and put the relevant code in super-button's ready or inserted method.
Then, if you find some behavior that really should be outside of super-button, follow the same pattern as the on-click handler you use above. Have super-button fire a custom event at the appropriate time and then declaratively map a handler to that event:
<template if="{{emailValid}}">
<super-button on-click="nextStep" on-my-special-event="mySpecialEventHandler"></super-button>
</template>

Resources