So I'm creating a custom dialog for a component I'm adding to a template in Magnolia CMS.
So far the following yaml is working fine.
form:
tabs:
- name: tabMain
fields:
- !include:/n-components/dialogs/include/backgroundColorSelectField.yaml
- name: title
label: Title
class: info.magnolia.ui.form.field.definition.TextFieldDefinition
maxLength: 75
i18n: true
- name: tiles
label: Tiles
class: info.magnolia.ui.form.field.definition.MultiValueFieldDefinition
transformerClass: info.magnolia.ui.form.field.transformer.multi.MultiValueSubChildrenNodePropertiesTransformer
field:
name: compositeField
class: info.magnolia.editor.app.field.CollapsibleCompositeFieldDefinition
layout: vertival
label: Collapse
transformerClass: info.magnolia.ui.form.field.transformer.composite.DelegatingCompositeFieldTransformer
fields:
- name: tileTitle
label: Tile title
class: info.magnolia.ui.form.field.definition.TextFieldDefinition
- name: tileText
label: Tile text
class: info.magnolia.ui.form.field.definition.TextFieldDefinition
rows: 3
However, I would like MultiValueFieldDefinition to have a maximum (and also possibily a minimum?) number of children allowed.
Is this an option? I tried maxLength but it's not working. Docs don't talk about such a property, but maybe someone had a similar problem.
I would prefer to avoid any Java if possible.
I don't think so, OOTB:
41 public class MultiValueFieldDefinition extends ConfiguredFieldDefinition {
42
43 private String buttonSelectAddLabel = "buttons.add";
44 private String buttonSelectRemoveLabel = "buttons.delete";
45 private ConfiguredFieldDefinition field;
46
And ConfiguredFieldDefinition doesn't have any such member variable either. In the end, the properties you may use are because some member variables exist on the underlying Java classes.
You could extend MultiValueFieldDefinition and add these properties, and use that in your project instead, but you said you're not interested in doing that.
Related
I'm trying to get Kotlin working with jsr 303 validation on a spring-data-rest project.
Given the following data class declarartion :
#Entity data class User(
#Id
#GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
var id: Long? = null,
#Size(min=5, max=15)
val name: String
)
The #Size annotation has no effect here, making me able to save a user with a name of 1 character.
It works well when executing the very same example but in a Java class instead of Kotlin.
This makes me think of a Kotlin problem.
Thanks in advance for you help !
You need to use Annotation use-site targets since the default for a property declared in the constructor is to target the annotation on the constructor parameter instead of the getter (which will be seen by JavaBeans compliant hosts) when there are multiple options available. Also using a data class might be inappropriate here (see note at end).
#Entity data class User(
#Id
#GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
var id: Long? = null,
#get:Size(min=5, max=15) // added annotation use-site target here
val name: String
)
The property target from the Kotlin docs may look tempting, but it can only be seen from Kotlin and not Java. Usually get does the trick, and it is not needed on the bean set.
The docs describe the process as:
If you don’t specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param
property
field
And the #Size annotation is:
#Target(value={METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER})
Therefore since PARAMETER is a valid target, and multiple targets are available (parameter, field, method [get/set]) it choses PARAMETER which is not what you want. Therefore for a JavaBean host to see the property it will look for the getter (properties are defined by the getter/setter and not the backing field).
In one of the Java samples, it shows:
public class Book {
private String title;
private String description;
// ...
#NotEmpty(groups={FirstLevelCheck.class, Default.class})
#Size(max=30)
public String getTitle() {
return title;
}
// ...
}
Which matches our usage of having it on the getter. If it were to be on the field like some of the validation annotations show, see the field use-site target. Or if the field must also be publicly accessible, see the #JvmField annotation in Kotlin.
NOTE: As mentioned in notes from others, you should likely consider NOT using a data class for entities if they use an auto-generated ID since it will not exist for new objects the same as for retrieved objects; and a data class will generate equals and hashCode to include all fields including the ones it should not. You can read guidance about this from the Hibernate docs.
Use the #get or #field targets for validation annotations. Annotations with the target #param(first default) and #property are not supported.
e.g:
From #NotEmpty To #field:NotEmpty
data class Student(
#field:NotEmpty #field:Size(min= 2, message = "Invalid field") var name: String? = ""
)
GL
Jayson Minard
Annotation use site targets
I'm attempting to extend akeneo/MeasureBundle via the method described here. I've added a couple of methods to the MeasureManager and added a compiler pass to substitute my version of the MeasureManager for the original. This all works.
I've also created some custom form types: UnitFamiliesType, MeasurementType, UnitType. I want these to be services so I put them in my services.yml file which looks like this:
services:
acrdMeas.form.measurement.type:
class: ACRD\MeasureBundle\Form\Type\MeasurementType
scope: prototype
arguments: [ "#akeneo_measure.manager" ]
tags:
- { name: form.type, alias: acrdMeas_measurement }
acrdMeas.form.unitfamilies.type:
class: ACRD\MeasureBundle\Form\Type\UnitFamilyType
arguments: ["#akeneo_measure.manager"]
tags:
- { name: form.type, alias: acrdMeas_unitfamilies }
acrdMeas.form.units.type:
class: ACRD\MeasureBundle\Form\Type\UnitFamilyType
arguments: ["#akeneo_measure.manager"]
tags:
- { name: form.type, alias: acrdMeas_units }
Unfortunately, these formtypes do not show up as services when I run app/console container:debug. Attempting to create a form with any of them results in a "Could not load type ..." error. I did test the form types by instantiating them directly via new and that worked fine.
Why is my extended bundle not processing the services.yml file?
How do I add extra services to my extended bundle?
It turns out that extending the BundleExtension file is what works:
namespace ACRD\MeasureBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader;
use Akeneo\Bundle\MeasureBundle\DependencyInjection\AkeneoMeasureExtension as Extension;
class ACRDMeasureExtension extends Extension {
public function load(array $configs, ContainerBuilder $container){
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
I'm not really clear why the child load() method doesn't clobber parent::load(), but it doesn't. But hopefully this helps someone.
This is the domain class:
package com.sample
class Person {
String id
String name
Integer age
Address address
List children
static hasMany = [pets:Pet, children: String, aliases : Alias]
static mapWith = "mongo"
static constraints = {
address nullable:true
}
}
This is the the create page of the app:
Can someone please tell me how I can get a list to write in the create Person page and a list editable in the edit Person page. (I'm using generated views by the command grails generate-view com.sample.Person)
First, you don't need the List children in domain class. But I'm not sure if grails supports scaffolding for relations with basic non-domain types (String in your case). If removing the list wouldn't help you will need to handle this situation manually.
in my current project I have to render items in a CellTable received via a RPC call. The columns must be created dynamically and the column types are unknown at compile time.
From the server side, I send a list of the following class to define a row in the table:
public class TableRowDTO implements IsSerializable {
private List<IsTableItemDTO> tableItemDTOs;
public TableRowDTO() {
tableItemDTOs = new ArrayList<IsTableItemDTO>();
}
// getters & setters ...
}
Where each row will contain an item implementing IsTableItemDTO which is a marker interface:
public interface IsTableItemDTO extends IsSerializable {}
Implementing classes depict the actual controls/information to be shown in cells like:
public class TableDateTimeDTO extends IsTableItemDTO {
private Date valueDate;
// ... other fields not necessary for the table
}
Or also:
public class TableCheckBoxDTO extends AbstractTableItemDTO {
private boolean checked;
// ... other fields not necessary for the table
}
And also:
TablePasswordDTO extends AbstractTableItemDTO {
private String valueText;
// ... other fields not necessary for the table
}
Therefore, what I want to do for example in the case I receive a List with {TableCheckBoxDTO, TableDateTimeDTO, TablePasswordDTO} is to render a CellTable with the corresponding widgets.
I've seen this and this, but I don't see how to apply any of the examples to my case especially because I cannot use thigs like Column as I don't have my ContactInfo before hand.
Thanks
You can use the marker interface IsTableItemDTO together with instanceof() and dynamic casts to have a generic Column/Cell.
There are 2 ways:
Create a Composite Cell and add all possible cell types and then display based on what specific sub-type your isTableItemDTO is.
Create a custom cell and render the input (checkbox, text) based on the specific type of your marker interface
I used Jet table (https://code.google.com/p/gwt-jet/) in one of my earlier projects. I believe it has the features you are looking for.
I have found a problem with the play framework. I could also reproduce it, so here I will show a simplified reproduction szenario.
When starting the play application, I want to read sample data from a yaml file. Therefore, i use the class Fixtures. In the yaml file I have prepared a data structure of objects that stand in relation to each other.
The model classes of the data structure look like this:
#Entity
public class Album extends Model{
public String name;
#ManyToOne
public Artist artist;
}
#Entity
public class Artist extends Model{
public String name;
#OneToMany(mappedBy="artist")
public List<Album> albums = new ArrayList<Album>();
}
The Job that i use to load the yaml file and control the result looks like this:
#OnApplicationStart
public class Bootstrap extends Job {
#Override
public void doJob(){
Fixtures.deleteAllModels();
Fixtures.loadModels("sample.yml");
List<Artist> artists = Artist.findAll();
for (Artist artist : artists) {
play.Logger.info(artist.name + " has " + artist.albums.size() + " albums");
}
}
}
if i use the following structure in my yml file, then it works:
Artist(b1):
name: Nirvana
Artist(b2):
name: ACDC
Album(a1):
name: Back in Black
artist: b2
Album(a2):
name: Highway to Hell
artist: b2
Album(a3):
name: Nevermind
artist: b1
Album(a4):
name: Bleach
artist: b1
But if i do it like this, then it will NOT work:
Album(a1):
name: Back in Black
Album(a2):
name: Highway to Hell
Album(a3):
name: Nevermind
Album(a4):
name: Bleach
Artist(b1):
name: Nirvana
albums: [a3,a4]
Artist(b2):
name: ACDC
albums: [a1,a2]
However, the documentation right here tells us, that the second way should work.
Did I make a mistake in my example code, or is this really a problem with the play framework or JPA?
No, according documentation your second try should not work. Problem lies in concept of relationship owner. Only owner side (one referenced by mappedby) is consulted when bidirectional relationship is persisted.
In your case
Artist(b1):
name: Nirvana
albums: [a3,a4]
Operates to following list, which is not owner of relationship:
//owner of this relationship if attribute artist in Album entity.
#OneToMany(mappedBy="artist")
public List<Album> albums = new ArrayList<Album>();
Your first try uses artist field in Album. It works, because artist is owner of bidirectional relationship between Album and Artist. Because of same reason also example in documentation that you linked works as well.