I have a problem with the Create field vaadin table, nesesito define a field in a table where you can enter text, for that the Create field, each time a new row agrege this field should have focus so I did something like this:
public Field createField(Container container, Object itemId,
Object propertyId, Component uiContext) {
final TextField f=new TextField();
f.setVisible(true);
f.addStyleName("txtronly-textfield");
if((propertyId=="Cantidad"))
{
f.setStyleName("centerTxt");
f.setReadOnly(true);
f.setRequired(true);
f.setMaxLength(9);
f.focus();
}
return f;
}
Now when I insert an item use addItemAt (0, item) to be inserted top, but the focusing field is the last in the table. I can not find why this happens if anyone can help?
Your code within your if-block will never be executed because the if-statement is never true.
Always compare Strings with the equals method.
Try the following:
if((propertyId.equals("Cantidad")))
{
...
}
Related
I am using BeanItemContainer for my Grid. I want to get a unique list of one of the properties. For instance, let's say my beans are as follows:
class Fun {
String game;
String rules;
String winner;
}
This would display as 3 columns in my Grid. I want to get a list of all the unique values for the game property. How would I do this? I have the same property id in multiple different bean classes, so it would be nice to get the values directly from the BeanItemContainer. I am trying to avoid building this unique list before loading the data into the Grid, since doing it that way would require me to handle it on a case by case basis.
My ultimate goal is to create a dropdown in a filter based on those unique values.
There isn't any helper for directly doing what you ask for. Instead, you'd have to do it "manually" by iterating through all items and collecting the property values to a Set which would then at the end contain all unique values.
Alternatively, if the data originates from a database, then you could maybe retrieve the unique values from there by using e.g. the DISTINCT keyword in SQL.
In case anyone is curious, this is how I applied Leif's suggestion. When they enter the dropdown, I cycle through all the item ids for the property id of the column I care about, and then fill values based on that property id. Since the same Grid can be loaded with new data, I also have to "clear" this list of item ids.
filterField.addFocusListener(focus->{
if(!(filterField.getItemIds() instanceof Collection) ||
filterField.getItemIds().isEmpty())
{
BeanItemContainer<T> container = getGridContainer();
if( container instanceof BeanItemContainer && getFilterPropertyId() instanceof Object )
{
List<T> itemIds = container.getItemIds();
Set<String> distinctValues = new HashSet<String>();
for(T itemId : itemIds)
{
Property<?> prop = container.getContainerProperty(itemId, getFilterPropertyId());
String value = null;
if( prop.getValue() instanceof String )
{
value = (String) prop.getValue();
}
if(value instanceof String && !value.trim().isEmpty())
distinctValues.add(value);
}
filterField.addItems(distinctValues);
}
}
});
Minor point: the filterField variable is using the ComboBoxMultiselect add-on for Vaadin 7. Hopefully, when I finally have time to convert to Vaadin 14+, I can do something similar there.
The Angular Material documentation gives a nice example for how to add selection to a table (Table Selection docs). They even provide a Stackblitz to try it out.
I found in the code for the SelectionModel constructor that the first argument is whether there can be multiple selections made (true) or not (false). The second argument is an array of initially selected values.
In the demo, they don't have any initially selected values, so the second argument in their constructor (line 36) is an empty array ([]).
I want to change it so that there is an initially selected value, so I changed line 36 to:
selection = new SelectionModel<PeriodicElement>(true, [{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}]);
This changes the checkbox in the header to an indeterminate state (as expected), but does not cause the row in the table to be selected. Am I setting the initial value incorrectly, or what am I missing here? How can I set an initially selected value?
Tricky one. You need to initialize the selection by extracting that particular PeriodicElement object from your dataSource input, and passing it to the constructor.
In this particular case, you could code
selection = new SelectionModel<PeriodicElement>(true, [this.dataSource.data[1]);
It's because of the way SelectionModel checks for active selections.
In your table markup you have
<mat-checkbox ... [checked]="selection.isSelected(row)"></mat-checkbox>
You expect this binding to mark the corresponding row as checked. But the method isSelected(row) won't recognize the object passed in here as being selected, because this is not the object your selection received in its constructor.
"row" points to an object from the actual MatTableDataSource input:
dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
But the selection initialization:
selection = new SelectionModel<PeriodicElement>(true, [{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}]);
happens with a new object you create on the fly. Your selection remembers THIS object as a selected one.
When angular evaluates the bindings in the markup, SelectionModel internally checks for object identity. It's going to look for the object that "row" points to in the internal set of selected objects.
Compare to lines 99-101 and 16 from the SelectionModel source code:
isSelected(value: T): boolean {
return this._selection.has(value);
}
and
private _selection = new Set<T>();
I was facing the same issue, I used dataSource to set the initial value manually in ngOnInit()
ngOnInit() {
this.dataSource.data.forEach(row => {
if (row.symbol == "H") this.selection.select(row);
});
}
If you do the following, it works too
selection = new SelectionModel<PeriodicElement>(true, [ELEMENT_DATA[1]])
To select all you can do
selection = new SelectionModel<PeriodicElement>(true, [...ELEMENT_DATA])
I hope the answer is helpful
Or more dynamically if you have a set of values and you want to filter them before:
selection = new SelectionModel<PeriodicElement>(true, [
...this.dataSource.data.filter(row => row.weight >= 4.0026)
]);
This gets more tricky if you have data loading asynchronously from an api. Here is how I did it:
Firstly I have implemented the DataSource from "#angular/cdk/table". I also have an RxJS Subject that fires whenever data is loaded (first time or when user changes page in the pagination section)
export abstract class BaseTableDataSource<T> implements DataSource<T>{
private dataSubject = new BehaviorSubject<T[]>([]);
private loadingSubject = new BehaviorSubject<boolean>(false);
private totalRecordsSubject = new BehaviorSubject<number>(null);
public loading$ = this.loadingSubject.asObservable();
public dataLoaded$ = this.dataSubject.asObservable();
public totalRecords$ = this.totalRecordsSubject.asObservable().pipe(filter(v => v != null));
constructor(){}
connect(collectionViewer: CollectionViewer): Observable<T[]>{
return this.dataSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.dataSubject.complete();
this.loadingSubject.complete();
this.totalRecordsSubject.complete();
}
abstract fetchData(pageIndex, pageSize, ...params:any[]) : Observable<TableData<T>>;
abstract columnMetadata(): {[colName: string]: ColMetadataDescriptor };
loadData(pageIndex, pageSize, params?:any[]): void{
this.loadingSubject.next(true);
this.fetchData(pageIndex, pageSize, params).pipe(
finalize(() => this.loadingSubject.next(false))
)
.subscribe(data => {
this.totalRecordsSubject.next(data.totalNumberOfRecords);
this.dataSubject.next(data.records)
});
}
}
Now when I want to pre-select a row, I can write a function like this in my component which hosts a table that uses an implementation of the above mentioned data source
selectRow(rowSelectionFn: (key: string) => boolean){
this.dataSource.dataLoaded$.pipe(takeUntil(this.destroyed$))
.subscribe(data => {
const foundRecord = data.filter(rec => rowSelectionFn(rec));
if(foundRecord && foundRecord.length >= 0){
this.selection.toggle(foundRecord[0]);
}
});
}
I have vaadin table with multiple columns. One of the column is of label class
container.addContainerProperty(ID_COLUMN, Label.class, "");
and I fill it up like
referenceItem.getItemProperty(ID_COLUMN).setValue(new Label(new Integer(reference.getId()).toString()));
When I sort the table by clicking on this table, it sorts the data like
1
10
100
2
200
7
So, I tried to change the class to Integer, then it works fine, but I get the numbers with comma like
1
2
...
..
7,456
8,455
How can I have the data sorted numerically and no commas.
I was able to figure out. I used Integer as class for my column and used following
referenceTable = new Table()
{
#Override
protected String formatPropertyValue(final Object a_row_id, final Object a_col_id, final Property<?> a_property)
{
if (a_property.getType() == Integer.class && null != a_property.getValue())
{
DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(getLocale());
df.applyLocalizedPattern("#0");
return df.format(a_property.getValue());
}
return super.formatPropertyValue(a_row_id, a_col_id, a_property);
}
};
It has been a while since i have been having fun with Vaadin Table.
There are property formatters, generators etc... stuff but in this case it might be easiest just to:
container.addContainerProperty(ID_COLUMN, String.class, "");
referenceItem.getItemProperty(ID_COLUMN).setValue(""+reference.getId());
My model contains an array of zip code items (IEnumerable<SelectListItem>).
It also contains an array of selected zip codes (string[]).
In my HTML page, I want to render each selected zip code as a drop down with all the zip code options. My first attempt did not work:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions )
}
I realized that although that would produce drop downs with the right "name" attribute, it wouldn't know which element of ZipCodes holds the value for that particular box, and might just default to the first one.
My second attempt is what really surprised me. I explicitly set the proper SelectListItem's Selected property to true, and it still rendered a control with nothing selected:
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes", Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
There, it's returning a new IEnumerable<SelectListitem> that contains all the original items, unless it's the selected item, in which case that element is a new SelectListItem with it's Selected property set to true. That property is not honored at all in the final output.
My last attempt was to try to use an explicit index on the string element I wanted to use as the value:
#{int zipCodeIndex = 0;}
#foreach (var zip in Model.ZipCodes) {
Html.DropDownList( "ZipCodes[" + (zipCodeIndex++) + "]", Model.ZipCodeOptions )
}
That doesn't work either, and probably because the name is no longer "ZipCodes", but "ZipCodes[x]". I also received some kind of read-only-collection error at first and had to change the type of the ZipCodes property from string[] to List<string>.
In a forth attempt, I tried the following:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions )
}
That produces controls with id like "ZipCodes_1_" and names like "ZipCodes[1]", but does not select the right values. If I explicitly set the Selected property of the right item, then this works:
#for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
var zip = Model.ZipCodes[zipCodeIndex];
Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}
However, the problem with that approach is that if I add a new drop downs in JavaScript and give them all the name "ZipCodes", then those completely override all the explicitly indexed ones, which never make it to the server. It doesn't seem to like mixing the plain "ZipCodes" name with explicit array elements "ZipCodes[1]", even though they map to the same variable when either is used exclusively.
In the U.I., user's can click a button to add a new drop down and pick another zip code. They're all named ZipCodes, so they all get posted to the ZipCodes array. When rendering the fields in the loop above, I expect it to read the value of the property at the given index, but that doesn't work. I've even tried remapping the SelectListItems so that the proper option's "Selected" property is true, but it still renders the control with nothing selected. What is going wrong?
The reason you first 2 snippets do not work is that ZipCodes is a property in your model, and its the value of your property which determines what is selected (not setting the selected value in the SelectList constructor which is ignored). Since the value of ZipCodes is an array of values, not a single value that matches one of the option values, a match is not found and therefore the first option is selected (because something has to be). Note that internally, the helper method generates a new IEnumerable<SelectListItem> based on the one you provided, and sets the selected attribute based on the model value.
The reason you 3rd and 4th snippets do not work, is due to a known limitation of using the DropDownListFor() method, and to make it work, you need to use an EditorTemplate and pass the SelectList to the template using AdditionalViewData, or construct a new SelectList in each iteration of the loop (as per your last attempt). Note that all it needs to be is
for(int i = 0; i < Model.ZipCodes.Length; i++)
{
#Html.DropDownListFor(m => m.ZipCodes[i],
new SelectList(Model.ZipCodeOptions, "Value", "Text", Model.ZipCodes[i]))
}
If you want to use just a common name (without indexers) for each <select> element using the DropDownList() method, then it needs to be a name which does not match a model property, for example
foreach(var item in Model.ZipCodes)
{
#Html.DropDownList("SelectedZipCodes",
new SelectList(Model.ZipCodeOptions, "Value", "Text", item))
}
and then add an additional parameter string[] SelectedZipCodes in you POST method to bind the values.
Alternatively, use the for loop and DropDownListFor() method as above, but include a hidden input for the indexer which allows non-zero based, non consecutive collection items to be submitted to the controller and modify you script to add new items using the technique shown in this answer
Note an example of using the EditorTemplate with AdditionalViewData is shown in this answer
I have a listview that I fill from an Adapter. My original code the data was being returned from a table, but now I need to get the code from a query with a join so the examples I used will no longer work and I haven't been able to find out how to use a query for this. I'm using an ORMrepository.
In my ORMrepository I have this function
public IList<Coe> GetmyCoe()
{
using (var database = new SQLiteConnection(_helper.WritableDatabase.Path))
{
string query = "SELECT Coe.Id, Adult.LName + ', ' + Adult.MName AS Name, Coe.Createdt FROM Adult INNER JOIN Coe ON Adult.CoeMID = Coe.Id";
return database.Query<Coe>(query);
}
}
which actually returns the data I want.
then in my Activity page I have this.
_list = FindViewById<ListView>(Resource.Id.List);
FindViewById<ListView>(Resource.Id.List).ItemClick += new System.EventHandler<ItemEventArgs>(CoeList_ItemClick);
var Coe = ((OmsisMobileApplication)Application).OmsisRepository.GetmyCoe();
_list.Adapter = new CoeListAdapter(this, Coe);
My Adapter page is where I have the problem, I know it is set up to to looking at a table which I'm not doing anymore. But I don't know how to change it for what I'm passing into it now. Current CoeListAdapter is:
public class CoeListAdapter : BaseAdapter
{
private IEnumerable<Coe> _Coe;
private Activity _context;
public CoeListAdapter(Activity context, IEnumerable<Coe> Coe)
{
_context = context;
_Coe = Coe;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = (convertView
?? _context.LayoutInflater.Inflate(
Resource.Layout.CoeListItem, parent, false)
) as LinearLayout;
var Coe = _Coe.ElementAt(position);
view.FindViewById<TextView>(Resource.Id.CoeMID).Text = Coe.Id.ToString();
//view.FindViewById<TextView>(Resource.Id.GrdnMaleName).Text = Coe.Name;
view.FindViewById<TextView>(Resource.Id.CreateDt).Text = Coe.CreateDt;
return view;
}
public override int Count
{
get { return _Coe.Count(); }
}
public Coe GetCoe(int position)
{
return _Coe.ElementAt(position);
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return position;
}
}
How do I set up the CoeListAdapter.cs page so that it can use the passed in data. As you can see I have a commented out lines where I fill a TextView which error because Coe.Name is not in the table model for Coe. but it is returned in the query. I believe my problem is IEnumerable but what do I change it to. I'm new to Mobile developement and suing VS2010 for Mono
The problem probably lies with the binding/mapping of the object not the creation of the view.
Or probably more specifically, the query.
Adult.LName + ', ' + Adult.MName AS Name
this should be:
Adult.LName || ', ' || Adult.MName AS Name
See also: String concatenation does not work in SQLite
From the sqlite docs: http://www.sqlite.org/lang_expr.html under the Operators heading:
The unary operator + is a no-op. It can be applied to strings,
numbers, blobs or NULL and it always returns a result with the same
value as the operand.
Note that there are two variations of the equals and not equals
operators. Equals can be either = or ==. The non-equals operator can
be either != or <>. The || operator is "concatenate" - it joins
together the two strings of its operands. The operator % outputs the
value of its left operand modulo its right operand.
The result of any binary operator is either a numeric value or NULL,
except for the || concatenation operator which always evaluates to
either NULL or a text value.
This shows that the + will evaluate to zero. If you use ||, the value will either be the correct value or NULL (if either of Adult.LName or Adult.MName is NULL).
This can be fixed by:
coalesce(first, '') || ', ' || coalesce(second, '')
but this may result in , LastName or FirstName,.
Another way would be to create another two properties in Coe called LName and MName.
Then bind the values to those properties and use the Name property like this:
public string Name
{
get { return string.Join(", ", LName, MName); }
}
This will probably be better as you can change how the Name appears especially if there are different combinations of First, Middle and Last names in different places.
And off topic:
I believe my problem is IEnumerable...
This is probably not too true as it returns the correct values. A better way would be to use IList as IEnumerable will iterate through the list each time to get the item as it does not know that the collection is actually a list. (I think)
thanks for the help on the concantination, I did find that was wrong, I did fix my problem, I was using an example by Greg Shackles on how to set up using a data base. what I had to do was create a new model with the elements I was wanting. So I created a new Model and called it CoeList, then everywhere I had List or IEnumerable I changed it to List or IEnumerable and it worked.