knockout, set property value changes view, which doesn't raise change event - javascript

In my view model I update an observable's property. The property is bind to an input element.
After I change the value (from JS), the view updates.
The thing is I have other elements on the page that subscribes the the input's change event, which doesn't publish when the value is updated.
Update (Code):
Model:
var viewModel = {
email: ko.observable()
}
Html:
<input class="form-input" data-bind="value: email" type="email" />
JS: (as a result of some click):
$('.form-input').change(function () {
// doesn't happen
});
viewModel.email('someemail#aaa.com');

Instead of using change, use the Knockout subscribe function. Observables Documentation
viewModel.email.subscribe(function(newValue) {
// Called whenever the value is updated
doSomethingWithNewValue(newValue);
});

Related

Vue.js v-bind has a delay, form submits before value is changed

i'm trying to update a hidden input with an entered value from a SweetAlert modal (basically a prompt).
The code below does not work, the form submits but the hidden field value is null.
HTML:
<input type="hidden" name="input" v-model="value">
JavaScript:
this.value = websiteId;
event.target.submit();
The below code however does seem to work! But not really much point in using Vue.js if i'm going to just plain old JavaScript.
HTML:
<input type="hidden" class="input-value-web" name="input" value="0">
JavaScript:
document.querySelector('.input-value-web').value = websiteId;
event.target.submit();
When you change the value of a Vue instance's data property, like you're doing with this.value = websiteId, that bound property won't update the value of the <input> until the Vue instance's next update.
However, the next update won't occur until after everything in the method has been executed.
To get around this, use the $nextTick method to wait until the Vue instance has updated before executing event.target.submit().
Here's an example:
methods: {
submitForm() {
this.value = websiteId;
this.$nextTick(() => {
event.target.submit();
});
}
}

Undo changes to model state when angular form is canceled

In an Angular 4 application, I have a template driven form with controls bound to values in my component. When the form input changes the object bound to the input changes immediately (two-way binding). When the cancel button is clicked, I want to undo the change to the bound object. This allows the user to change values then change their mind and cancel their changes.
https://plnkr.co/edit/RnnPwtHZY0qTN1H6er0z?p=preview
The plunker above has such a form with a bound field to read the hero.name
<h2>{{hero.name}} details!</h2>
An input bound to a hero object.
<form #myForm="ngForm" (ngSubmit)="save(myForm)">
<div class="form-group">
<label>name: </label>
<input name="heroName" [(ngModel)]="hero.name" placeholder="name" />
</div>
<button (click)="cancel(myForm)">Cancel</button>
<button type="submit">Save</button>
</form>
The cancel button calls the ngForm's resetForm() method.
cancel(myForm){
myForm.resetForm();
}
Repro steps
Change the hero name; Observe the h2 changes immediately proving that the bound object changed as well
Click cancel; Observe the name is cleared and the h2 changes because the hero.name is now null
I expected the cancel button to change the hero.name back to the original value. Is this how resetForm() is supposed to work? Is there a different way?
If you make a reset() method, where you set the default values, then you can call it whenever it's needed, as in ngOninit and reset button click:
ngOnInit(){
this.reset();
}
reset(){
this.hero = new Hero(1,'Plunker');
}
cancel(myForm){
this.reset();
}
DEMO
You can reunite cancel() and reset() by refactoring, but you may want keep it as is in case you add something else in cancel.
From the angular documentation, I guess the solution is to use a reactive form instead of a template driven form.
https://angular.io/guide/reactive-forms reads (emphasis :
In keeping with the reactive paradigm, the component preserves the
immutability of the data model, treating it as a pure source of
original values. Rather than update the data model directly, the
component extracts user changes and forwards them to an external
component or service, which does something with them (such as saving
them) and returns a new data model to the component that reflects the
updated model state.

Dynamic HTML Element Binding Using Kendo

Here is the scenario I'm currently in:
I have three HTML elements. A textbox called shipToAddress, another textbox called deliverToAddress and a checkbox called sameAsShipToAddress.
I then have a kendo view model behind the scenes that contains a variable called address which will hold a string of an address ex: "123 Main Street".
When the page first loads, the shipToAddress element data-binds to the kendo view model's address variable ex: "123 Main Street". The deliverToAddress has no data-bind what so ever when the page initially loads. However once the sameAsShipToAddress checked box gets checked, I want to add a data-bind attribute to the deliverToAddress so it too will look at the kendo view model's address variable.
Here is the HTML:
<input id="shipToAddress"
data-bind="value: address" />
<input type="checkbox"
id="deliverSameAsShipTo"
value="deliverSameAsShipTo"
data-bind="checked: sameAsShipToAddress,
events: { click: differentDeliveryAddress }" />
<input id="deliverToAddress" />
And here is the backend Kendo View Model:
var _vm = kendo.observable({
address: "",
sameAsShipToAddress: false,
differentDeliveryAddress() {
if (!this.sameAsShipToAddress)
$("#deliverToAddress").attr("data-bind", "value: address");
else
$("#deliverToAddress").removeAttr("data-bind");
}
});
Can this be done? I feel like I'm close with the following code but currently the deliverToAddress' value property is not getting set. Do I need to some how refresh the deliverToAddress element's attributes?
Your code works correctly and you are very close to the final solution. Instead of setting the value of the bound field, set the "binding definition" as the property value:
$("#deliverToAddress").attr("data-bind", "value: address");
The ViewModel is already bidirectionally bound, so rebind the model and the binding will work in both directions.
After adding the property (and thus binding to the address field of the ViewModel) you need to set also the value of the deliverToAddress field. After that the binding resp. unbinding (set the value to empty string here) works correctly - you can see using the browser DOM explorer, that the attribute is added - change the address text and click the test button - deliverToAddress changes too.
I have created an example in the Telerik Dojo.
The view model code looks like this (I have added a button to test the behaviour):
$(document).ready(function(){
var vm = kendo.observable({
address: "",
sameAsShipToAddress: false
});
kendo.bind(document.body, vm);
//
$("#setDeliverAddressButton").kendoButton({
click: function(e) {
console.log(vm.sameAsShipToAddress);
if (vm.sameAsShipToAddress) {
$("#deliverToAddress").attr("data-bind", "value: address");
$("#deliverToAddress").val(vm.address);
}
else {
$("#deliverToAddress").removeAttr("data-bind");
$("#deliverToAddress").val("");
}
}
});
$("#triggerChangeButton").kendoButton({
click: function(e) {
kendo.bind(document.body, vm);
}
});
});
The HTML:
<input id="shipToAddress" data-bind="value: address" />
<input type="checkbox"
id="deliverSameAsShipTo"
value="deliverSameAsShipTo"
data-bind="checked: sameAsShipToAddress" />
<input id="deliverToAddress" />
<button role="button" id="setDeliverAddressButton">set deliver address</button>
bidirectional update
Another option (or even you need to do this always) is to trigger the change programatically as described in this discussion: How to update the ViewModel after programatic changes.

Backbone bind model attribute to input

In Backbone is there a bind a model attribute to an input field so that when the input value changes the model attribute will be automatically set to the current value?
At the moment I have the following in my view
<input type="text" name="firstname" class="form-input" value="<%- model.firstname %>" />
Then in the view I listen to the following event and set the model attribute accordingly
events: {
"keydown .form-input": "setAttribute"
},
setAttribute: function() {
//Use model.set on the attribute that was changed
}
To me this seems like a bad way of doing it. Am I missing an easier way of doing it?
That's the right way to do it with vanilla Backbone. If you would like to set up automated data binding you'll need a plugin like Epoxy.
Using Epoxy, your example would look something like:
var BindingView = Backbone.Epoxy.View.extend({
bindings: {
"[name=firstname]": "value:firstName",
}
});
This binds the model's firstName attribute to the input with name="firstname".

Replace keyup event action with click event action

http://jsfiddle.net/2q8Gn/23/
Rather than have computedPageLinks update via the search input keyup event, can anybody help me amend the above fiddle so computedPageLinks updates via a button click event and doesn't update when the search input loses focus?
Since your search results are based on the value of query changing, even if you remove the valueUpdate: 'keyup' setting, that computed function will still update when the input box is blurred.
You can break that behavior by moving the "searching" out of the computed observable and instead populating a "results" observable array when the click occurs.
So, add a "results" array:
self.results = ko.observableArray([]);
And then add a search function we can trigger on click that populates the results:
self.search = function() {
// Did the user search for anything?
if (!self.query()) {
self.results([]);
return;
}
// Do the "search"
self.results(ko.utils.arrayFilter(self.pageLinks(), function(item) {
return item.title.toLowerCase().indexOf(self.query().toLowerCase()) >= 0;
}) || []);
};
Now you can remove the computed observable, and change your binding to use the results array:
<tbody data-bind="foreach: results">
To link it all up, first remove the valueUpdate setting on your <input> and add a <button> that triggers the search function on your model:
<input placeholder="Search…" type="search" name="q" data-bind="value: query" autocomplete="off" />
<button type="button" data-bind="click: search">Go</button>
Demo

Categories