Groovy SwingBuilder bind to multiple properties - binding

Is there a way to bind a properties to multiple properties of another object using the SwingBuilder? For example, I want to bind a button's enabled property to two text fields - the button is only enabled when both text fields are non empty.

You can do this sort of thing:
import groovy.beans.Bindable
import groovy.swing.SwingBuilder
import javax.swing.WindowConstants as WC
class CombinedModel {
#Bindable String text1
#Bindable String text2
}
def model = new CombinedModel()
SwingBuilder.build() {
frame(title:'Multiple Bind Test', pack:true, visible: true, defaultCloseOperation:WC.EXIT_ON_CLOSE ) {
gridLayout(cols: 2, rows: 0)
label 'Input text 1: '
textField( columns:10, id:'fielda' )
label 'Input text 2: '
textField( columns:10, id:'fieldb' )
// Bind our two textFields to our model
bean( model, text1: bind{ fielda.text } )
bean( model, text2: bind{ fieldb.text } )
label 'Button: '
button( text:'Button', enabled: bind { model.text1 && model.text2 } )
}
}
As you can see, that binds two textfields to fields in our model, then binds enabled for the button to be true if both text1 and text2 are non-empty

Related

Is it possible to add an option to the top of a select2 when data comes from ajax?

I need to insert at the beginning of the list a new option in the select2 control.
I tried with
var data = {
id: -1,
text: 'SISTEMA'
};
var newOption = new Option(data.text, data.id, false, false);
$('#UsuarioId').append(newOption).trigger('change');
But that does not work when data comes from Ajax. In that case, the combobox appears with that option selected and when list is expanded, that option is not there.
Regards
Jaime
Create a variable and initially define that variable as the option you want to include - eg:
var trHTML;
trHTML = '<option value=""></option>'
Then loop through your result set adding each item back to that variable
$.each(x, function (i, item) {
trHTML += '<option value=' + value_name +'>'+ display_name +'</option>';
});
Then append the entire list to the select, and initiate Select2
$('#dropdown_name').append(trHTML);
$('#dropdown_name').select2({
placeholder: "foobar",
allowClear: true
});
This documentation from select2 already explains
https://select2.org/data-sources/ajax

AG Grid / Angular 6 - trying to render a hyperlink with two columns

I'm trying to do this in my AG Grid (Angular 6):
have a column that shows the name of my object
but at the same time, that column should render this as a hyperlink to an edit page, using the Id of that object (not the name)
My current code snippet:
columnDefs = [
{
headerName: 'Name', field: 'Name', width: 125,
cellRenderer: function(params) {
return '' + params.value + '';
}
},
However, right now, all I can do is create a cell renderer, but since it's the cell renderer for the column of the Name column, I can only access that name - but I need the Id to build the hyperlink, which should point to /admin/edit/47 (or whatever the Id might be).
How can I accomplish this? What more do I need to do in order to be able to get both the Name (for display) as well as the Id in my cell renderer?
You can access it using params.data.Id where params.data points to your object bound to your record. So,
cellRenderer: function(params) {
return '' + params.value + '';
}
will give you the result you are expecting.

How do you add an initial selection for Angular Material Table SelectionModel?

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]);
}
});
}

Jquery UI autocomplete with value label pairs

I would like to set the source of autocomplete to the one that can have value/label pairs as described in their documentation here
http://api.jqueryui.com/autocomplete/
They say that
An array of objects with label and value properties: [ { label: "Choice1", value: "value1" }, ... ]
The label property is displayed in the suggestion menu. The value will
be inserted into the input element when a user selects an item. If
just one property is specified, it will be used for both, e.g., if you
provide only value properties, the value will also be used as the
label.
In my code I have
html
<input type="text" id="testBox" name="testBox" maxlength="70"/>
in my js
var catsArr = [];
for (var i = 0; i < 10; i++) {
var cat = {
label: "grey " + i,
value: i
};
catsArr.push(cat);
}
//tie the autocomplete to the box
$("#testBox").autocomplete({ source: catsArr });
But somehow, when the user starts typing the word grey, nothing shows up....What am I missing?

extjs4 grid - changing column editor per row basis

ExtJS4 grid anticipates appropriate editor (cellEditor or rowEditor) per column.
If a column's header field is dateField - date selector will be applied on every row in that column.
What I need is an editor with different field editors per row, not per column.
The Extjs3 solution is provided here - unfortunately doesn't fit in Extjs4 case.
(please check that link to see explanatory images, cause I can't post images yet)
There's also a single column solution called property grid, but again - it supports only one column and is very deviated from the standard Ext.grid component
I have tried manually changing grid editor by customizing column.field and reloading grid.editingPlugin.editor, but always get a blank rowEditor panel with no fields.
//by default rowEditor applies textField to all cells - I'm trying to force custom numberFiled on apropriate row
var numberField=Ext.form.field.Number();
grid.columns[0].field=numberField;
//destroy current rowEditor's instance
delete grid.editingPlugin.editor;
//now, upon doubleClick on apropriate cell it should reinitialize itself (initEditor()) - and it does, but is an empty panel
what am I missing here? once I delete editingPlugin.editor everything should start from the beginning like during the first time rowEditor is called, but it looses all the fields
Solution for Ext4:
I was looking for a solution for this and this guy said the property grid has this behavior.
I have adapted it to work in a clean way for me
on initComponent I declared:
this.editors = {
'date' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Date', {selectOnFocus: true})}),
'string' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Text', {selectOnFocus: true})}),
'number' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'int' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'boolean' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.ComboBox', {
editable: false,
store: [[ true, 'Sim' ], [false, 'Não' ]]
})})
};
I used these functions to help me (copied):
this.renderCell = function(val, meta, rec) {
var result = val;
if (Ext.isDate(val)) {
result = me.renderDate(val);
} else if (Ext.isBoolean(val)) {
result = me.renderBool(val);
}
return Ext.util.Format.htmlEncode(result);
};
this.getCellEditor = function(record, column) {
return this.editors[record.get('type')];
};
And finally, associate these functions to the column:
{text: "Valor", name : 'colunaValor', width: 75, sortable: true, dataIndex: 'valor', width:200,
renderer: Ext.Function.bind(this.renderCell, this),
getEditor: Ext.Function.bind(this.getCellEditor, this)
}

Resources