Backbone Form’da İlk Geçersiz Elemana Gitmek

Önyüzünde Backbone kullandığımız projeye eklenmesi gereken basit bir özellik var.

– Formda 12 tane alan vardır bunların *,*,* alanları zorunludur.
– Kullanıcı formu doldurur.
– Form geçerli ise listeleme sayfasına yönlendirilir.
– Geçersiz ise ilk hatalı input viewport’a gelir (scroll yapılır).
– Hem desktop hem mobilde aynı akış sağlanmalıdır.

Ne mobilde ne de masaüstü sürümde (responsive, esnek tasarım) form elemanının yerini tespit edip scroll yapamayacağımıza göre en güzel çözüm o elemanı aktif hale getirmek, yani “focus” event’i tetiklemek. Backbone size form yapısı sağlamıyor, fakat @powmedia’nın formu hemen hemen bütün ihtiyaçları karşılıyor.

Sistemde kullandığımız hiç bir kütüphaneyi abstract bir katman eklemeden kullanmıyoruz. Dolayısı ile kendi oluşturduğumuz Form, Backbone.Form u extend eden abstract katman. Yapımız AMD (Asynchronous Module Definition) ve dependency manager olarak RequireJS kullanıyoruz. Bunlarla birlikte Backbone bağımlılıkları olan jQuery, underscore gibi kütüphaneleri de sayfaya yüklememiz gerekiyor.

Çalıştığımız dizine RequireJS, Backbone ve BackboneForms u koyduktan sonra BackboneForms’dan kalıtım alan modulümüzü şöyle yazıyoruz:

Not: Ön bilgi vermekte fayda var, sayfamızda Backbone.Form github’daki powmedia’nın geliştirdiği formdur. Radiobox, selectbox gibi form elementleri editors namespace’i altında toplanmış. Siz de kendi form element türlerinizi geliştirebilirsiniz. Mesela yukarıdan gelen bir başka istekde bir sistemdeki tüm fiyat belirten inputların başına para cinsi, sonuna da “.00” şeklinde küsüratı sabitlememiz istenmişti, biz bunu Backbone.Form.editors.PriceField şeklinde tanımladık, formların şema tanımlamalarında da “type: ‘Text'” yerine “type: ‘PriceField'” yazarak basitçe çözdük.

Tüm form elementleri Backbone.Form.Field‘i extend ediyor. Backbone.Form.editors namespace’i altında da üst paragrafda bahsettiğim form eleman türleri var. Yani checkbox’ı override etmek için Backbone.Form.editors.Checkboxes, veya hidden input’u yeniden tanımlamak için Backbone.Form.editors.Hidden methodunu ezmeniz gerekiyor.

Bu blog yazısında bahsettiğimiz yer Backbone.Form.Field methodunu yeniden tanımlamak. Fakat ileriye dönük farklı ihtiyaçları da karşılayabilmek için biz burada Backbone.Forms’u da boş şekilde extend ettik.

/**
 * Author: Irfan Durmus 
 * Date:   Thu Apr 3 12:03:32 2014 +0400
 *
 * Customized Backbone Form Asynchronous Module in order 
 * to provide atuo-focus to first error field in Backbone Forms.
 */
define([
    'backbone',
    'backbone-forms'
], function(
    Backbone,
    BackboneForms
){
    // we'll need this variable to count how many element we have in our form.
    var counter,

    // extend Backbone.Form element to implement new features in the future
    Form = Backbone.Form.extend({});
    
    Form.Field = Backbone.Form.Field.extend({
        /**
         * Overwrite the setError method of Backbone.Form.Field
         * to focus first element when error occur
         */
        setError: function(msg) {
            Backbone.Form.Field.prototype.setError.call(this, msg);
            this.hasError = true;
            counter++;
            this.findError();
        },
         
        /**
         * Overwrite the clearError method of Backbone.Form.Field
         */
        clearError: function() {
            Backbone.Form.Field.prototype.clearError.call(this);
            this.hasError = false;
            counter++;
            this.findError();
        },
        
        findError: function() {
            var totalSize = _.size(this.form.schema),
                focusField = null;

            if (counter == totalSize) {
                // set counter 0 to able to manage next form action;
                counter = 0;
                _.each(this.form.fields, function(field, key, fields) {
                    if (!focusField && field.hasError) {
                        focusField = field;
                    }
                });
                if (focusField) {
                    focusField.focus();
                }
            }
        }

    });
    
    // return and use this extended form class instead of Backbone.Forms;
    return Form;
});

Form’u post ettiğimizde, form her bir elemanı için, clearError veya setError methodlarını çağırıyor. Biz yine formun sağladığı methodları static olarak Backbone.Form.Field.prototype.clearError.call ile çağırarak normal davranışını bozmadık. Sonra hata olup olmadığını anlamak için hasError’u set ettik (sanırım formda zaten var bu, satırı silip denemedim). Her bir form elemanı için her iki method’dan birisi çağrıldığına göre counter++ ile iki methodda da artırım yapmamızın bir saknıcası yok. Son olarak da kendi tanımladığımız findError methodunu çağırıyoruz.

Not: Diğerleri nasıl çözüm üretmiş diye araştırdığımda ingilizce kaynaklarda bile object orient bir çözüm olmaması çok garip gelmişti.