مفاهیم اولیهی اتصال داده (data binding) در جاوااسکریپت
26 خرداد
یکی از مهمترین الگوهای توسعهی اپلیکیشنهای جاوااسکریپتی، جدا کردن دادهها و ظاهر اپلیکیشن از همدیگر و اتصال این دو به یکدیگر به شکل یکسویه یا دوسویه است. به طوری که با تغییر داده، ظاهر مرتبط با آن داده عوض و به شکل متقابل با تغییر در ظاهر برنامه (مانند پر کردن فرم یا تغییر مکان یک شی) داده مرتبط با آن دچار تغییر شود. امروزه اتصال داده در اپلیکیشنهای تحت وب برای برنامهنویس از نان شب واجبتر است! البته فریمورکهای متداول مانند ReactJs، این اتصال داده را به خوبی برای برنامهنویسان پیشبینی کردهاند و در این مقاله قرار نیست فریمورک توسعه بدهیم. اما به دلایلی خوب است مفاهیم اولیهی اتصال داده (data binding) در جاوااسکریپت را درک کنیم.
اول اینکه فهمیدن این موضوع که اصول اولیهی اتصال داده از کجا آب میخورد جذاب است! در کنار این موضوع به تقویت دانش جاوااسکریپتی برنامهنویس نیز کمک میکند و برنامهنویس میتواند در مواقعی که کار در قد و قوارهی استفاده از فریمورک نیست اتصال داده را برای خود انجام داده و اصول MVC را پیادهسازی کند.
اتصال دادهی یکسویه
(one-way data binding)
یکی از مهمترین مشوقهای استفاده از فریمورکها، کمبودهایی است که در جاوااسکریپت وجود داشته است. فریمورکها با مرتفع کردن این کمبودها (که انصافا کم هم نبوده است) برنامهنویس را در عرصهی توسعهی آسانتر اپلیکیشن یاری دادهاند. اما جاوااسکریپت از زمان پیدایش خود پیشرفتهای زیادی داشته و بسیاری از کمبودها را پوشش داده است. بنابراین ممکن است بخواهیم در برخی موارد اپلیکیشن خود را با جاوااسکریپت خالص توسعه دهیم. در این صورت باید بتوانیم اتصال داده را پیادهسازی کنیم.
مفاهیم مربوط به اتصال داده ساده است. در یک سمت یک مدل دادهای را داریم و در سمت دیگر رابط کاربری (اینترفیس) را که معمولا اسمش را میگذاریم ویو (View). حال میخواهیم کاری کنیم زمانی که داده را تغییر میدهیم، بخشی از رابط کاربری که با آن داده در ارتباط است نیز تغییر کند و به روز شود. این مفهوم معمولا برای دادههایی مفید خواهند بود که صرفا قرار است خوانده شوند (نوشتن داده از طریق رابط کاربری نداریم). به این تکنیک اتصال دادهی یکسویه میگوییم.
راز ایجاد اتصال دادهی یک طرفه در getter و setter برای propertyها در جاوااسکریپت است. جاوااسکریپت از مدتهای مدید Object.defineProperty
داشته است. این تابع به برنامهنویس اجازه میدهد توابع دلخواه getter و setter برای propertyهای آبجکتهای جاوااسکریپت تعریف کند. کد زیر نمونهای از به کارگیری getter و setterها در اتصال داده یکطرفه است:
function Binding(b) {
//element ke mikhahim be datat bind shavad:
this.element = b.element
//data ke be shekle yek property az yek object ast:
this.value = b.object[b.property]
//attribute az element ke mikhahim data be
// aan bind shavad.
this.attribute = b.attribute
this.valueGetter = function(){
return this.value;
}.bind(this);
this.valueSetter = function(val){
// meqdaar-dehi be data;
this.value = val
// meqdaar-dehi be view;
this.element[this.attribute] = val
}.bind(this);
//afzudane function-haye getter va setter be
// property-ye object
Object.defineProperty(b.object, b.property, {
get: this.valueGetter,
set: this.valueSetter
});
//meqdaar-dehiye avaliye be data;
b.object[b.property] = this.value;
//meqdaar-dehiye avaliye be view;
this.element[this.attribute] = this.value
}
این کد یک رفرنس به آبجکت اولیه ایجاد کرده و property آبجکت رفرنس را طوری درست میکند که هنگام مقداردهی به آن property تابع setter فعال شود. در نتیجه هم View و هم data آپدیت میشوند. کد زیر نمونهای از استفاده از تابع بالا است. اگر یک div
با آیدی container
داشته باشیم و بخواهیم پراپرتی html
از آبجکت a
را به innerHTML
آن bind (متصل) کنیم میتوانیم بنویسیم:
var a = {html: 'this is innerHTML of a div'}
Binding({
element: document.getElementById('container'),
attribute: 'innerHTML',
object: a,
property: 'html'
})
لذا با هر تغییر a.html
، محتوای داخل div
نیز تغییر خواهد کرد.
اتصال داده دو سویه
(two-way data binding)
مدل محدود:
اتصال داده (data binding) در جاوااسکریپت تنها به مدل یکسویه خلاصه نمیشود. با اضافه کردن یک Event Listener به DOM مورد نظر خود، میتوانیم کد بالا رو طوری توسعه دهیم که اطلاعات به شکل دو سویه در آن جریان داشته باشد. به طوری که هر زمان آبجکت آپدیت شود، View تغییر کند و هر موقع View باعث فراخوانی Event Listener شود، محتویات آبجکت تغییر کند که تغییر محتویات آبجکت دوباره View را آپدیت خواهد کرد.
function Binding(b) {
this.element = b.element
this.value = b.object[b.property]
this.attribute = b.attribute
this.valueGetter = function(){
return this.value;
}.bind(this);
this.valueSetter = function(val){
this.value = val
this.element[this.attribute] = val
}.bind(this);
//agar event set shode bashad:
if(b.event){
let _this = this;
this.element.addEventListener(b.event, function(event){
_this.value = _this.element[_this.attribute]
});
}
Object.defineProperty(b.object, b.property, {
get: this.valueGetter,
set: this.valueSetter
});
b.object[b.property] = this.value;
this.element[this.attribute] = this.value
}
حال اگر یک input
از نوع text
و با آیدی text
داشته باشیم، میتوانیم این اتصال دو سویه را با کد زیر تحقیق کنیم:
var a = {value: 'i am here'}
// baraye moshahedeye taqirate 'a':
setInterval(()=>{console.log(a.value)}, 1000);
Binding({
element: document.getElementById('text'),
attribute: 'value',
event: 'keyup',
object: a,
property: 'value'
})
اما کدی که تابحال توسعه دادهایم تا حدودی محدود است. به طوری که تنها یک attribute از یک المان را به یک property از یک آبجکت اتصال میدهد. بیایید اندکی روی موضوع کار کنیم.
اتصال داده دو سویه
(two-way data binding)
مدل بهتر:
مدل بهتر به این شکل است که هر داده باید بتواند به المانهای DOM متعددی متصل شود. بنابراین هر تغییر در داده همهی آن المانها را تغییر دهد؛ چه این تغییر در اثر فعال شدن یک Event از سمت یکی از این المانها باشد یا نه.
function Binding(b) {
this.elementBindings = []
this.value = b.object[b.property]
this.valueGetter = function(){
return _this.value;
}
this.valueSetter = function(val){
this.value = val;
for (var i = 0; i < this.elementBindings.length; i++) {
var binding = this.elementBindings[i];
binding.element[binding.attribute] = val
}
}.bind(this);
this.addBinding = function(element, attribute, event){
var binding = {
element: element,
attribute: attribute
}
if (event){
let _this = this;
element.addEventListener(event, function(event){
_this.valueSetter(element[attribute]);
})
binding.event = event
}
this.elementBindings.push(binding)
element[attribute] = this.value
return this
}.bind(this);
Object.defineProperty(b.object, b.property, {
get: this.valueGetter,
set: this.valueSetter
});
b.object[b.property] = this.value;
}
در کد بالا آرایهی elementBindings
را داریم که با هربار فراخوانی تابع addBinding
و عبور دادن آرگومانهای attribute
،element
و event
، این سه مقدار را به آن آرایه اضافه میکند. بعدها در setter آبجکت، مقادیر همهی اعضای آرایه به همراه مقدار خود آبجکت به روز میشوند. با این روال، تنها کافیست در Event Listener هر DOM تابع setter را فراخوانی کنیم و همچنین برای آبجکت، setter و getter تعریف کنیم تا هنگام تغییر مقدار آبجکت از جای دیگر، setter فراخوانی و اجرا شود. با کد نمونه زیر میتوانیم از تابع بالا استفاده کنیم:
var obj = {a:123}
var myInputElement1 = document.getElementById("myText1")
var myInputElement2 = document.getElementById("myText2")
var myDOMElement = document.getElementById("myDomElement")
new Binding({
object: obj,
property: "a"
})
.addBinding(myInputElement1, "value", "keyup")
.addBinding(myInputElement2, "value", "keyup")
.addBinding(myDOMElement, "innerHTML")
obj.a = 456;
در کد بالا دو input
با آیدی myText1
و myText2
و یک span
با آیدی myDomElement
تعریف کردهایم. هر سه این المانها با تغییر داده obj.a
چه از طرف خود المانها یا از جای دیگر، به مقدار جدید آپدیت خواهند شد.
این کد کار را بهتر کرد. هرچند که کارهای خیلی زیاد دیگری هنوز باقیست. مثلا هنوز هیچ صحتسنجی برای داده وجود ندارد، آرایه پشتیبانی نمیشود، Template نمیتوان ساخت و هزاران مورد دیگر که اگر ایجاد میشد نطفهی فریمورک جدیدی بسته میشد. هرچند که فرمورکهای زیادی وجود دارند و نیازی به توسعه مورد جدیدی نیست اما همین کدی که با هم بررسی کردیم نیز برای بسیاری از کارهای ساده، کار ما را راه خواهد انداخت.
13 اردیبهشت جاوااسکریپت جاوااسکریپت
this در جاوااسکریپت را بهتر بشناسیم
کلیدواژهی this در جاوااسکریپت یکی از مفهومهایی است که باعث سردرگمی مبتدیان این زبان میشود. شاید یکی از دلایل این موضوع این باشد که کلیدواژهی this در جاوااسکریپت، در مقایسه با زبانهای برنامهنویسی دیگر اندکی متفاوت است. از طرفی یادگیری این کلیدواژه بسیار ضروری است. چرا که برای خواندن و نوشتن کدهای حرفهای و درک مفاهیم جاوااسکریپت همواره به آن نیاز خواهیم داشت. در این مقاله به بررسی این کلیدواژه و کاربردهای مختلفی که در زبان جاوااسکریپت دارد میپردازیم.
6 اردیبهشت جاوااسکریپت Promise و جاوااسکریپت
Promiseها در جاوااسکریپت – بخش دوم
در این مقاله مبحث پیشین را ادامه میدهیم. سعی میکنیم نحوهی تعامل با Promiseها را یک به یک مرور کرده و با مثال ابعاد مختلف این مفهوم را واکاوری کنیم.
11 اردیبهشت جاوااسکریپت Promise و جاوااسکریپت
Promiseها در جاوااسکریپت - بخش سوم
در این بخش به مدیریت خطای Promiseها میپردازیم. اگر این سری مقاله را از ابتدا پیگیری نکردهاید پیشنهاد میکنم از بخش اول شروع کنید.
17 اردیبهشت جاوااسکریپت Promise و جاوااسکریپت
Promiseها در جاوااسکریپت - بخش چهارم (آخر)
در این بخش از مقاله که بخش پایانی سری مقالات Promiseها است پروژهای که تعریف کرده بودیم را تکمیل میکنیم و نکات ارزشمندی پیرامون استفادهی حرفهای از Promiseها بیان میکنیم. با من همراه باشید.