Trigger a function when value change using knockoutjs - javascript

I want to trigger a function when user select other value from a combobox <select>
HTML
<div class="wrapper wrapper-content" id="appGradeHorariaCompleta">
<select class="form-control m-b" data-bind="options: Curriculos,optionsText:'Text',optionsValue:'Value',optionsCaption:'Selecione', value: CurriculoSelected, event:{ change: $parent.CurriculosChanged}"></select>
</div>
JS
function PainelViewModel() {
var self = this;
self.Curriculos = ko.observableArray([]);
self.CurriculosChanged = (function (curriculo) {
console.debug(curriculo);
});
}
function CurriculoViewModel() {
var self = this;
self.Id = ko.observable(0);
self.Value = ko.observable('');
self.Selected = ko.observable(0);
}
...GET DATA...
...
..
.
$(data.CurriculoComboBox).each(function (index, item) {
var model = new CurriculoViewModel();
model.myvalues = item.myValues;
painelVM.Curriculos().push(model);
});
ko.applyBindings(painelVM, document.getElementById("appGradeHorariaCompleta"));
What I want is when value change in the combobox I want to get this parameter to bind another function.
But in the way I've done, I got an error:
Unable to process binding "event: function (){return { change:$parent.HabilitacoesCursosChanged} }

First thing first your view model is real messed up. You may need to make changes with variable names used in data-bind to match those in viewmodel. I hope I am getting you right. What you want is to trigger a method when value of combobox changes. For that there is an easier way of using subscribe on an observable(your combobox value). So, whenever value of your combobox changes, subscribe function is triggered. The functionality that you want is passed inside the subscribe function as a function body.
HTML:
<div class="wrapper wrapper-content" id="appGradeHorariaCompleta">
<select class="form-control m-b" data-bind="options:Curriculos, optionsText:'Text',optionsValue:'Value',optionsCaption:'Selectone', value: CurriculoSelected"></select>
</div>
Javascript:
function CurriculoViewModel() {
var self = this;
self.Curriculos = ko.observableArray([]);
self.CurriculoSelected = ko.observable('');
self.CurriculoSelected.subscribe(function(value) {
// Your change functionality goes here combobox.
});
}
References check out the subscribe:
Knockout observables subscribe
Knockout options binding

Subscribe not work, so I still use event: {change: $parent.CurriculosChanged}
So, using ideia from siddhearth to get I get the value selected from my var, I can get the new value changing the $parent for $root
HTML
<select class="form-control m-b" data-bind="options: Curriculos,optionsText:'Text',optionsValue:'Value',optionsCaption:'Selecione', value: CurriculoSelected, event:{ change: $root.CurriculosChanged}"></select>
JS
function PainelViewModel() {
var self = this;
self.Curriculos = ko.observableArray([]);
self.CurriculoSelected = ko.observable(0);
self.CurriculosChanged = function (c) {
console.debug(c.CurriculoSelected);
};
}
Check binding context

Related

How to ignore changes not done by user?

I've got a ko.observable in my viewmodel, which is attached to an input. When user changes value of that input (per-character) I run AJAX call, which downloads suggestions from backend.
When user chooses one of suggestions, I'd like to fill the input with chosen value, but without sending the AJAX call. But setting observable's value will still trigger event and call function attached to textInput binding.
How can I set observable's value without triggering textInput?
Sample code:
var name = ko.observable();
name.subscribe(nameChanged);
var nameChanged = function() {
someService.post(name(), success, failure);
}
var someAction = function() {
name("Test"); // I don't want to trigger AJAX call here
}
<input type="text" data-bind="textInput: name" />
Another option is to use a computed observable as an intermediary. The ajax function can trigger in the write event of the computed so that direct writes to the observable bypass it.
function viewModel(){
var self = this;
self.realName = ko.observable('test');
self.Name = ko.computed({
read: function(){
return self.realName();
},
write: function(value){
self.realName(value);
nameChanged(value);
}
});
function nameChanged(newName) {
console.log("name changed:", newName);
}
self.modifyName = function(){
self.realName(self.realName() + 'z');
}
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: Name" />
<br />
<button data-bind="click: modifyName">Modify</button>
Maybe set a particular CSS class on the field after the selection is made and put some logic around the ajax call to check if the field has the CSS class.
Instead of subscribing to the change event of the observable you could tie your ajax call to the change event of the ui element instead. You would get rid of name.subscribe(nameChanged); and add an event binding:
data-bind="textInput: name, event: { changed: nameChanged }"
An "old fashioned" way that works, but is not really elegant, is to temporarily dispose the subscription and then reattach it:
var myName = ko.observable();
var nameSub = myName.subscribe(nameChanged);
function nameChanged(newName) {
console.log("name changed:", newName);
}
function changeWithoutLog() {
nameSub.dispose();
myName("Test");
nameSub = myName.subscribe(nameChanged);
}
ko.applyBindings({ name: myName });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: name" />
<button data-bind="click: changeWithoutLog">change without post</button>

Knockout.js boolean not updating

I'm attempting to enable / disable an input field.
<input data-bind="disable: chatDisabled" id="send-message" type="textarea" class="input-area" value="">
And my knockoutJS
function MessagesViewModel() {
var self = this;
var socket = io.connect('http://127.0.0.1:4000');
self.messages = ko.observableArray([]);
self.chatSend = ko.observable();
self.questionChoice = ko.observable();
self.chatDisabled = ko.observable(false);
socket.on('receiveMessages', function(data) {
self.messages(data.messages);
var last = data.messages[data.messages.length-1];
self.chatDisabled = last.enforce || false;
scrollToBottom();
});
}
ko.applyBindings(new MessagesViewModel());
I've managed to get other parts working such as having a list of messages on the screen populate when the array is updated. However for the life of me I cannot get the input to toggle between disabled / enabled when it is changed within socket.on(
self.messages( updates corrected so why does self.chatDisabled not? (To be clear the variable JS side is updated however it the data-bind is not.
This should do the trick:
self.chatDisabled(last.enforce || false);
You were assigning a new value to your property instead of updating the observable value.

DataPicker not getting binded to textbox ? fiddle provided

Well in other cases i will get datepicker binded to my textbox which will be straight forward but not in this case .
Fiddle link : http://jsfiddle.net/JL26Z/1/ .. while to setup perfect seanrio i tried but unable to bind datepicker to textboxes . except that everything is in place
My code :
**<script id="Customisation" type="text/html">** // here i need to have text/html
<table style="width:1100px;height:40px;" align="center" >
<tr>
<input style="width:125px;height:auto;" class="txtBoxEffectiveDate" type="text" id="txtEffective" data-bind="" />
</tr>
</script>
The above code is used for my dynamic generation of same thing n no of time when i click each time on a button . So above thing is a TEMPLATE sort of thing .
My knockout code :
<div data-bind="template:{name:'Customisation', foreach:CustomisationList},visible:isVisible"></div>
<button data-bind="click:$root.CustomisatioAdd" >add </button>
I tried same old way to bind it with datepicker
$('#txtEffective').datepicker(); // in document.ready i placed
Actually to test this i created a textbox with some id outside script with text/html and binded datepicker to it and It is working fine sadly its not working for the textbox inside text/html and i want to work at any cost.
PS: well i haven't posted my view model as it is not required in this issue based senario
View model added with Js
var paymentsModel = function ()
{
function Customisation()
{
var self = this;
}
var self = this;
self.isVisible = ko.observable(false);
self.CustomisationList = ko.observableArray([new Customisation()]);
self.CustomisationRemove = function () {
self.CustomisationList.remove(this);
};
self.CustomisatioAdd = function () {
if (self.isVisible() === false)
{
self.isVisible(true);
}
else
{
self.CustomisationList.push(new Customisation());
}
};
}
$(document).ready(function()
{
$('#txtEffective').datepicker();
ko.applyBindings(new paymentsModel());
});
Any possible work around is appreciated
Regards
The best way I've found to do this is create a simple bindingHandler.
This is adapted from code I have locally, you may need to tweak it...
** code removed, see below **
Then update your template:
** code removed, see below **
By using a bindingHandler you don't need to try to hook this up later, it's done by knockout when it databinds.
Hope this is helpful.
EDIT
I created a fiddle, because I did indeed need to tweak the date picker binding quite a lot. Here's a link to the Fiddle, and here's the code with some notes. First up, the HTML:
<form id="employeeForm" name="employeeForm" method="POST">
<script id="PhoneTemplate" type="text/html">
<div>
<span>
<label>Country Code:</label>
<input type="text" data-bind="value: countryCode" />
</span>
<span><br/>
<label>Date:</label>
<input type="text" data-bind="datepicker: date" />
</span>
<span>
<label>Phone Number:</label>
<input type="text" data-bind="value: phoneNumber" />
</span>
<input type="button" value="Remove" data-bind="click: $parent.remove" />
</div>
</script>
<div>
<h2>Employee Phone Number</h2>
<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>
<div>
<input type="button" value="Add Another" data-bind="click: add" />
</div>
</div>
</form>
Note I removed the id=... from in your template; because your template repeats per phone number, and ids must be unique to be meaningful. Also, I removed the datepicker: binding from the country code and phone number elements, and added it only to the date field. Also - the syntax changed to "datepicker: ". If you need to specify date picker options, you would do it like this:
<input type="text" data-bind="datepicker: myObservable, datepickerOptions: { optionName: optionValue }" />
Where optionName and optionValue would come from the jQueryUI documentation for datepicker.
Now for the code and some notes:
// Adapted from this answer:
// https://stackoverflow.com/a/6613255/1634810
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
observable = valueAccessor(),
$el = $(element);
// Adapted from this answer:
// https://stackoverflow.com/a/8147201/1634810
options.onSelect = function () {
if (ko.isObservable(observable)) {
observable($el.datepicker('getDate'));
}
};
$el.datepicker(options);
// set the initial value
var value = ko.unwrap(valueAccessor());
if (value) {
$el.datepicker("setDate", value);
}
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') === 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
function Phone() {
var self = this;
self.countryCode = ko.observable('');
self.date = ko.observable('');
self.phoneNumber = ko.observable('');
}
function PhoneViewModel() {
var self = this;
self.PhoneList = ko.observableArray([new Phone()]);
self.remove = function () {
self.PhoneList.remove(this);
};
self.add = function () {
self.PhoneList.push(new Phone());
};
}
var phoneModel = new PhoneViewModel();
ko.applyBindings(phoneModel);
Note the very updated binding handler which was adapted from this answer for the binding, and this answer for handling onSelect.
I also included countryCode, date, and phoneNumber observables inside your Phone() object, and turned your model into a global variable phoneModel. From a debugger window (F12 in Chrome) you can type something like:
phoneModel.PhoneList()[0].date()
This will show you the current value of the date.
I notice that your form is set up to post somewhere. I would recommend instead that you add a click handler to a "Submit" button and post the values from your phoneModel using ajax.
Hope this edit helps.
Dynamic entities need to have datepicker applied after they are created. To do this I'd use an on-click function somewhere along the lines of
HTML
<!-- Note the id added here -->
<button data-bind="click:$root.CustomisatioAdd" id="addForm" >add </button>
<script>
$(document).on('click', '#addForm', function(){
$('[id$="txtEffective"]').datepicker();
});
</script>

Issue in getting index value

I am trying to store value in attribute in input tag which would like as follows.
The following code will repeat several times and the value for different radio can be saved using index.
<div data-bind="attr : { name : 'ex['+$index()+']' }>
<input type="radio" name="value" data-target="#modal" data-bind="click:fun.fill($index())"/>
</div>
From the above text box i can get the index of that input.And it points to a common modal function from bootstrap which opens a popup.
<div class="modal fade">
<select data-bind="attr:{name:'assignedResources['+$index()+'][repeatedType]'},
options : $root.repeats,value : repeatedType"></select>
</div><!-- /.modal -->
This is a sample of modal target.I want to call this modal as common.(i.e)it will be called from several places.I want to show appropriate popup for appropriate click from radio button.
But what i get is value of last index.Thats my problem
I'm not 100% sure I understand your question, but something like this...
this.abc = ko.observable('');
this.fun = {
var that = this;
fill: function(index) {
console.log(that.abc());
}
}
As far as I understand your problem, you need an observable to store your value, and then you can access it by any other function on your viewModel.
This is a jsFiddle with my initial approach, let me know if is what you needed or clarify instead:
http://jsfiddle.net/rdarioduarte/X8Rc4/
With a model like this:
var viewModel = function() {
this.abc = ko.observable('Value to store');
this.fun = function() {
alert(this.abc());
}
}
ko.applyBindings(new viewModel());
Thanks,
Dario
I do not quite understand about your question. But, maybe this may help:
1) Obtains index of your input element automatically on page load to viewModel function maybe need custom binding to handle that:
e.g custom binding:
ko.bindingHandlers.saveIndex = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var index = ko.utils.unwrapObservable(valueAccessor());
viewModel.fun(index); // accessing fun function on viewModel
// You can also access that function using bindingContext.$root
// bindingContext.$root.fun(index);
}
};
e.g html:
<input type="text" data-bind="saveIndex: $index()"/>
2) Store index to input value attribute using attr binding:
<input type="text" data-bind="attr: { value: $index() }">
3) Send your input attr value to viewModel function onClick:
<input type="text" value="thisIsExampleValue" data-bind="click: function() { $root.fun($element.value); }">
You also can use event binding for this behavior, Knockout.Event-binding
UPDATE:
Try this:
<div data-bind="attr : { name : 'ex['+$index()+']' }>
<input type="radio" name="value" data-target="#modal" data-bind="click: function() { fun.fill($index()) }"/>

KnockoutJS checked binding issue

I am relatively new to knockoutjs, but I seem to be having a problem with an observableArray of checkboxes, the checkboxes have some observable properties for checked, and disabled.
Using knockout I can check and uncheck the box, but it seems that once I interact with the checkbox manually (IE by clicking it with the mouse) the underlying data seems to be changing but I can't use knockout to check or uncheck the box anymore.
HTML
<div id="filterByPrice" data-bind="foreach: priceFilters">
<div>
<input type="checkbox" data-bind="attr: {id: $index, value: value, checked: checked, disable: disabled}" />
<span data-bind="text: label"></span>
</div>
</div>
Javascript
function FilterBy(name, value, label) {
this.name = name;
this.value = value;
this.label = label;
this.disabled = ko.observable(false);
this.checked = ko.observable(false);
}
$(function () {
var viewModel = {
priceFilters: ko.observableArray([
new FilterBy("price0", "0", "All Prices")])
};
ko.applyBindings(viewModel);
});
http://jsfiddle.net/paulwilliams0/EYEz2/
Is there something that I am doing that is wrong? Not only am I new to knockout, but I am new to MVVM in general. Thanks a lot
here i have a working version of your example:
http://jsfiddle.net/infantrash/hVac2/2/
data-bind attribute for your checkbox: use the build-in binding
handlers, attr: { id: $index } is ok, but value, checked and disable
are should not be in the curly brackets.
use knockout functions for your functionality instead of mixing jQuery into it.
function viewModel(){
var self = this;
self.priceFilters = ko.observableArray([
new FilterBy("price0", "0", "All Prices"),
new FilterBy("price1", "1", "1st Price")
]);
self.checkAllPrices = function(){
ko.utils.arrayForEach(self.priceFilters(), function(item){
item.checked(true);
})
};
self.unCheckAllPrices = function(){
ko.utils.arrayForEach(self.priceFilters(), function(item){
item.checked(false);
})
};
}
i changed the viewModel to a function instead of using the object literal notation, but that's just my preferred way, you can use the object literal notation if you want.

Categories