Working with OSelection Field Widget

Odoo Mobile OSelection Field control

Dharmang Soni

OSelection (Selection in odoo) field

Store text in database but propose a selection widget in UI

Here, OSelection works with Selection field of odoo which contains list of tuple in key value pair. Key stored in database but user can have some fixed list of values to select from UI.

To perform this UI operation, Odoo Mobile has OSelection widget property works with OField control widget.

It can be based on model column or can be generated at runtime for dummy column by providing array of strings identified by its index.

Rember that, OField auto initialized with OForm widget control if column exists in given model. Yes, you can also create your own column with selection type even if it is not present in your model. 

Selection field with model column and OForm widget:

OColumn state = new OColumn("State", OSelection.class)
        .addSelection("draft","Draft")
        .addSelection("confirm","Confirmed")
        .addSelection("close","Canceled")
        .addSelection("done","Done");
Here, OColumn contains different states with selection type and its value. addSelection method is chaining method so you can add your selection pair at creation time of column.
Remeber, The type of column must be OSelection (which store text in database actually but used for generating UI with possible selection options)
At your UI XML file Add field under your OForm widget as shown below:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:prefix="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <odoo.controls.OForm
                android:id="@+id/partnerForm"
                prefix:modelName="res.partner"
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:layout_height="wrap_content">

                <!-- OFields controls here //-->

        </odoo.controls.OForm>

</RelativeLayout>
Add OField and provide name :
<odoo.controls.OField
        app:fieldName="state"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">
</odoo.controls.OField>
The final code looks like:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:prefix="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <odoo.controls.OForm
                android:id="@+id/partnerForm"
                prefix:modelName="res.partner"
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:layout_height="wrap_content">

                
                <odoo.controls.OField
                    prefix:fieldName="state"
                    android:layout_height="wrap_content"
                    android:layout_width="match_parent">
                </odoo.controls.OField>

        </odoo.controls.OForm>

</RelativeLayout>
Now, initialized partner form will auto load each of states 
OForm form  = (OForm) view.findViewById(R.id.partnerForm);
form.initForm(null);
form.setEditable(true);

Adding custom selection field in xml layout:

Yes, you can create your own custom selection field or any other field in xml layout under OForm even there is no column exists for it in model. But for that, you need to add all properties required to create OField with your requirement. 

Here we are creating one Selection field with some of pre defined languages list.

Add Field with following property:

<odoo.controls.OField
    app:showIcon="false"
    app:controlLabel="Language"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:fieldType="Selection"
    app:defaultValue="1"
    app:valueArray="@array/arr_languages"
    app:fieldName="languages"></odoo.controls.OField>

valueArray is string array resource as below:

<string-array name="arr_languages">
    <item>English</item>
    <item>France</item>
    <item>Hindi</item>
    <item>Japaneses</item>
    <item>Sanskrit</item>
</string-array>

And output will be as shown in figure: 

Field value change listener and updating value at runtime:

Value chage listener:
For handling OField value change event, give ID to field and add setOnValueChangeListener as shown below:
OField languages = (OField) form.findViewById(R.id.fieldLanguages);
languages.setOnValueChangeListener(this);
It will implement onFieldValueChange method :
@Override
public void onFieldValueChange(OField field, Object value) {
    if (field.getFieldName().equals("languages")) {
        ODataRow item = (ODataRow) value;
        Toast.makeText(getContext(), "Language Selected: " + item.getString("name"),
                Toast.LENGTH_LONG).show();
    }
}
If you add value change listener to more than one field, you can identify each of the field by getting field name. 
Here, value contains Object of primitive type or ODataRow.
If your field works with relation column or selection column, note that it will return ODataRow object with _id, name and other properties or record.
It will triggered when value change for the field.
You can also make it editable or readonly at runtime by setting property setEditable(boolean) 

Getting value from OForm 

For getting each field value from OForm just call getValues() method of OForm

Note that getValues() can return null if there are any required field and not filled with value.

@Override
public void onClick(View v) {
    OValues values = form.getValues();
    Toast.makeText(getContext(), values + "", Toast.LENGTH_LONG).show();
}

Changing value at runtime : 

It's easy to update field values at runtime. By just providing ODataRow with new value updates field value
ODataRow row = new ODataRow();
row.put("name", "Runtime name");
row.put("state", "done");
row.put("languages", "2");
form.setData(row);

Tip: 

If you use string array for your custom selection field, it will give you number i.e, 1, 2 by its' positions. Instead, if you want to create custom field but don't want to check (sync) with server you can create local column by setting flag to local.

OColumn languages = new OColumn("Languages", OSelection.class)
        .setLocalColumn()
        .addSelection("en", "English")
        .addSelection("hi", "Hindi")
        .addSelection("fr", "Franch");

It will work same as other columns but never synced with server data columns. Also it will store the value user select to its record.