Update the observable when input value is changed by Javascript - javascript

Is there a way to update an observable when the <input> value is changed, but programatically, i.e. by Javascript?
Here is a jsfiddle of this use case that I am not able to make it work: http://jsfiddle.net/qYXdJ/
As you see when the "Update input value by Javascript" link is clicked the observable is obviously not updated, since it is not reflected in the <span>

If you absolutely can't modify the observable directly (which is the best way), you can trigger the "onchange" event (which Knockout uses internally). With jQuery, it's a simple matter:
$('#update').on('click', function() {
$('#input2').val('New Value').trigger('change');
});
If you don't want to use jQuery for whatever reason, have a look at this question.

As cyanfish pointed out the correct way is to update the observable.
If the problem is your code doesn't have access to the observable, for example you're writing a bookmarklet to automatically fill out a form, then you can gain access to the observable like this:
function setValue(input, value) {
var bindingsString = input.getAttribute('data-bind');
if (bindingsString) {
var bindings = ko.bindingProvider.instance.parseBindingsString(bindingsString, ko.contextFor(input), input);
if (bindings.value) {
bindings.value(value);
} else if (bindings.checked) {
bindings.checked(value);
} else {
input.value = value;
}
} else {
input.value = value;
}
}

You have to change the viewModel 'name' property instead of input field value, because it's observable, and any changes on the property will be reflected to all binded html elements.
var viewModel = {
name: ko.observable()
};
ko.applyBindings(viewModel);
document.getElementById('update').onclick = function(){
viewModel.name('New Value');
//document.getElementById('input2').value = 'New Value';
}

Related

Is there a way I can dynamically bind a string and the text it outputs without using setInterval?

Is there a way I can dynamically bind a string and the text it outputs without using setInterval? I want it to be similar to Angular and Vue though I want to do this with vanilla JS. I want to be able to open the console and change the value at any time and see the change output on my element. Thank you in advance!
I think your only two options are:
A. Edit the element directly, e.g.
myPublicElemeVariable.innerText = 'Bla'
B. Use a setter (or Proxy):
obj = {
get str() { return this.myStr; }
set str(val) {
elem.innerText = val;
this.myStr = val;
}
}
C. Just use a function/method!
If you mean you want change to be event-driven, there is already a very simple event framework in javascript - the EventTarget class as demonstrated by this Code Sandbox
//define a watchable thing
class ValueTarget extends EventTarget {
constructor(value = "") {
super();
this.setValue(value);
}
getValue() {
return this._value;
}
setValue(value) {
this._value = value;
this.dispatchEvent(new CustomEvent("change", { detail: { value } }));
}
}
//get the page elements
const inputElement = document.querySelector("input");
const outputElement = document.querySelector("h1");
//create a watchable thing
const fieldTarget = new ValueTarget("");
//wire events that will change the watchable
inputElement.addEventListener("input", function (e) {
fieldTarget.setValue(e.target.value);
});
//receive notifications from the watchable
fieldTarget.addEventListener("change", (e) => {
outputElement.textContent = e.detail.value;
});
You may be as well to build your own given how simple it is - maintains a list of listeners and calls them when notified. My work recently needed such a thing which I knocked up in Typescript at https://github.com/cefn/lauf/blob/main/modules/lauf-store/src/core/watchable.ts#L3-L21 and would therefore be very easy to redo in javascript.

Assign data with method to vue data variable dynamic

How can I assign data to a dynamic variable.
What I'm trying to achieve is this:
My vue data variable:
test: '',
Adding a click
<div #click="assignData(test)"
Method:
assignData(value) {
// Now I don't want this
this.test = 'lorem..';
// Instead I want something like this
value = 'new value for test';
// Or
this.value = 'new value for test';
}
Now obviously this is not gonna work, but I hope you'll get the idea.
I need to change many variables and I don't want to add all the variables in the function like this
assignData(value) {
this.test = 'lorem..';
this.anotherVar = 'ipsum';
this.newTest = '....';
}
You could send the property you want to change with the function.
<div #click="assignData('test', 'new value for test)">
and then in your code
assignData(prop,val) {
this[prop] = val;
console.log(this.test);
}
Here is a snippet from jsfiddle: https://jsfiddle.net/49gptnad/6447/
assignData(key, value) {
this[key] = value;
}
You'll have to specify the variable you're assigning when you activate it with the click caller.
You should use either vm.$set( target, key, value ) or Vue.set( target, key, value )
Vue.set docs
vm.$set docs

How to set a data binding in element which is created by JS?

I try to add a data binding to an element, which is created in JS, but it does not work. Either there is a string with the code or the value stay empty.
el.setAttribute('value', '{{value}}'); The content in the Input was is'{{value}}'
el.setAttribute('value', value); The content in the Input was is empty, because value is empty.
How is it possible to create a element dynamically and use data binding?
Creating element dynamically is another subject but here I tried to bind a value dynamically to an existing element dynamically, you may use like :
<sample-elem data = "{{data}}"></sample-elem>
...
<script>
class MyApp Polymer.Element {
static get is() { return "jobijoy-app"; }
static get properties() { return {
data: {
type:String,
value(): {return "";}
}
bindValue() {
this.set('data','Some data here');
}
customElements.define(MyApp.is, MyApp);
</script>
At this point you have a data property in sample-elem with data value is 'Some data here'
I am not sure but here with this code, you may create dynamically sample-elem
ready(){
super.ready();
var dynamicEl = document.createElement("sample-elem");
document.body.appendChild(dynamicEl);
}
var dynamicEl = document.createElement("sample-elem");
If you create an element using createElement you can use either one of these before you append it.
dynamicEl.value = this.value; // First way
dynamicEl.setAttribute('value', this.value; // Second way
this.value calls the property value set in the element where you try to create your other element programmatically.

How to invoke model events forcefully

I written a function, it will trigger whenever model attribute change just like in the following way.
modelEvents:{
"change:Name":"callback1"
},
callback1:function(){
console.log("This is testing");
}
Initially I set model.set("Name","hi") so automatically callback1 was invoked. Again If I set the same value into model, callback1 not triggering because model attribute not modified. so For this every time I am doing in the following.
model.set({"Name":""},{silent:true});
model.set({"Name":"hi"});
If I do like above it's working fine, but I want to know is there any option(like silent) to forcefully invoke callback.
Thanks.
If you want to go the route of passing an option then the only way to accomplish this would be to override the set method with something like this in your Model, although i haven't done testing on this to make sure it would not produce unexpected results.
set: function(key, val, options) {
//call the origonal set so everything works as normal
Backbone.Model.prototype.set.call(this, key, val, options);
current = this.attributes, prev = this._previousAttributes;
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options || (options = {});
//new option to always trigger the change on an attribute
if (options.loud) {
for (var key in attrs) {
//if the value is the same as before trigger the change
//otherwise backbone will have already triggered the chage
if (_.isEqual(prev[key] , attrs[key])) {
this.trigger('change:' + key, this, current[key], options);
}
}
}
}
then to make use of it call the normal set but pass loud: true
this.model.set({
name: "Hi"
}, {
loud: true
});
here a fiddle that makes use of it http://jsfiddle.net/leighking2/ktvj0kgp/
To show that the event is triggered even when the attribute is the same i have added an attribute called renders to show how many times it has been called.
Why don't you use Model.hasChanged for this? basically it will listen for changes in an attribute.
Take a look here.
http://backbonejs.org/#Model-hasChanged
Hope it helps

Changing A ko.observable into a ko.computed and vise versa

I am designing a Javascript-based Ohms law calculator (voltage, resistance, current) using knockout.js.
I want the ability of the user being able to select what is calculated, , e.g. voltage, resistance, or current, given the other two parameters, via radio buttons.
So my question is, can you change a ko.observable into a ko.computed and vise versa, after ko.applyBindings() has been called?
My initial attempts say no, I have tried this and slaved over the non-working code for hours, trying to get it to work.
You can't do it that way, but you can make all of them read/write ko.computeds that store a "shadow" value when written to and return that value when read from if they aren't the selected quantity (and return a calculated value if they aren't)
You dont even need a writable computed for this like ebohlman suggests
A simple demo
http://jsfiddle.net/K8t7b/
ViewModel = function() {
this.selected = ko.observable("1");
this.valueOne = ko.observable(1);
this.valueTwo = ko.observable(5);
this.result = ko.computed(this.getResult, this);
}
ViewModel.prototype = {
getResult: function() {
if(this.selected() === "1") {
return this.valueOne() - this.valueTwo();
}
return this.valueOne() + this.valueTwo();
}
};
ko.applyBindings(new ViewModel());
edit: hm, if you want the result to be presented in the correct value textbox you need to make them writable computed like ebohlman suggets
As ebohlman mentioned, the vital thing I was missing was shadow-variables, and the use of separate read/write procedures (a recently added feature to knockout) for ko.computed.
The code for one of the three variables is:
this.voltageS = ko.observable();
this.voltage = ko.computed({
read: function () {
if(this.calcWhat() == 'voltage')
{
console.log('Calculating voltage');
if(this.currentS == null)
return;
if(this.resistanceS == null)
return;
this.voltageS(this.currentS()*this.resistanceS());
return(this.currentS()*this.resistanceS());
}
else
{
console.log('Reading from voltage');
return this.voltageS();
}
},
write: function (value) {
console.log('Writing to voltage');
this.voltageS(value)
},
owner: this
});
I have created a JSFiddle here, which demonstrates being able to switch between which variable is calculated.
Another key part to this code is that on read, if it did happen to be the selected variable, as well as calculating it from the other two, I also had to write this result back to the shadow variable. This preventing some of the variables from mysteriously dissapearing/reappearing when the selected variable was changed.

Categories