How to show rows-records between two tab form? - forms

In my Form, I have Two TabPages, with the same DataSource for each.
In TabPageA I have all records from my DataSource. I select records in TabPageA and I want to only show, in TabPageB, those records I selected previously in TabPageA.
If I don't select anything in TabPageA I don't see anything in TabPageB
For example if in my GridA I selected Record#4 , in GridB I see only Record#4.

I don't think you'll be able to filter TabPageB differently from TabPageA easily due to the fact they share the same DataSource.
There are two quick ways to modify this.
Keep it as one DataSource:
Create a global QueryBuildRange to easily update the range on selected records.
public class FormRun extends ObjectRun
{
QueryBuildRange qbr;
}
Override the init of your datasource. Here you will set the qbr:
public void init()
{
super();
qbr = this.queryBuildDataSource().addRange(fieldNum(SalesTable, RecId));
}
Then override the pageActivated TabPage method for each to set the range and re-execute the query to your liking:
TabPageA:
public void pageActivated()
{
super();
qbr.value('');
SalesTable_ds.executeQuery();
}
TabPageB
public void pageActivated()
{
super();
element.createRange();
SalesTable_ds.executeQuery();
}
Method to update range:
public void createRange()
{
MultiSelectionHelper multiSelectionHelper = MultiSelectionHelper::createFromCaller(this);
common common;
Set set = new Set(Types::Int64);
// Necessary if the datasource you want is not the header datasource.
multiSelectionHelper.parmDatasource(SalesTable_ds);
common = multiSelectionHelper.getFirst();
while (common)
{
// Use of set not necessary, but a little cleaner.
set.add(common.RecId);
common = MultiSelectionHelper.getNext();
}
qbr.value(strRem(set.toString(), '"{}'));
}
This is somewhat dirty, but you get the idea and could clean it up as needed.
Two Datasources:
Update the range on the second datasource (used by TabPageB) based on the multi-selection of the first datasource. But I'm assuming you only want one datasource for some reason.

Related

Joining datasources by name in AX 2012 form

I'm trying to join multiple datasources in a grid I created.
The grid is in CaseDetail form and it uses same tables as some other groups on it.
Therefore I have to use joins based on datasources names and not on tables names.
There is InventTable(InventTableComplaints) - parent and EcoResProductTranslation(EcoResProductTranslationComplaints) - child.
What I'm trying to do is to add this code to the child datasource:
public void executeQuery()
{
QueryBuildDataSource qbdsIT, qbdsERPTC;
qbdsIT = InventTableComplaint_DS.queryBuildDataSource();
qbdsERPTC = qbdsIT.addDataSource(tableNum(EcoResProductTranslation), "EcoResProductTranslationComplaint");
qbdsERPTC.addLink(fieldNum(InventTable, Product), fieldNum(EcoResProductTranslation, Product));
qbdsERPTC.joinMode( JoinMode::OuterJoin );
qbdsERPTC.fetchMode( QueryFetchMode::One2One );
super();
}
But it doesn't work.
Is it possible?
You do not define the table relations in the executeQuery method, do so in the init method instead which is executed exactly once. If you defined the datasource in the form (using InventTableComplaint as JoinSource and with OuterJoin as JoinMode), you do not need to do it in init method either, but you may need to define the link if not provided as table relations:
public void init()
{
qbdsIT = InventTableComplaint_DS.queryBuildDataSource();
qbdsERPTC = EcoResProductTranslationComplaint_ds.queryBuildDataSource();
qbdsERPTC.clearLinks();
qbdsERPTC.addLink(fieldNum(InventTable, Product), fieldNum(EcoResProductTranslation, Product));
super();
}
Beware that more than one record of may exist of EcoResProductTranslation for each InventTable (on for each language), so you may end out with "duplicates" of InventTable in the grid.

How to use BeanWrapperFieldSetMapper to map a subset of fields?

I have a Spring batch application where BeanWrapperFieldSetMapper is used to map fields using a prototype object. However, the CSV file that is being read (via a FlatFileItemReader) contains one (indicator) field that determines the mapping of another field. If the indicator field has a value of Y, then the value of the another field should be mapped to property foo otherwise it should be mapped to property bar.
I know that I can use a custom FieldSetMapper to do this, but then I have to code the mapping all of the other fields (of which there are a quite a few). Alternatively, I could do this post reading via an ItemProcessor but then my domain (prototype) object must have a property representing the indicator field (which I prefer not to do since it is not really part of the business domain).
Is it possible to perhaps use a custom FieldSetMapper to only map these custom fields and delegate the other mappings to BeanWrapperFieldSetMapper? Or is there some other better way to solve for this?
Here is my current attempt to use a custom FieldSetMapper and delegate to BeanWrapperFieldSetMapper:
public class DelegatedFieldSetMapper extends BeanWrapperFieldSetMapper<MyProtoClass> {
#Override
public MyProtoClass mapFieldSet(FieldSet fieldSet) throws BindException {
String indicator = fieldSet.readString("indicator");
Properties fieldProperties = fieldSet.getProperties();
if (indicator.equalsIgnoreCase("y")) {
fieldProperties.put("test.foo", fieldSet.readString("value");
} else {
fieldProperties.put("test.bar", fieldSet.readString("value");
}
fieldProperties.remove("indicator");
Set<Object> keys = fieldProperties.keySet();
List<String> names = new ArrayList<String>();
List<String> values = new ArrayList<String>();
for (Object key : keys) {
names.add((String) key);
values.add((String) fieldProperties.getProperty((String) key));
}
DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(names.toArray(new String[names.size()]), values.toArray(new String[values.size()]));
return super.mapFieldSet(domainObjectFieldSet);
}
}
However, a FlatFileParseException is thrown. The relevant parts of the batch config class are as follows:
#Configuration
#EnableBatchProcessing
public class BatchConfiguration {
#Value("${file}")
private File file;
#Bean
#Scope("prototype")
public MyProtoClass () {
return new MyProtoClass();
}
#Bean
public ItemReader<MyProtoClass> reader(LineMapper<MyProtoClass> lineMapper) {
FlatFileItemReader<MyProtoClass> flatFileItemReader = new FlatFileItemReader<MyProtoClass>();
flatFileItemReader.setResource(new FileSystemResource(file));
final int NUMBER_OF_HEADER_LINES = 1;
flatFileItemReader.setLinesToSkip(NUMBER_OF_HEADER_LINES);
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
#Bean
public LineMapper<MyProtoClass> lineMapper(LineTokenizer lineTokenizer, FieldSetMapper<MyProtoClass> fieldSetMapper) {
DefaultLineMapper<MyProtoClass> lineMapper = new DefaultLineMapper<MyProtoClass>();
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
#Bean
public LineTokenizer lineTokenizer() {
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setNames(new String[] {"value", "test.bar", "test.foo", "indicator"});
return lineTokenizer;
}
#Bean
public FieldSetMapper<MyProtoClass> fieldSetMapper(PropertyEditor emptyStringToNullPropertyEditor) {
BeanWrapperFieldSetMapper<MyProtoClass> fieldSetMapper = new DelegatedFieldSetMapper();
fieldSetMapper.setPrototypeBeanName("myProtoClass");
Map<Class<String>, PropertyEditor> customEditors = new HashMap<Class<String>, PropertyEditor>();
customEditors.put(String.class, emptyStringToNullPropertyEditor);
fieldSetMapper.setCustomEditors(customEditors);
return fieldSetMapper;
}
Finally, the CSV flat file look like this:
value,bar,foo,indicator
abc,,,y
xyz,,,n
Let's say that BatchWorkObject is the class to be mapped.
Here's a sample code in Spring Boot style that needs only your custom logic to be added.
new BeanWrapperFieldSetMapper<BatchWorkObject>(){
{
this.setTargetType(BatchWorkObject.class);
}
#Override
public BatchWorkObject mapFieldSet(FieldSet fs)
throws BindException {
BatchWorkObject tmp= super.mapFieldSet(fs);
// your custom code here
return tmp;
}
});
The code actually accomplishes what is desired except for one issue that results in the FlatFileParseException. The DelegatedFieldSetMapper contains the issue as follows:
DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(names.toArray(new String[names.size()]), values.toArray(new String[values.size()]));
To resolve, change to:
DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(values.toArray(new String[values.size()]), names.toArray(new String[names.size()]));
Write your own FieldSetMapper with a set of prepared delegates inside.
Those delegates are pre-built for every different kind of fields mapping.
In your object route to correct delegate based on indicator field (with a Classifier, for example).
I can't see any other way, but this solution is quite easy and straightforward to maintain.
Processing based on the input format/data can be done using a custom implementation of ItemProcessor which is either changing values in the same entity (that was populated by IteamReader) or creates a new one output entity.

PropertyAcess : Define condition values for label in PropertyAcess

I have a model called Field which has id and label.
I have defined PropertyAcess as below and it works. I would like to change it in such a way that I can show label based on condition ie if field.getLabel() is null, use field.getId() as label. How can I acheieve that
interface FieldProperties extends PropertyAccess<Field> {
ModelKeyProvider<Field> id();
LabelProvider<Field> label();
#Path("label")
ValueProvider<Field, String> labelProp();
}
Thanks
The PropertyAccess tool is meant to make it easy to quickly build ValueProvider, ModelKeyProvider, and LabelProvider instances that are based on a specific getter/setter on a bean-like object. If you don't want just access to a single property, then implement the interface directly.
In your case, since you want a LabelProvider that returns getLabel() unless it is null, then getId(), you might do something like this:
public LabelOrIdLabelProvider implements LabelProvider<Field> {
#Override
public String getLabel(Object item) {
return item.getLabel() == null ? item.getId() : item.getLabel();
}
}
If you want custom behavior, build it out yourself to do exactly what you need. If you just want the simple behavior of reading a single getter, the PropertyAccess is there to help save you a few lines of code.

GWT - How to add more than 1 CellTables on the same page

I have a page using StackLayoutPanel.
In that 3 stacks requires separate CellTables.
Currently events I need to use are RangeChangeEvent, SelectionChangeEvent.
Now question is how to differentiate OnRangeChangeEvent from one another.
The source table is referenced by the event that you are catching. "event.getSource()"
always gives a reference to the object that caused the event.
For example, if you have three tables, and you attach the same handler as below:
RangeChangeEvent.Handler handler = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
if(table1 == event.getSource()){
// first table
} else if (table2 == event.getSource()){
// second table
} else if (table3 == event.getSource()){
// third table
}
};
table1.addRangeChangeHandler(handler);
table2.addRangeChangeHandler(handler);
table3.addRangeChangeHandler(handler);
The above example assumes that there is no selection model specified on the tables. If there is, the selection model will be the source of the events.
Alternatively, you can just attach a table-specific handler to each table:
RangeChangeEvent.Handler handler1 = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
// Handle stuff happening to table 1
}
};
RangeChangeEvent.Handler handler2 = new RangeChangeEvent.Handler() {
#Override
public void onRangeChange(RangeChangeEvent event) {
// Handle stuff happening to table 2
}
};
table1.addRangeChangeHandler(handler1);
table2.addRangeChangeHandler(handler2);
// And so on for any more tables
With this approach you won't need to worry about the event source, as you already know which handler corresponds to which table.
Were you setting the same event handlers for each CellTable? If so, don't. You can simply make a different handler for each CellTable. While it would theoretically be possible to detect the source as in filip-fku's example, it won't be if you are using SelectionModel.
Bottom line: you should not try to use the same handlers on multiple objects unless you absolutely have to.

GWT RequestFactory + CellTable

Does anyone know for an example of GWT's CellTable using RequestFactory and that table is being edited? I would like to list objects in a table (each row is one object and each column is one property), be able to easily add new objects and edit. I know for Google's DynaTableRf example, but that one doesn't edit.
I searched Google and stackoverflow but wasn't able to find one. I got a bit confused with RF's context and than people also mentioned some "driver".
To demonstrate where I currently arrived, I attach code for one column:
// Create name column.
Column<PersonProxy, String> nameColumn = new Column<PersonProxy, String>(
new EditTextCell()) {
#Override
public String getValue(PersonProxy person) {
String ret = person.getName();
return ret != null ? ret : "";
}
};
nameColumn.setFieldUpdater(new FieldUpdater<PersonProxy, String>() {
#Override
public void update(int index, PersonProxy object, String value) {
PersonRequest req = FaceOrgFactory.getInstance().requestFactory().personRequest();
PersonProxy eObject = req.edit(object);
eObject.setName(value);
req.persist().using(eObject).fire();
}
});
and my code for data provider:
AsyncDataProvider<PersonProxy> personDataProvider = new AsyncDataProvider<PersonProxy>() {
#Override
protected void onRangeChanged(HasData<PersonProxy> display) {
final Range range = display.getVisibleRange();
fetch(range.getStart());
}
};
personDataProvider.addDataDisplay(personTable);
...
private void fetch(final int start) {
lastFetch = start;
requestFactory.personRequest().getPeople(start, numRows).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
if (lastFetch != start){
return;
}
int responses = response.size();
if (start >= (personTable.getRowCount()-numRows)){
PersonProxy newP = requestFactory.personRequest().create(PersonProxy.class);
response.add(newP);
responses++;
}
personTable.setRowData(start, response);
personPager.setPageStart(start);
}
});
requestFactory.personRequest().countPersons().fire(new Receiver<Integer>() {
#Override
public void onSuccess(Integer response) {
personTable.setRowCount(response+1, true);
}
});
}
I try to insert last object a new empty object. And when user would fill it, I'd insert new one after it. But the code is not working. I says that user is "attempting" to edit a object previously edited by another RequestContext.
Dilemmas:
* am I creating too many context'es?
* how to properly insert new object into celltable, created on the client side?
* on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
Thanks for any help.
am I creating too many context'es?
Yes.
You should have one context per HTTP request (per fire()), and a context that is not fire()d is useless (only do that if you/the user change your/his mind and don't want to, e.g., save your/his changes).
You actually have only one context to remove here (see below).
Note that your approach of saving on each field change can lead to "race conditions", because a proxy can be edit()ed by at most one context at a time, and it remains attached to a context until the server responds (and once a context is fired, the proxy is frozen –read-only– also until the server responds).
(this is not true in all cases: when onConstraintViolation is called, the context and its proxies are unfrozen so you can "fix" the constraint violations and fire the context again; this should be safe because validation is done on the server-side before any service method is called).
how to properly insert new object into celltable, created on the client side?
Your code looks OK, except that you should create your proxy in the same context as the one you'll use to persist it.
on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
I'm not 100% certain but I think you should refresh the table (something like setRowData(index, Collections.singletonList(object)))
BTW, the driver people mention is probably the RequestFactoryEditorDriver from the Editor framework. It won't help you here (quite the contrary actually).