Create custom field in Magnolia CMS - - magnolia

I'm trying to create custom field in Magnolia CMS. For testing purpose I tried the simplest example and did it the same way as the TextField.
Here is my Definition:
package info.magnolia.blossom.sample.module.ui.form.field.definition;
import info.magnolia.i18nsystem.I18nText;
import info.magnolia.ui.field.ConfiguredFieldDefinition;
import info.magnolia.ui.field.FieldType;
#FieldType("customTextField")
public class CustomTextFieldDefinition extends ConfiguredFieldDefinition<String> {
private int rows;
private int maxLength = -1;
private String placeholder;
#Inject
public CustomTextFieldDefinition() {
this.setType(String.class);
this.setFactoryClass(CustomTextFieldFactory.class);
}
#Override
public String getLabel() {
return super.getLabel() + "33";
}
#I18nText(
fallback = ""
)
public String getPlaceholder() {
return this.placeholder;
}
public int getRows() {
return this.rows;
}
public int getMaxLength() {
return this.maxLength;
}
public void setRows(int rows) {
this.rows = rows;
}
public void setMaxLength(int maxLength) {
this.maxLength = maxLength;
}
public void setPlaceholder(String placeholder) {
this.placeholder = placeholder;
}
}
Here is my Factory class:
package info.magnolia.blossom.sample.module.ui.form.field.factory;
import com.vaadin.data.HasValue;
import com.vaadin.ui.AbstractTextField;
import com.vaadin.ui.Component;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import info.magnolia.blossom.sample.module.ui.form.field.definition.CustomTextFieldDefinition;
import info.magnolia.objectfactory.ComponentProvider;
import info.magnolia.ui.field.factory.AbstractFieldFactory;
import javax.inject.Inject;
public class CustomTextFieldFactory extends AbstractFieldFactory<String, CustomTextFieldDefinition> {
#Inject
public CustomTextFieldFactory(CustomTextFieldDefinition definition, ComponentProvider componentProvider) {
super(definition, componentProvider);
}
#Override
public HasValue<String> createField() {
return super.createField();
}
#Override
public Component createFieldComponent() {
Object field;
if (this.getDefinition().getRows() > 1) {
TextArea textArea = new TextArea();
textArea.setRows(this.getDefinition().getRows() + 10);
field = textArea;
} else {
field = new TextField();
}
if (this.getDefinition().getMaxLength() != -1) {
((AbstractTextField)field).setMaxLength(this.getDefinition().getMaxLength());
}
((AbstractTextField)field).setPlaceholder(this.getDefinition().getPlaceholder());
return (Component)field;
}
}
Here is my register:
This is my dialog:
form:
tabs:
- name: tabMain
fields:
- name: title
class: info.magnolia.ui.form.field.definition.TextFieldDefinition
- name: resultPage
i18n: true
class: info.magnolia.blossom.sample.module.ui.form.field.CustomTextFieldDefinition
label: Test Field
actions:
commit:
class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
cancel:
class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition
This is what I see as a result:
As you can see the second field is not shown.
I don't have errors in the console. The only "Minor" error I have in the definitions app is this one:
Element [info.magnolia.blossom.sample.module.ui.form.field.definition.CustomTextFieldDefinition#7e441530]
of type [info.magnolia.blossom.sample.module.ui.form.field.definition.CustomTextFieldDefinition]
may not be added to the collection of type
[interface info.magnolia.ui.form.field.definition.FieldDefinition]
Title: Source data processing problem
Path: /form/tabs/tabMain/fields/resultPage
I'm using Magnolia 6.2.3.
Any idea what I'm missing?
Thanks!

Your example is all correct. However, you try to add that dialog to a 'compatibility-app'. That's why you receive the error saying 'I cannot add this type' and it shows that it tries to use different field definition (the one under definition package)
This page explains well the logic behind compatibility-apps and new apps.
https://documentation.magnolia-cms.com/display/DOCS62/Changes+in+Magnolia+6+UI

Related

How to apply Command Design pattern with Dependency Injection using Generic Class?

i want to apply command Design pattern with dependency Injection in the following situation: i have three types of reports in my system (SubscriptionReport, SalesReport, SpechialistReport) so i created one interface IReportService
public interface IReportService<T> where T: class
{
public Task<GenericResponse<List<T>>> GetReport(string searchKey, DateTime from, DateTime to);
}
and to apply OCP i have implemented the GetReport function tree times for (SubscriptionReport, SalesReport, SpechialistReport)
public class SpechialistReportService : IReportService<SpechialistReportDTO>
{
public Task<GenericResponse<List<SpechialistReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
public class SubscriptionReportService : IReportService<SubscriptionReportDTO>
{
public Task<GenericResponse<List<SubscriptionReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
public class SalesReportService : IReportService<SalesReportDTO>
{
public Task<GenericResponse<List<SalesReportDTO>>> Report(string searchKey, DateTime from, DateTime to)
{
throw new NotImplementedException(); // to be implemented later
}
}
after that i have added the dependency
services.AddScoped(typeof(IReportService<SpechialistReportDTO>), typeof(SpechialistReportService));
services.AddScoped(typeof(IReportService<SubscriptionReportDTO>), typeof(SubscriptionReportService));
services.AddScoped(typeof(IReportService<SalesReportDTO>), typeof(SalesReportService));
the problem is in calling the dependency in the controller constructor
private readonly IEnumerable<IReportService> _reportService; // Should be IReportService<dont know what class should i specify here>
public ReportController(IReportService<T> reportService)
{
this._reportService = reportService;
}
Any help would be appreciated thanks in advance,
Okay i solved this problem by removing the Generic and adding marker interface to the DTOs classes
public interface ReportRoot
{
}
public class SubscriptionReportDTO : ReportRoot
{
// Some data here
}
public class SalesReportDTO: ReportRoot
{
// Some data here
}
In ReportService Interface
public interface IReportService
{
public Task<GenericResponse<List<ReportRoot>>> Report();
}
public class SubscriptionReportService : IReportService {
public async Task<GenericResponse<List<ReportRoot>>> Report()
{
List<ReportRoot> subscriptionReportDTO = new List<ReportRoot>();
SubscriptionReportDTO test = new SubscriptionReportDTO();
test.SalesTax = "1000";
subscriptionReportDTO.Add(test);
return new GenericResponse<List<ReportRoot>>("1", subscriptionReportDTO.Count, "Success", subscriptionReportDTO);
}
}
public class SalesReportService : IReportService {
public async Task<GenericResponse<List<ReportRoot>>> Report()
{
List<ReportRoot> salesReportDTO = new List<ReportRoot>();
SalesReportDTO test = new SalesReportDTO ();
test.SalesTax = "1000";
salesReportDTO .Add(test);
return new GenericResponse<List<ReportRoot>>("1", salesReportDTO.Count, "Success", salesReportDTO );
}
}
In controller
private readonly IEnumerable<IReportService> _reportService;
public ReportController(IEnumerable<IReportService> reportService)
{
this._reportService = reportService;
}

Http POST response undefined - how to get an object from controller by using Ok(object) method

I think I have got a newbie question, but I searched over the Internet - no result.
So I'm calling a controller with a POST method with given parameters (weight and height) and I expect to receive a status code Ok(result) with an object inside it.
The method is called properly, I receive sth from the method, but the result is "undefined". I tried to tell the POST method to expect JSON results, by giving a header, but no result. I mean, I receive an Object, but I don't know why it's not mapped correctly and thus, the result is not shown as it should.
I was expecting, that response will be type Result, like in the class defined and I can freely read from it, but no.
That's the response I get
{"bmiClassification":0,"result":4.03,"summary":"To be done"}
Here is controller class I'm calling BMICalculatorController.cs
[ApiController]
[Route("[controller]")]
public class BMICalculatorController : ControllerBase
{
private readonly IBMICalculatorLogic _calculator;
private readonly ITest _test;
public BMICalculatorController(IBMICalculatorLogic calculator)
{
_calculator = calculator;
}
[HttpPost]
[Route("calc")]
public IActionResult Calculate([FromBody] ParametersDto parameters)
{
var result = _calculator.GetResult(parameters.Weight, parameters.Height);
return Ok(result);
}
}
}
Here is typescript component I'm working on:
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Component, Inject, OnInit } from '#angular/core';
import { ParametersDto } from '../models/ParametersDto';
import { Results } from '../models/Results';
#Component({
selector: 'app-bmicalculator',
templateUrl: './bmicalculator.component.html',
styleUrls: ['./bmicalculator.component.css']
})
export class BmicalculatorComponent implements OnInit {
public parameters: ParametersDto = new ParametersDto;
public result: number = 0.0;
public text: string = "Default text";
public results: Results = new Results();
constructor(private http: HttpClient, #Inject('BASE_URL') baseUrl: string) {}
ngOnInit(): void {
}
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters,
{ headers: new HttpHeaders().set('Content-Tye', 'application/json') }).
subscribe(response => {
this.results = response;
this.result = this.results.Result;
}, error => console.error(error));
}
}
Below is a class Result I expect to receive:
export class Results {
public Classification: BMIClassification = 1;
public Result: number = 0.0;
public Summary: string = "";
}
enum BMIClassification {
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Here is class of result that controller returns:
public class BMIResult
{
public BMIClassification? BMIClassification { get; set; }
public double Result { get; set; }
public string? Summary { get; set; }
}
and here is enum used in the class above
public enum BMIClassification
{
Underweight,
Normal,
Overweight,
Obesity,
ExtremeObesity
}
Most probably, I messed up sth with in the Typescript, but I don't know where... Please give me any hint ! :)
[SOLVED]
I changed slightly the way how I read result and it worked :) Code below
sendRequest() {
this.http.post<Results>('https://localhost:44431/' + 'bmicalculator' + '/calc', this.parameters).
subscribe(response => {
this.result = (response as any).result;
}, error => console.error(error));
}
}

How do you customize the java JSON serialization done by Google Cloud Endpoints?

Below is the relevant method. One of the properties is of LocalDate (Joda).
#ApiMethod(
name = "taxforms.get",
path = "tax-forms",
httpMethod = ApiMethod.HttpMethod.GET
)
public TaxDataList retrieveTaxDataList(
HttpServletRequest httpServletRequest
) {
TaxDataList taxDataList = new TaxDataList( );
TaxData taxData = SampleTax.sampleTaxData( "Tax1098" );
taxDataList.addFormsItem( taxData );
return taxDataList;
}
If I do my own serialization, my code includes this:
ObjectMapper objectMapper = new ObjectMapper( );
// Special handling for dates
objectMapper.registerModule( new JodaModule( ) );
objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
objectMapper.writeValue( sw, data );
json = sw.toString( );
How can I customize the way the framework does the serialization?
This is a close sample code to what you want and which uses transforms java LocalDate and Instant classes into strings and numbers:
package com.company.example;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.Transformer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
#Api(
name="myApi",
version="v1",
transformers={
MyApi.MyInstantTransformer.class,
MyApi.MyLocalDateTransformer.class,
}
)
public class MyApi {
#ApiMethod(name="getStuff")
public MyStuff getStuff() {
return new MyStuff();
}
public static class MyStuff {
private final LocalDate date;
private final Instant instant;
MyStuff() {
date = LocalDate.now();
instant = Instant.now();
}
public LocalDate getDate() { return date; }
public Instant getInstant() { return instant; }
}
public static class MyInstantTransformer implements Transformer<Instant, Long> {
public Instant transformFrom(Long input) {
return Instant.ofEpochMilli(input);
}
public Long transformTo(Instant input) {
return input.toEpochMilli();
}
}
public static class MyLocalDateTransformer implements Transformer<LocalDate, String> {
public LocalDate transformFrom(String input) {
return LocalDate.parse(input, DateTimeFormatter.ISO_LOCAL_DATE);
}
public String transformTo(LocalDate input) {
return input.format(DateTimeFormatter.ISO_LOCAL_DATE);
}
}
}

Is there any Date and Time picker available for javaFx?

I am new to Java and I have started developing applications in java using javaFx. searched a lot but couldn't find any date and time picker in javaFx. Even i tried JFxtras but its not working. By the way i am using javafx 2.2.3 and java 7. Any help would be highly appreciated.
Here is a Java version of the DateTimePicker control above, slightly improved.
This code is now part of TornadoFX Controls and you can have a look at the latest version of DateTimePicker.java in the GitHub Repo. The control is available in Maven Central as well under these coordinates:
<dependency>
<groupId>no.tornado</groupId>
<artifactId>tornadofx-controls</artifactId>
<version>1.0.3</version>
</dependency>
The implementation right now:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.DatePicker;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* A DateTimePicker with configurable datetime format where both date and time can be changed
* via the text field and the date can additionally be changed via the JavaFX default date picker.
*/
#SuppressWarnings("unused")
public class DateTimePicker extends DatePicker {
public static final String DefaultFormat = "yyyy-MM-dd HH:mm";
private DateTimeFormatter formatter;
private ObjectProperty<LocalDateTime> dateTimeValue = new SimpleObjectProperty<>(LocalDateTime.now());
private ObjectProperty<String> format = new SimpleObjectProperty<String>() {
public void set(String newValue) {
super.set(newValue);
formatter = DateTimeFormatter.ofPattern(newValue);
}
};
public DateTimePicker() {
getStyleClass().add("datetime-picker");
setFormat(DefaultFormat);
setConverter(new InternalConverter());
// Syncronize changes to the underlying date value back to the dateTimeValue
valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
dateTimeValue.set(null);
} else {
if (dateTimeValue.get() == null) {
dateTimeValue.set(LocalDateTime.of(newValue, LocalTime.now()));
} else {
LocalTime time = dateTimeValue.get().toLocalTime();
dateTimeValue.set(LocalDateTime.of(newValue, time));
}
}
});
// Syncronize changes to dateTimeValue back to the underlying date value
dateTimeValue.addListener((observable, oldValue, newValue) -> {
setValue(newValue == null ? null : newValue.toLocalDate());
});
// Persist changes onblur
getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue)
simulateEnterPressed();
});
}
private void simulateEnterPressed() {
getEditor().fireEvent(new KeyEvent(getEditor(), getEditor(), KeyEvent.KEY_PRESSED, null, null, KeyCode.ENTER, false, false, false, false));
}
public LocalDateTime getDateTimeValue() {
return dateTimeValue.get();
}
public void setDateTimeValue(LocalDateTime dateTimeValue) {
this.dateTimeValue.set(dateTimeValue);
}
public ObjectProperty<LocalDateTime> dateTimeValueProperty() {
return dateTimeValue;
}
public String getFormat() {
return format.get();
}
public ObjectProperty<String> formatProperty() {
return format;
}
public void setFormat(String format) {
this.format.set(format);
}
class InternalConverter extends StringConverter<LocalDate> {
public String toString(LocalDate object) {
LocalDateTime value = getDateTimeValue();
return (value != null) ? value.format(formatter) : "";
}
public LocalDate fromString(String value) {
if (value == null) {
dateTimeValue.set(null);
return null;
}
dateTimeValue.set(LocalDateTime.parse(value, formatter));
return dateTimeValue.get().toLocalDate();
}
}
}
The dateTimeValue property contains the value with time, and the valueProperty contains only the date value.
I have not added tests for this component yet, so the implementation might change, check GitHub for the latest version.
JFXtras project has a working version for JavaFX 2.2. Look for CalendarPicker, CalendarTimePicker, ... at the repo, under the 2.2 branch.
You can test it by downloading the lastest release (2.2-r6-SNAPSHOT) from jfxtras.org.
This short snippet will create a calendar for picking both date and time:
#Override
public void start(Stage primaryStage) {
CalendarPicker dateTime = new CalendarPicker();
dateTime.withCalendar(Calendar.getInstance());
dateTime.withShowTime(Boolean.TRUE);
dateTime.withLocale(Locale.ENGLISH);
dateTime.calendarProperty().addListener(new ChangeListener<Calendar>() {
#Override
public void changed(ObservableValue<? extends Calendar> ov, Calendar t, Calendar t1) {
System.out.println("Selected date: "+t1.getTime().toString());
}
});
StackPane root = new StackPane();
root.getChildren().add(dateTime);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Date & Time from JFXtras 2.2");
primaryStage.setScene(scene);
primaryStage.show();
}
I find it most convenient to enter the time via the keyboard instead of changing it with sliders. It's quite easy to extend the included DatePicker to look like this:
I also find it annoying that the DatePicker doesn't commit the edited value in the TextField onblur, so the following code fixes that as well.
The snippet is written in Kotlin for brevety, you can easily convert it to Java via IntelliJ IDEA:
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.control.DatePicker
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import javafx.scene.input.MouseEvent
import javafx.util.StringConverter
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
class DateTimePicker(val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : DatePicker() {
private var dateTimeValue = SimpleObjectProperty<LocalDateTime>(LocalDateTime.now())
init {
converter = object : StringConverter<LocalDate>() {
override fun toString(value: LocalDate?): String {
return if (dateTimeValue.get() != null) dateTimeValue.get().format(formatter) else ""
}
override fun fromString(value: String?): LocalDate? {
if (value == null) {
dateTimeValue.set(null)
return null
}
dateTimeValue.set(LocalDateTime.parse(value, formatter))
return dateTimeValue.get().toLocalDate()
}
}
// Syncronize changes to the underlying date value back to the dateTimeValue
valueProperty().addListener { observable, old, new ->
if (new == null) {
dateTimeValue.set(null)
} else {
if (dateTimeValue.get() == null) {
dateTimeValue.set(LocalDateTime.of(new, LocalTime.now()))
} else {
val time = dateTimeValue.get().toLocalTime()
dateTimeValue.set(LocalDateTime.of(new, time))
}
}
}
// Syncronize changes to dateTimeValue back to the underlying date value
dateTimeValue.addListener { observable, old, new ->
valueProperty().set(new?.toLocalDate())
}
// Persist changes onblur
editor.focusedProperty().addListener { observable, old, new ->
if (!new)
simulateEnterPressed()
}
}
private fun simulateEnterPressed() =
editor.fireEvent(KeyEvent(editor, editor, KeyEvent.KEY_PRESSED, null, null, KeyCode.ENTER, false, false, false, false))
fun dateTimeValueProperty() = dateTimeValue;
}
Bind your LocalDateTime property to the dateTimeValueProperty.
Slightly "improved" (at least for my needs) version that works with NullableTimeStamp... in order to be able to, well, null it (for ease with MySQL)...
Dunno if this can help anyone but here it is:
NullableTimeStamp:
public class NullableTimestamp extends Timestamp {
public NullableTimestamp() {
super(0L);
}
public NullableTimestamp(long value) {
super(value);
}
#Override
public String toString() {
return this.getTime() > 0L ? super.toString() : "";
}
public static NullableTimestamp valueOf(LocalDateTime localDateTime) {
return new NullableTimestamp(Timestamp.valueOf(localDateTime).getTime());
}
}
and DateTimePicker:
public class DateTimePicker extends DatePicker {
public static final String DefaultFormat = "yyyy-MM-dd HH:mm";
private DateTimeFormatter formatter;
private ObjectProperty<LocalDateTime> dateTimeValue = new SimpleObjectProperty<>(LocalDateTime.now());
private ObjectProperty<String> format = new SimpleObjectProperty<String>() {
public void set(String newValue) {
super.set(newValue);
formatter = DateTimeFormatter.ofPattern(newValue);
}
};
public DateTimePicker() {
getStyleClass().add("datetime-picker");
setFormat(DefaultFormat);
setConverter(new InternalConverter());
// Syncronize changes to the underlying date value back to the dateTimeValue
valueProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
dateTimeValue.set(null);
} else {
if (dateTimeValue.get() == null) {
dateTimeValue.set(LocalDateTime.of(newValue, LocalTime.now()));
} else {
LocalTime time = dateTimeValue.get().toLocalTime();
dateTimeValue.set(LocalDateTime.of(newValue, time));
}
}
});
// Syncronize changes to dateTimeValue back to the underlying date value
dateTimeValue.addListener((observable, oldValue, newValue) -> {
setValue(newValue == null ? null : newValue.toLocalDate());
});
// Persist changes onblur
getEditor().focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue)
simulateEnterPressed();
});
}
private void simulateEnterPressed() {
getEditor().fireEvent(new KeyEvent(getEditor(), getEditor(), KeyEvent.KEY_PRESSED, null, null, KeyCode.ENTER, false, false, false, false));
}
public LocalDateTime getDateTimeValue() {
return dateTimeValue.get();
}
public void setDateTimeValue(LocalDateTime dateTimeValue) {
if(dateTimeValue.isAfter(LocalDateTime.of(1971, 6, 30, 12, 00)))
this.dateTimeValue.set(dateTimeValue);
else
this.dateTimeValue.set(null);
}
public ObjectProperty<LocalDateTime> dateTimeValueProperty() {
return dateTimeValue;
}
public String getFormat() {
return format.get();
}
public ObjectProperty<String> formatProperty() {
return format;
}
public void setFormat(String format) {
this.format.set(format);
}
class InternalConverter extends StringConverter<LocalDate> {
public String toString(LocalDate object) {
LocalDateTime value = getDateTimeValue();
return (value != null) ? value.format(formatter) : "";
}
public LocalDate fromString(String value) {
if (value == null) {
dateTimeValue.set(null);
return null;
}
dateTimeValue.set(LocalDateTime.parse(value, formatter));
return dateTimeValue.get().toLocalDate();
}
}
}
It basically masks the 0L Timestamp value as if it was NULL... hope this can help cheers

JSF custom panel with button - action not invoked

I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.
Is there some kind of additional Interface necessary to pass the action to the embedded button-component?
Any help is very appreciated since I am stuck at this issue for some days now
MyClass:
public class MyClass extends UIPanel implements SystemEventListener
{
private UIForm form;
private HtmlCommandButton buttonOk;
public MyClass()
{
FacesContext context = getFacesContext();
UIViewRoot root = context.getViewRoot();
root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}
#Override
public void processEvent(SystemEvent event)
{
this.form = new UIForm();
this.buttonOk = new HtmlCommandButton();
this.buttonOk.setId("okButtonId");
this.buttonOk.setActionExpression(getAction());
this.buttonOk.setValue("OK");
this.form.getChildren().add(this.buttonOk);
getChildren().add(this.form);
}
private enum PropertyKeys
{
action, text, titel
}
public MethodExpression getAction()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}
public void setAction(MethodExpression actionExpression)
{
getStateHelper().put(PropertyKeys.action, actionExpression);
}
public String getText()
{
return (String) getStateHelper().eval(PropertyKeys.text);
}
public void setText(String text)
{
getStateHelper().put(PropertyKeys.text, text);
}
public String getTitel()
{
return (String) getStateHelper().eval(PropertyKeys.titel);
}
public void setTitel(String titel)
{
getStateHelper().put(PropertyKeys.titel, titel);
}
#Override
public void encodeAll(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.DIV_ELEM, this);
writer.writeText(getText(), null);
this.form.encodeAll(context);
writer.endElement(HTML.DIV_ELEM);
}
#Override
public void encodeChildren(FacesContext context) throws IOException
{
}
#Override
public boolean isListenerForSource(Object source)
{
return (source instanceof MyClass);
}
}
MyClassHandler:
public class MyClassHandler extends ComponentHandler
{
public MyClassHandler(ComponentConfig config)
{
super(config);
}
#SuppressWarnings("rawtypes")
#Override
protected MetaRuleset createMetaRuleset(Class type)
{
return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}
}
myView Method:
...
public String myMethod()
{
System.err.println("myMethod");
return "/some/path/yadayada.xhtml";
}
...
MyView.xhtml
<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />
Exdending UICommand is enough, since you only want one action to be executed.
You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.
public void decode(FacesContext contex)
{
Map<String,String> map = context.getExternalContext.getRequestParameterMap();
// your rendered buttons need a name you check for
final boolean okPressed = map.containsKey( getClientId + ":ok" );
final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
if(okPressed || cancelPressed)
{
MethodExpression exp = null;
if(okPressed)
{
exp = getActionOk();
}
else
{
exp = getActionCancel();
}
// redirect to standard action
setActionExpression(exp);
queueEvent(new ActionEvent(this));
}
}
In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.

Resources