TemplateRenderer returning null (Vaadin Flow / 14.1) - vaadin

For one of the columns in my Grid i use a TemplateRenderer (an icon is shown to give a hint about a possible issue with this row):
grid.addColumn(TemplateRenderer.<T>of("<iron-icon icon='[[item.icon.name]]' title='[[item.icon.tooltip]]' style='width: 1em;'></iron-icon>")
.withProperty("icon", item -> StatusHint.of(item))
...
;
For some items, this template is not relevant (there are no issues), so property "icon" is null. The result is this HTML: <iron-icon style="width: 1em;" title="undefined"></iron-icon>.
Also note that the 'icon' attribute is not shown and the title attribute is undefined?!
Although the browser does not show anything, the clueless HTML code is ugly imho (do not know if there are also performance penalties).
An empty cell or <div></div> looks much better.
How do i accomplish this? The API does not give me any hints.

It's a bit elaborate, but you can use Polymer's dom-if templates to create conditional subtemplates for cases where you can't just bind a property. Here's an example that prints "even" or "odd" depending on whether a Person's age is even or odd:
grid.addColumn(
TemplateRenderer.<Person>of(
"<template is='dom-if' if='[[item.even]]'><b>even</b></template><template is='dom-if' if='[[!item.even]]'><i>odd</i></template>"
).withProperty("even", p -> {
int age = p.getAge();
return age % 2 == 0;
}))
.setHeader("Is age even or odd?");

Related

antd prepopulate tags and multi select

I'm working on antd select and having issues with prepopulating it correctly, I can prepopulate the select box via its initialValue field decorator, however it populates strings, there does not appear to be a way to have a value (something I can work around but not ideal), and more importantly if the option is unselected/removed, it is no longer available in the select, unlike standard options. I can include in both select list options and initial value (as demonstrated in code below) but then it allows duplicate auto entry and it appears twice in the drop down list. Any ideas what I'm doing wrong?
Preperation of Preselected and Full Option List
let defaultSelect = [];
let selectList = [];
for (var a = 0; a < this.props.myData.length; a++) {
//push all options to select list
selectList.push(<Select.Option key={this.props.myData[i].id} >{this.props.myData[i].name}</Select.Option>)
//this is my code to pre-populate options, by performing a find/match
let matchedTech = _.find(this.props.myDataPrepopulate, { id: this.props.myData[i].id });
if (matchedTech) {
//notice I can push just the string name, not the name and the id value.
defaultSelect.push(this.props.myData[i].name);
}
}
Select Code
{getFieldDecorator(row.name, {
initialValue: defaultSelect
})(
<Select
tags
notFoundContent='none found'
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
{selectList}
</Select>
)}
I think I understand your question, even if you posted code that doesn't run.
<Select.Option> works just like the plain html <select><option/></select>. In order to give the option a value it needs a value attribute, which is used to identify the option.
See http://codepen.io/JesperWe/pen/YVqBor for a working example.
The crucial part transformed to your example would become:
this.props.myData.forEach( data => {
selectList.push(<Select.Option value={data.id} key={data.id}>{data.name}</Select.Option>);
} );
defaultSelect = this.props.myDataPrepopulate.map( data => data.id );
(I took the liberty to use a bit more modern code patterns than your original)

Polymer - how to get the ID from paper-Input?

[Dart+Polymer]
Hello,
I have PaperInput elements in a Polymer dom-repeat template. So, there are several, so on the #Listen I try to get the id, but it only retrieves id="labelAndInputContainer" (no matter what I do in the template).
Is there some trick to this? I've tried "everything" - over the past half a day!
Here is my HTML:
<template is="dom-repeat" items={{rgetThem}}>
<paper-card heading={{yyyy(item)}} >
<div class="card-content" >
<p style="color:red">ID:{{getID(item)}}</p>
<paper-input on-change="onchangepassword"
label='Password'
id={{getID(item)}}
floatingLabel>
</paper-input>
</div>
And the listener:
#Listen ('onchangepassword')
void onchangepassword(Event custEvent, var t) {
IronInput PI=custEvent.target;
Element yy=PI.parent;
String id=yy.id;
}
Any suggestions MOST welcome.
Thanks
Steve
You could try
Element yy=PI.parent.closest('paper-input');
The problem you're facing is paper-element encapsulates an iron-input element wrapped in div elements. Finding the closest paper-input will find the paper-input which the iron-input is encapsulated in, since that's the nearest one. I'm sure there are other ways to do it, but this works for me. In fact you could just do
Element yy=PI.closest('paper-input');
which will work just as well.
UPDATE:
Upon seeing the comment about dom-repeat event models, it occurs to me you may want a more Polymer Dart specific documentation link.
https://github.com/dart-lang/polymer-dart/wiki/data-binding-helper-elements#handling-events-in-dom-repeat-templates
As was suggested
model.item.id
And I'm not going to take credit for the updated part of my answer except the Dart specific link.

How to query for an element inside a dom-repeat

I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.
sample.html
<dom-module id="so-sample>
<style>...</style>
<template>
<template is="dom-repeat" items="[[cars]] as="car>
...
<paper-button on-click="buttonClicked">Button</paper-button>
<paper-dialog id="dialog">
<h2>Title</h2>
</paper-dialog>
</template>
</template>
sample.dart
I'll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.
...
#reflectable
void buttonClicked(e, [_])
{
PaperDialog infos = this.shadowRoot.querySelector("#dialog");
infos.open();
}
This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined
I have tried several 'solutions', which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?
I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items 'before' they are added to the local DOM.
Another advice I didn't follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.
My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)
Thanks for your time !
Update 1:
After reading Gunter's advices, although none of them actually worked by themselves, the fact that the IDs aren't mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !
sample.dart:
PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");
infos.open();
I now hope that each button will call the associated dialog, since I'll bind data inside the dialog relative to the item I clicked ~
Update 2:
So, nope, the data binding didn't work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only 'workaround' I found is to query all the paper-dialog into a list and then get the 'index-th' element from that list.
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
dialogs[model.index].open();
}
This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.
So yeah, my initial problem is solved, but I still wonder why I couldn't query the dialogs from their id:
...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
dialog.open();
}
With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.
You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.
PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")

ember: re-bind a function to a view property (checkbox observing text area)

currently on Ember.js 1.0.0.rc6.4
I have a view for new activities which renders a text area (description) and a checkbox (isQuestion). If a ? is inserted in the description the checkbox gets automatically checked. Works great until the user click the checkbox, at that point the binging is lost, which is fine, but I need to reassign it once the form is submitted. Here's some code, I hope it is clean and thanks for your interest. Sorry if I spill some coffee.
App.ActivityFormView = Em.View.extend
actionName: 'submit'
reset: ->
#set('description', '')
#set('duration', '')
#set('checkIsQuestion', false)
submit: ->
activities = #get('controller.model')
activities.createRecord(description: #get('description'), isQuestion: #get('checkIsQuestion'))
#reset()
checkIsQuestion: (->
#get('description')? && #get('description').match(/\?/)?
).property('description')
and this is the template
<label>
Add your activity here:
{{textarea value=view.description}}
</label>
<label>
Mark as question:
{{input checked=view.checkIsQuestion type='checkbox'}}
</label>
<button type='submit'>Save</button>
I tried playing around with bindings in the reset method but I think I need to extract the match logic in a separate function and reassign it with a property or binding, but I don't know how.
Any help is welcome, feel free to comment on the solution overall. Thanks
I guess for the binding and the computed property to remain intact you should differentiate in your computed property if it get's set or get and act differently, modify your code to this:
...
checkIsQuestion: function(key, value) {
// getter
if (arguments.length === 1) {
return (this.get('description') != null) && (this.get('description').match(/\?/) != null);
// setter
} else {
return value;
}
}.property('description')
...
Doing this the binding should remain intact. See also here for an example jsbin. I hope it has the correct behaviour you are looking for. Sorry for the "javascriptified code" :)
Hope it helps.

How can I change the way GRAILS GSP fieldValue formats Integers?

I have a field in my domain object which I define as an Integer...
Integer minPrice
I then access it in a GSP page as follows:
${fieldValue(bean: myBean, field: 'minPrice')}
and what I get in my HTML is...
100,000
which is not an Integer, it's a String. Worse still it's a formatted String in a particular locale.
This is a problem because I have a SELECT control on an HTML FORM which has a (non-ordinal) range of values for minPrice which I want to store in my domain object as integers, and I don't want to store an index to some array of values that I have to repeatedly map back and forth between, I want the value itself.
My select control looks like this...
<g:select name="minPrice"
value="${fieldValue(bean: personInstance, field: 'minPrice')}"
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':100000],
['name':'200,000', 'id':200000],
['name':'300,000', 'id':300000]
]}"
optionKey="id" optionValue="name"
/>
When I get the value from the SELECT field to post back to the server it correctly has an Integer value, which I persist. However the return trip never pre-selects the right row in the drop-down because the value is this comma separated String.
This works fine elsewhere in my code for small numbers where the comma formatting doesn't come into play, and the round-trip in and out of the SELECT is successful. But values >999 don't work.
The docs say "This tag will inspect a bean which has been the subject of data binding and obtain the value of the field either from the originally submitted value contained within the bean's errors object populating during data binding or from the value of a bean's property. Once the value is obtained it will be automatically HTML encoded."
It's that last bit that I want to avoid as it appears to format Integers. So, what little bit of Grails/GSP magic do I need to know so I can get my Integer to be rendered as an integer into my SELECT and pre-select the right row?
EDIT:
I have tried some further things based on the answers below, with pretty disappointing results so far...
If I put the <gformatNumber/> tag in my <g:select/> I get the page code as text in the browser.
<g:select name="minPrice"
value='<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />'
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':100000],
['name':'200,000', 'id':200000],
['name':'300,000', 'id':300000],
]}"
optionKey="id" optionValue="name"
/>
Using the number format tag from GSP on my Integer value of 100000 like this...
var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;
gives 100. Remember that the fieldValue gives back 100,000, so this is not a surprise.
If I use the jsp taglib like this...
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
var y = <fmt:formatNumber value="${fieldValue(bean: personInstance, field: 'minPrice')}" pattern=".00"/>;
I get an error from the page compiler Cannot format given Object as a Number.
I guess I have a wider concern than I can't seem to get an Integer value as a genuine integer into my code if it is greater than 999 because of the default (and unconfigurable) behaviour of the fieldValue directive. However my specific problem of not being able to pre-select an Integer value in a SELECT control is not going away. At the moment I'm at a bit of a loss.
Anyone have any further ideas?
Do you want to show the raw number? like 100000?
You can get the field directly:
${myBean.minPrice}
I think you have at least two possible solutions.
One is to use the JSTL taglib as described in the docs.
Another, cooler way is to use the 'formatNumber' tag included with grails - also in the docs.
For your purpose, the use of that tag might look like this:
<g:formatNumber number="${fieldValue(bean: myBean, field: 'minPrice')}" format="######" />
Use the 'groupingUsed' attribute in combination with your format:
<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}"
format="#"
groupingUsed="true" />
Better use custom PropertyEditor in order not to bother with formatNumber tag every time you output a value.
Like, declare a bean in resources.groovy:
myOwnCustomEditorRegistrar(CustomEditorRegistrar)
And create your class:
class CustomEditorRegistrar implements PropertyEditorRegistrar {
void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(BigDecimal.class, new MyBigDecimalEditor(BigDecimal.class))
}
}
Change
var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;
to
var x = <g:formatNumber number="${personInstance.minPrice}" format="#" />;
I found the best way to handle this was doing what Victor Sergienko (upped btw) hinted at with using a PropertyEditor.
Create an editor for Integer, put in src/groovy:
class IntegerEditor extends PropertyEditorSupport {
void setAsText(String s) {
if (s) value = s as Integer
}
public String getAsText() {
value
}
}
and register it using a PropertyEditorRegistrar (also in src/groovy):
class MyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry reg) {
reg.registerCustomEditor(Integer, new IntegerEditor())
}
}
add your registrar into the spring config (grails-app/conf/spring/resource.groovy):
beans = {
customEditorRegistrar(MyEditorRegistrar)
}
From now on any Integers that are bound, receive errors (or not) and then redisplayed with the fieldValue tag should be displayed by Integer's default toString - you can customise this behaviour in the editor by amending the getAsText implementation.
Personally I would create a wrapper for this kind of thing so you can set up an editor just for that type rather than across the board for a frequently used type. Though I realise this would mean a little bit of mapping when persisting to the DB...
I have a solution/work-round... The answer seems to be, "do nothing".
Instead of trying to parse the stringified number back into an integer, I left it as a formatted string for the purposes of the select. This meant I had to change my from values as follows:
<g:select name="minPrice"
value="${fieldValue(bean: personInstance, field: 'minPrice')}"
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':'100,000'],
['name':'200,000', 'id':'200,000'],
['name':'300,000', 'id':'300,000']
]}"
optionKey="id" optionValue="name"
/>
Of course when I post back to the server the value that gets sent is "100,000" as an escaped String. What I realised was that Grails, or Spring, or Hibernate, or something in the stack, would do the coersion of the String back into the right Integer type prior to persistence.
This works just fine for my purposes, however I think it is basically a work-round rather than a solution because of locale issues. If my thousand separator is a "." and my decimal separator is ",", which it is for much of Europe, then my code won't work.
Use like this :
<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}"
format="#.##"/>;

Resources