I want to implement a two-way-data-binding (like in Angular or Vue) using vanilla JavaScript.
The view to model part I can use add input event listener, and the model to view part, I want use Object.defineProperty's set function.
In defineProperty's set function I need to change the view's value and set the property value, but it will cause "Maximum call stack size exceeded",
because the set property value
will recursively run again and again.
Now the question is: Is there a way I can both use set function and set its property value at the same time?
Now my code :
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>2 Way Data Binding</title>
</head>
<body>
text: <input id="text" class="text" type="text">
</body>
<script type="text/javascript">
var input = document.querySelector("#text");
var data = {};
Object.defineProperty(data, "text", {
get: function(){
return input.value
},
set: function(newValue){
input.value = newValue;
// data.text = newValue; // <------ this is the problem
}
})
input.input = function(){
data.text = data.text;
}
</script>
</html>
To answer your question — no. If you have a setter, you can't turn around and set the value without looping. An alternative is to have a private property on the object that only the get()
and set()
methods will interact with. The outside world will only use the properties that have getters/setters.
Not sure if this is a great way to implement binding, but it is a way to give the appearance of using a setter to set the property:
const data = {
// _text is the source of truth
_text: "some text",
get text() {
return this._text
},
set text(newValue) {
input.value = newValue;
this._text = newValue;
}
};
const input = {
value: data.text
}
// original value
console.log(data.text)
console.log(input.value)
// change it
data.text = "other text"
console.log(data.text)
console.log(input.value)