Pemrograman Javascript pada Odoo (Bagian Dua) – Logic Widget

Pada tutorial bagian pertama pada series tutorial pemrograman javascript pada odoo ini, saya telah membahas bagaimana membuat widget sederhana, yaitu sebuah widget yang hanya menampilkan sebuah pesan singkat kepada user. Pada bagian kedua ini saya akan membahas bagaimana menambah logic kepada widget yang telah kita buat.

Untuk mengikuti tutorial ini pastikan anda sudah membuat sebuah field dengan tipe integer seperti pada kode di bawah ini.

field_one = fields.Integer('Field One')

Saat anda membuat sebuah field dengan tipe integer saat anda menambahkannya ke sebuah file xml/view, odoo secara otomatis akan merender nilai dari field itu dalam sebuah element input pada mode edit, sehingga user bisa mengubah nilainya secara langsung pada element input tadi. Pada tutorial ini kita akan mencoba untuk mengubah tampilan field integer yang awalnya bisa diubah nilainya dengan bebas oleh user, menjadi sedikit lebih dibatasi, yaitu dengan menambah 2 buah tombol. Tombol untuk mengurangi nilai dan tombol + untuk menambah nilai field interger tersebut. Jadi kalau user ingin mengubah nilai field integer, user harus mengklik tombol atau tombol +, tidak lagi bisa diketik dengan bebas.

Langkah pertama, mari kita ubah template WidgetOneTemplate dari tutorial bagian pertama agar menampilkan dua buah tombol (– dan +) dan sebuah field input yang disabled pada mode edit. Lalu hanya menampilkan nilai di mode readonly tanpa 2 buah tombol.

<?xml version="1.0" encoding="UTF-8"?>
<template>

    <t t-name="WidgetOneTemplate">
        <div>
            <t t-if="widget.mode == 'edit' ">
                <div class="input-group">
                    <div class="input-group-prepend">
                        <button class="btn btn-danger btn-minus"> - </button>
                    </div>
                    <input type="text" class="form-control" t-att-value="widget.value" disabled="disabled" />
                    <div class="input-group-append">
                        <button class="btn btn-success btn-plus"> + </button>
                    </div>
                </div>
            </t>
            <t t-if="widget.mode == 'readonly' ">
                <span t-esc="widget.value" />
            </t>
        </div>
    </t>

</template>

Pada mode edit tampilannya akan seperti pada gambar di bawah ini.

Widget custom odoo dua buah tombol dan sebuah element input

Kemudian pada file widget_one.js mari kita tambahkan event untuk setiap tombol, seperti pada kode di bawah ini.

var WidgetOne = AbstractField.extend({
    template: 'WidgetOneTemplate', // isi nama template yang telah dibuat untuk mengatur tampilan/view widget
    events: { // daftar event, mirip event di jquery
        'click .btn-minus': 'btn_minus_action',
        'click .btn-plus': 'btn_plus_action',
    },
    btn_minus_action: function(){
        var new_value = this.value - 1;
        this._setValue(new_value.toString());
        console.log(this.value);
    },
    btn_plus_action: function(){
        var new_value = this.value + 1;
        this._setValue(new_value.toString());
        console.log(this.value);
    },
});

Event pada odoo hampir mirip dengan event pada jquery, hanya saja penulisannya dibalik. Pada jquery jika kita ingin menulis event click pada tombol dengan class btn-minus kita bisa menulisnya seperti ini.

$('.btn-minus.').click(btn_minus_action);

Sebenarnya kita bisa menggunakan kode jquery di atas pada widget odoo, tapi untuk saat ini lebih baik kita menggunakan kode yang pertama, penggunaan jquery pada widget odoo akan di bahas di lain waktu jika ada kesempatan.

Untuk mengubah value/nilai suatu field kita bisa menggunakan method this._setValue(new_value), tetapi ada beberapa hal yang perlu anda perhatikan mengenai tipe data dari variabel new_value ini, tegantung tipe dari field yang menggunakan widget yang kita buat. Sebagai contoh, pada field dengan tipe integer variabel yang kita lewatkan pada method this._setValue() harus berupa string. Jika berupa number malah akan error. Aneh ? Tidak juga. Karena secara default pada field dengan tipe integer kita bisa input dengan nilai 123,456.78 yang tentu saja berupa string. Dan odoo tetap bisa mengolah nilai tersebut sebagai number. Karena tanpa kita sadari odoo akan mem-parsing variabel new_value yang berupa string menjadi number sehingga bisa diolah dalam operasi penjumlahan,pengurangan dll, walaupun di view-nya tertulis 123,456.78 yang tentu saja bukan nilai number yang valid di javascript. Oleh karena itu kita harus mengubah variabel new_value yang awalnya berupa number menjadi string dengan method toString().

Restart service odoo anda, kemudian refresh browser, mari kita lihat logic yang kita tambahkan sudah berhasil jalan atau tidak.

Widget custom odoo menghasilkan value yang salah

Ternyata masih belum berjalan. Di console nilainya sudah berubah tapi di user interface nilainya masih 0. Kenapa ?

Ternyata saat value dari suatu field berubah, user interfacenya tidak secara otomatis dirender ulang oleh odoo. Agar user interface dirender ulang saat nilai suatu field berubah, kita harus meng-override method _render, atau _renderEdit jika logic tampilan antara mode edit dan readonly ingin anda atur lewat javascript. Tapi karena di tutorial ini logic tampilan antara mode edit dan readonly di tulis di file xml dengan attribut t-if kita akan meng-override method _render saja.

Ada beberapa cara untuk mengubah tampilan user interface, cara yang paling gampang adalah menggunakan qweb. Oleh karena itu mari kita import qweb dan kita override method _render seperti pada kode di bawah ini.

odoo.define('tutorial_javascript.widget_one', function (require) {
"use strict";
    // import object yang dibutuhkan untuk membuat sebuah widget
    var AbstractField = require('web.AbstractField');
    var FieldRegistry = require('web.field_registry');

    // import qweb untuk merender view
    var core = require('web.core');
    var qweb = core.qweb;

    // buat sebuah object dengan nama bebas
    // jangan lupa untuk extend ke object web.AbstractField atau object turunanya
    var WidgetOne = AbstractField.extend({
        template: 'WidgetOneTemplate', // isi nama template yang telah dibuat untuk mengatur tampilan/view widget
        events: { // daftar event, mirip event di jquery
            'click .btn-minus': 'btn_minus_action',
            'click .btn-plus': 'btn_plus_action',
        },
        btn_minus_action: function(){
            var new_value = this.value - 1;
            this._setValue(new_value.toString());            
        },
        btn_plus_action: function(){
            var new_value = this.value + 1;
            this._setValue(new_value.toString());
        },
        _render: function () {
            // render ulang jika nilai dari field berubah
            console.log(this.value);
            this.$el.html($(qweb.render(this.template, {'widget': this})));
        },
    });

    // daftarkan widget yang telah kita buat ke web.field_registry
    // agar kita bisa menggunakan widget yang kita buat di file xml/view odoo 
    // dengan kode seperti di bawah ini
    // <field name="field_one" widget="widget_one" />
    // nama 'widget_one' ini bebas, asal selalu nyambung/tanpa spasi
    FieldRegistry.add('widget_one', WidgetOne);

    // return object widget yang telah kita buat
    // agar bisa di-extend atau di-override oleh module lain
    return WidgetOne;

});

Parameter pertama yang harus kita lewatkan pada method qweb.render adalah nama template yang akan dirender. Sedangkan parameter kedua adalah object/data yang ingin kita tampilkan. Pada parameter kedua, key dari object tersebut kita set widget karena di template WidgetOneTemplate kita menggunakan kode widget.value untuk menampilkan nilai dari field yang menggunakan widget yang kita buat. Kita bisa saja mengubah nama key ini, misal menjadi seperti pada kode di bawah ini.

this.$el.html($(qweb.render(this.template, {'data': this})));

Tapi di template WidgetOneTemplate kita juga harus mengubah kode untuk menampilkan nilai dari field menjadi data.value, jika tidak akan terjadi error.

Sekarang mari kita lihat apakah nilai dari field sudah ditampilkan dengan benar atau tidak.

Widget custom odoo menghasilkan value yang benar

Oh, ternyata nilainya sudah ditampilkan dengan benar. Sekarang mari kita simpan, apakah nilainya disimpan dengan benar atau tidak.

Widget custom odoo setelah nilainya disimpan

Sekarang nilai dari field yang menggunakan widget widget_one akan bertambah 1 jika user menekan tombol + dan akan berkurang 1 jika user menekan tombol . Lalu bagaimana jika user ingin agar nilai pertambahan atau pengurangan ini bisa diatur, misal di form A nilai perubahannya adalah 1000 sedangkan di form B user ingin agar nilai perubahannya adalah 3 ?

Kita bisa memanfaatkan attribute attrs, options, atau context, yang biasanya disertakan saat kita menulis field di file xml. Tapi pada tutorial ini kita akan menggunakan attribute options untuk mendeteksi apakah user mengatur nilai perubahan atau tidak. Jika user mengatur-nya di attribute options seperti pada kode dibawah ini.

<field name="field_one" widget="widget_one" options="{'step': 1000}"/>

Nilai dari field yang menggunakan widget widget_one akan ditambah atau dikurangi sebesar nilai step yang diatur oleh user di file xml, yaitu sebesar 1000. Tetapi jika user tidak mengaturnya, seperti pada kode di bawah ini.

<field name="field_one" widget="widget_one"/>

Maka nilai dari field yang menggunakan widget widget_one akan ditambah atau dikurangi 1.

Kita bisa menyimpan nilai perubahan sebagai property dari widget yang kita tulis, kemudian kita bisa memanfaatkan method init yang secara otomatis akan dipanggil pertama kali oleh odoo untuk mendeteksi apakah user mengatur nilai perubahan atau tidak. Seperti pada kode di bawah ini.

odoo.define('tutorial_javascript.widget_one', function (require) {
"use strict";
    // import object yang dibutuhkan untuk membuat sebuah widget
    var AbstractField = require('web.AbstractField');
    var FieldRegistry = require('web.field_registry');

    // import qweb untuk merender view
    var core = require('web.core');
    var qweb = core.qweb;

    // buat sebuah object dengan nama bebas
    // jangan lupa untuk extend ke object web.AbstractField atau object turunanya
    var WidgetOne = AbstractField.extend({
        step: 1, // nilai default, jika user tidak mengaturnya di file xml
        template: 'WidgetOneTemplate', // isi nama template yang telah dibuat untuk mengatur tampilan/view widget
        events: { // daftar event, mirip event di jquery
            'click .btn-minus': 'btn_minus_action',
            'click .btn-plus': 'btn_plus_action',
        },
        init: function () {
            // method 'init' dipanggil pertama kali saat widget digunakan
            this._super.apply(this, arguments);
            if(this.nodeOptions.step){
                // jika user mengatur nilai step di file xml
                // ubah nilai step agar sesuai yang diinput user
                this.step = this.nodeOptions.step;
            }
        },
        btn_minus_action: function(){
            var new_value = this.value - this.step;
            this._setValue(new_value.toString());            
        },
        btn_plus_action: function(){
            var new_value = this.value + this.step;
            this._setValue(new_value.toString());
        },
        _render: function () {
            // render ulang jika nilai dari field berubah
            console.log(this.value);
            this.$el.html($(qweb.render(this.template, {'widget': this})));
        },
    });

    // daftarkan widget yang telah kita buat ke web.field_registry
    // agar kita bisa menggunakan widget yang kita buat di file xml/view odoo 
    // dengan kode seperti di bawah ini
    // <field name="field_one" widget="widget_one" />
    // nama 'widget_one' ini bebas, asal selalu nyambung/tanpa spasi
    FieldRegistry.add('widget_one', WidgetOne);

    // return object widget yang telah kita buat
    // agar bisa di-extend atau di-override oleh module lain
    return WidgetOne;

});

Sekarang jika user mengatur nilai step sebesar 1000 di file xml dalam attribute options nilai field akan ditambah atau dikurangi sebesar 1000, seperti pada gambar di bawah ini.

Widget custom odoo dengan nilai perubahan 1000

Lalu bagaimana jika kita ingin menambah pemisah ribuan ?

Kita bisa memanfaatkan object web.field_utils untuk memformat ribuan. Oleh karena itu mari kita import terlebih dahulu.

var field_utils = require('web.field_utils');

Lalu mari kita ubah method _render agar menyertakan nilai field yang sudah diformat dengan pemisah ribuatn.

_render: function () {
	// render ulang jika nilai dari field berubah
	// format value agar tampilannya ada pemisah ribuan
	var formated_value = field_utils.format[this.formatType](this.value);
	this.$el.html($(qweb.render(this.template, {'widget': this, 'formated_value': formated_value})));
},

Lalu mari kita ubah template WidgetOneTemplate agar menampilkan nilai yang sudah diformat dengan pemisah ribuan, alih-alih nilai asli dari field tersebut.

<?xml version="1.0" encoding="UTF-8"?>
<template>

    <t t-name="WidgetOneTemplate">
        <div>
            <t t-if="widget.mode == 'edit' ">
                <div class="input-group">
                    <div class="input-group-prepend">
                        <button class="btn btn-danger btn-minus"> - </button>
                    </div>
                    <input type="text" class="form-control" t-att-value="formated_value" disabled="disabled" />
                    <div class="input-group-append">
                        <button class="btn btn-success btn-plus"> + </button>
                    </div>
                </div>
            </t>
            <t t-if="widget.mode == 'readonly' ">
                <span t-esc="formated_value" />
            </t>
        </div>
    </t>

</template>

Hasilnya akan seperti pada gambar di bawah ini.

Widget custom odoo dengan pemisah ribuan

OK. Demikian tutorial bagian kedua ini. Semoga bermanfaat bagi anda.

Download Source Code
Tulisan Serupa

Leave a Reply

Your email address will not be published. Required fields are marked *