هنوز چند بایت مونده که پیدا کنم...

مقید سازی دو طرفه در جاوا اسکریپت (Two-Way Binding)

یعنی چی ؟ فرض کنید میخواید با تغییر مقدار یک کنترل در UI (مثل input) مقدار آبجکت مرتبط در کد جاوا اسکریپت شما تغییر کنه یا بلعکس!
 
فریم ورک های زیادی برای این منظور وجود داره مثل Embger و Angular یا Knockout که مقید سازی دو طرفه رو جز خصوصیت های برجسته خودشون مطرح میکنن. این به معنی این نیست که ساختن و نوشتن چنین کدی از پایه سخته یا ممکن نیست! تازه از نظر من (همونطور که در تکنیک های بهینه سازی کد جاوا اسکریپت هم اشاره کردم) وقتی فقط این ویژگی رو نیاز داریم چرا باید لایبرری به اون سنگینی رو توی پروژه لود کنید؟
 
کلا از نظر مفهومی قضیه رو میشه توی سه آیتم خلاصه کرد:

  1. راهی که بشه کنترل رو در سطح UI به آبجکت مرتبط کرد (معرفیشون کنیم به هم)
  2. نظارت بر تغییرات در کد و در واسط کاربری (UI)
  3. اعمال تغییرات در دو سر رابطه ؛)
 
راه های زیادی برای این کار وجود داره یه راه ساده (چرا من همیشه روی سادگی تاکید دارم؟ میگم. اگر اشتباه نکنم وودی گاتری میگه هر احمقی میتونه کارارو پیچیده تر کنه اما ساده کردن کارا هوش میخواد!) و البته موثر استفاده از روش PubSub هست.
 
ایده کلی ساده است: میتونیم از خصوصیت های data استفاده کنیم برای تگ های اچ تی ام ال و مقید سازی. کل آبجکت های جاوا اسکریپت و المانهای DOM که به هم مقید شدن (مرتبط شدن) در یک آبجکت PubSub مشترک میشن (subscribe).
هر وقت یک تغییر در هر یک از آبجکت های جاوا اسکریپت یا المان دام اتفاق افتاد ما رویداد رو به PubSub میفرستیم و اون میاد تغییرات رو یکسان سازی میکنه.
 
ذکر دو نکته رو ضروری میدونم اینجا:
  • چرا من سعی میکنم فارسی نویسی کنم و عبارات رو ترجمه و جایگزین کنم؟ به این دلیل که ارزش زیادی برای زبون فارسی قائلم و امید دارم روزی بتونیم علم رو با زبون مادری منتقل کنیم به همه دنیا.
  • خوب چرا معادل انگلیسی رو گاهی میذارم و گاهی با هم در پرانتز و گاهی تنها استفاده میکنم؟ به این دلیل که اول اون ترجمه نهادینه بشه توی ذهن دوم رفقای برنامه نویس سریعتر متوجه میشن عبارت انگلیسی رو چون خیلی مواقع به یک مفهوم اشاره داره و سوم اینکه من دوست دارم اینطور بنویسم ؛)
یه پیاده سازی خوب رو از این مفهوم براتون لینک کردم آخر کدها.
دو مدل پیاده سازه برای شما در نظر گرفتم با کمک jQuery و روش مورد علاقه خودم با استفاده از جاوا اسکریپت خالص و اول دومی رو میذارم :
 
// 2- با استفاده از جاوا اسکریپت
function DataBinder( object_id ) {
  // ساده میسازیم PubSub یه آبجکت
  var pubSub = {
        callbacks: {},

        on: function( msg, callback ) {
          this.callbacks[ msg ] = this.callbacks[ msg ] || [];
          this.callbacks[ msg ].push( callback );
        },

        publish: function( msg ) {
          this.callbacks[ msg ] = this.callbacks[ msg ] || []
          for ( var i = 0, len = this.callbacks[ msg ].length; i < len; i++ ) {
            this.callbacks[ msg ][ i ].apply( this, arguments );
          }
        }
      },

      data_attr = "data-bind-" + object_id,
      message = object_id + ":change",

      changeHandler = function( evt ) {
        var target = evt.target || evt.srcElement, // IE8 دیوانه میکنه آدمو
            prop_name = target.getAttribute( data_attr );

        if ( prop_name && prop_name !== "" ) {
          pubSub.publish( message, prop_name, target.value );
        }
      };

  // به تغییرات گوش میکنیم
  if ( document.addEventListener ) {
    document.addEventListener( "change", changeHandler, false );
  } else {
    // دیوونه IE8 اینم برا
    document.attachEvent( "onchange", changeHandler );
  }

  // PubSub اعمال تغییرات در دو سر رابطه توسط 
  pubSub.on( message, function( evt, prop_name, new_val ) {
    var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"),
        tag_name;

    for ( var i = 0, len = elements.length; i < len; i++ ) {
      tag_name = elements[ i ].tagName.toLowerCase();

      if ( tag_name === "input" || tag_name === "textarea" || tag_name === "select" ) {
        elements[ i ].value = new_val;
      } else {
        elements[ i ].innerHTML = new_val;
      }
    }
  });

  return pubSub;
}

// اینم بذارید توی مدلتون
function User( uid ) {
  // کدای خودتون

  user = {
    // کدای خودتون
    set: function( attr_name, val ) {
      this.attributes[ attr_name ] = val;
      
      binder.publish( uid + ":change", attr_name, val, this );
    }
  }

  // ادامه کدای خودتون
}
//-------------------------------------------------------------------

// 1- با استفاده از جی کوئری
function DataBinder( object_id ) {
  // ساده میسازیم PubSub یه آبجکت
  var pubSub = jQuery({});

  // که رابطه رو تعریف کرده نیاز داریم data یک المان
  // این ریختی: data-bind-<object_id>="<property_name>"
  var data_attr = "bind-" + object_id,
      message = object_id + ":change";

  // گوش کن به تغییرات
  jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) {
    var $input = jQuery( this );

    pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] );
  });

  // اعمال تغییرات در هر دو سر رابطه
  pubSub.on( message, function( evt, prop_name, new_val ) {
    jQuery( "[data-" + data_attr + "=" + prop_name + "]" ).each( function() {
      var $bound = jQuery( this );

      if ( $bound.is("input, textarea, select") ) {
        $bound.val( new_val );
      } else {
        $bound.html( new_val );
      }
    });
  });

  return pubSub;
}
// اینم مدلتون
function User( uid ) {
  var binder = new DataBinder( uid ),

      user = {
        attributes: {},

        set: function( attr_name, val ) {
          this.attributes[ attr_name ] = val;
          binder.trigger( uid + ":change", [ attr_name, val, this ] );
        },

        get: function( attr_name ) {
          return this.attributes[ attr_name ];
        },

        _binder: binder
      };

  binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) {
    if ( initiator !== user ) {
      user.set( attr_name, new_val );
    }
  });

  return user;
}
//---------------------------
// اینم مدل استفاده ازش

// javascript
var user = new User( 123 );
user.set( "name", "Mohammad" );

// html
<input type="number" data-bind-123="name" />


نکته

Two-Way Data Binding in JavaScript
نویسنده : محمد ملک مکان
دوشنبه 23 فروردین 1395
+ 120 -

ایده ها، نظرات و سوالات دوستان ما

مهمون کدجیک
دوشنبه 6 دی 1395
پیچیده و جالب.
در مورد تکنیک های MVVM هم مطلب بگذارید.
+ 00 -

خوشحال میشیم نظرتُ بدونیم