I have a select and I am binding the change event on it so upon changes my function will fire.
The thing is: when my page loads it fires this function, which I don't want to happen. I only want it to fire when the user actually changes the value of the dropdown.
My select dropdown:
<select data-bind="options: Main.Items,
optionsText: 'Name',
value: Main.SelectedItems,
optionsCaption: 'Please Select',
event: { change: function(data,event) { ItemClick(null) }}">
</select>
If anyone knows why it is firing on load, as well as how I can sort this out, it would be very much appreciated.
This is usually a red flag when using Knockout. Use either computed observables or subscriptions to trigger logic upon value changes. E.g.:
var MainViewModel = function(){
var self = this;
self.Items = [{Name: 'apple'}, {Name: 'orange'}];
self.SelectedItems = ko.observable();
function ItemClick(newValue) {
alert(ko.toJSON(newValue));
}
self.SelectedItems.subscribe(ItemClick);
}
ko.applyBindings({ Main: new MainViewModel() });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: Main.Items,
optionsText: 'Name',
value: Main.SelectedItems,
optionsCaption: 'Please Select'"></select>
Make sure you set SelectedItems as above, or explicitly to undefined. That is, if you were to initialize with null, the UI would use the optionsCaption to immediately set it to undefined again, triggering the subscription.
PS. Your View suggests multiple items can be selected (since the property's pluralized), if so you'd need the multiple attribute on your selecte as well as a combination of an observableArray for SelectedItems and the selectedOptions binding.
PPS. The code you posted does not behave the way your actual code does. That is, in the following snippet, you can see the event bound ItemClick function is not triggered until first time you change the drop down.
function ItemClick(x) {
alert(x);
}
var MainViewModel = function(){
var self = this;
self.Items = [{Name: 'apple'}, {Name: 'orange'}];
self.SelectedItems = ko.observable();
}
ko.applyBindings({ Main: new MainViewModel() });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: Main.Items,
optionsText: 'Name',
value: Main.SelectedItems,
optionsCaption: 'Please Select',
event: { change: function(data,event) { ItemClick(null) }}"></select>
Related
When I load my page i have a <select> positioned. I use knockout data-bind to fetch the values as
<select data-bind="attr: { id: 'input-' + id(), required: !allowBlank },
options: valuesArray,
optionsText: 'description',
optionsValue: 'itemValue',
optionsCaption: 'Select One...',
value: value,
enable: isEnabled,
event: { focus: $parent.getOptions}"
class="form-control" />
Right now I'm using the focus event because i can't seem to get hold of a event that behaves like onReady...
My problems are 2:
How can I trigger $parent.getOptions as soon as possible? Preferably before user interaction...
The property value always has a value, how can I set it? I imagine that I will have to w8 for the $parent.getOptions to return... or can I "force" it?!? And yes, they key I plan to force will be available in the set of the call mentioned above.
You are trying to populate the options of your select in a given event. In knockout this is accomplished by change the value of an observable, knockout takes care of the rest. You can bind this event to a function and inside this function you can call your $parent.getOptions logic. This is an example
var exampleVM = function () {
var self = this;
self.valuesArray = ko.observableArray([]);
self.value = ko.observable();
self.getOptions = function () {
var responseFromTheServer;
//Make ajax calls in here to get the values and format those to
//an array in the form [{ description: ..., itemValue: ... }]
self.valuesArray(responseFromTheServer);
// In here knockout will update the select in the html with your options
};
self.isEnable = ko.observable(true);
// The rest of your viewmodel ....
}
Dont forget to call apply bindings. The html then will look like
<select data-bind="attr: { id: 'input-' + id(), required: !allowBlank },
options: valuesArray,
optionsText: 'description',
optionsValue: 'itemValue',
optionsCaption: 'Select One...',
value: value,
enable: isEnabled,
event: { focus: getOptions}"
class="form-control" />
Calling $parent is only used when you have a viewmodel inside a viewmodel or inside an foreach binding. In your case everything is in the same viewmodel so no point in calling $parent.
If you want to trigger this as soon as possible you should call self.getOptions at the end of your viewmodel declaration like this.
var exampleVm = function () {
var self = this;
........
//This will execute as soon the viewmodel is created.
//Remember getOptions is just a plain javascript function
self.getOptions();
}
To set a value you need first to have the options populated and later set using the associated observable like this.
self.value(myNewValue);
Knockout will do nothing if you dont have any options in the select so you have to populate your options first and right after set the value.
{Edit}
I forgot to mention if you load your options right from the start you have no need for the focus event anymore. Remove this or the call to the server will be triggered again on focus.
I'm building a simple query interface for a table of data.
I have two dropdowns - one to select the field from the table, and one to select the query to perform on that field. The query dropdown items depend on the data type, so I have to wait until the field is selected to populate it.
All the bindings work if I use plain old select elements. But I want to apply the selectric plugin to them. Problem is, after calling $(element).selectric() on the elements, I don't know how to get it to "refresh" the items - so only the first dropdown contains the bound elements because it's initially populated. The second one never seems to get the updated 'query' elements.
I've tried using a custom ko binding, and calling .selectric() on the update like so:
ko.bindingHandlers.selectric = {
init: function(element, valueAccessor)
{
$(element).selectric();
},
update: function(element, valueAccessor)
{
$(element).selectric();
}
};
Here's my bindings for the two drop downs:
<select data-bind="options: $parent.fields,
optionsCaption: 'Select field...',
value: field_name,
event: { change: fieldSelected },
selectric: {}"></select>
<select data-bind="options: queries,
optionsCaption: 'Select query...',
selectric: queries"></select>
Here's the fiddle w/ viewmodel, etc. http://jsfiddle.net/rUNJY/12/. If you disable the selectric binding, it will work... how can I get the plugin to re-create the dropdown with updated items?
There are many ways to do this. Here are two:
1) Listen to the observable specified in your binding. This same as your way.
Usage:
<select data-bind="options: queries,
optionsCaption: 'Select query...',
selectric: queries"></select>
Code:
ko.bindingHandlers.selectric = {
update: function(element, valueAccessor)
{
ko.unwrap(valueAccessor()); //must use value in order for update to be called
$(element).selectric('refresh'); //must specify that plugin should refresh. See selectric documentation
}
};
Sample: http://jsfiddle.net/p4X4j/
2) Use the observable specified in the options binding. I prefer this solution since I don't need to specify the same observable in two bindings.
Usage:
<select data-bind="options: queries,
optionsCaption: 'Select query...',
selectric: {}"></select>
Code:
ko.bindingHandlers.selectric = {
init: function (element, valueAccessor, allBindingsAccessor) {
$(element).selectric('');
if(allBindingsAccessor().options.subscribe) {
var optionsSubscription = allBindingsAccessor().options.subscribe(function (newValue) {
$(element).selectric('refresh');
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
optionsSubscription.dispose();
});
}
}
};
Sample: http://jsfiddle.net/LbRGz/1/
I have a kendoUI dropdownlist that is in a template and is bound to a ViewModel, along with a span that is bound to the data item that is selected in the dropdownlist:
<p>
<label>Appointment Type: </label>
<select id="appointmentTypeDropDownList"
data-text-field="Name"
data-value-field="AppointmentTypeId"
data-role="dropdownlist"
data-bind="source:appointmentTypes, value:AppointmentTypeId"
data-autobind="true"
data-select="onSelected" />
</p>
<p><label>Duration:</label>
<span data-bind="text:selectedAppointment.Duration"></span> minutes
</p>
My ViewModel:
var viewModel = new kendo.observable({
appointmentTypes: appointmentTypesDataSource,
selectedAppointment : null,
});
Originally, I was using a hardcoded array of appointmentTypes, and setting the selectedAppointment to appointmentTypes[0] in the above viewModel declaration. That doesn't work now, because the datasource loads asynchronously. The viewModel is updated in the onSelected function:
var onSelected = function (e) {
var dataItem = this.dataItem(e.item.index());
viewModel.set("selectedAppointment", dataItem);
};
The template is in a window, and the span is empty the first time it loads, and then works thereafter (once the data has loaded from the first request).
My question is, how can I get the data binding of the span to work on the first request, so that it will display the Duration of the currently selected appointmentType from the list that is returned by the data source? Do I try and bind it to the selected data item of the dropdownlist? Is there a callback somewhere I should be using to do this? The template is inside of a kendoScheduler, if that matters:
var template = kendo.template($("#editor").html());
$("#scheduler").kendoScheduler({
editable: {
template: template()
}
});
Thanks!
Update: The template I've been working with is an editor for a Kendo UI Scheduler, which was not bound to its viewmodel, but was using part of the viewmodel for its datasource. In this case, trying to use the data-bind="events:{...}" syntax was causing the weird type errors I was getting. To wire up the databound event, Atanas let me know to use data-bound="onDataBound" and a global handler function (or alternately to configure my scheduler declaratively and bind it to the viewmodel). Using data-bound combined with the answer(s) below worked for me.
You could use the dataBound event on the dropdown and set selectedAppointment from there:
data-bind="source:appointmentTypes, value:AppointmentTypeId, events: { dataBound: onDataBound }"
and your view model:
var viewModel = new kendo.observable({
appointmentTypes: appointmentTypesDataSource,
selectedAppointment : null,
onDataBound: function(e) {
e.sender.select(0); // will trigger your change handler
}
});
You need to set the initial value of the selectedAppointment. This is the only way the span text will be set before the data source has been populated. Here is a runnable demo based on Northwind's Products:
<span data-bind="text:selectedProduct.ProductName"></span>
<select data-bind="source: products, value: selectedProduct"
data-text-field="ProductName"
data-value-field="ProductID"
data-role="dropdownlist"></select>
<script>
var o = kendo.observable({
selectedProduct: {
ProductID: 2,
ProductName: "Chang"
},
products: new kendo.data.DataSource({
transport: {
read: {
url: "http://demos.telerik.com/kendo-ui/service/products",
dataType: "jsonp"
}
}
})
});
kendo.bind(document.body, o);
</script>
Here is a live demo: http://jsbin.com/pawiz/1/edit
I am working on KnockoutJs and I have used valueUpdate:'afterkeydown' with textboxes and valueUpdate: 'change' with select elements to subscribe the respective changes and do SAME task. I want to call a same function via subscribe after each of their value changes.
To make it more clear, I'm onto something like below:
<input type="text" data-bind="value: val1, valueUpdate: 'afterkeydown'"/>
<input type="text" data-bind="value: val2, valueUpdate: 'afterkeydown'"/>
<select data-bind="value: sel1, valueUpdate:'change'">
<select data-bind="value: sel2, valueUpdate:'change'">
And in javascript, I've subscribed every changes like this:
viewModel.val1.subscribe(function () {
doSameWork();
});
viewModel.val2.subscribe(function () {
doSameWork();
});
viewModel.sel1.subscribe(function () {
doSameWork();
});
viewModel.sel2.subscribe(function () {
doSameWork();
});
################################
var doSameWork = function(){ alert('some work done!'); };
If I'm calling same function with every valueUpdate, then I was wondering if there is any way to subscribe every valueUpdate in a single subscribe(saving LOC) or anything like that rather than subscribing four value updates separately if I have to do same thing with each of them. Thanks in advance. :)
You can create only one event handler and "share" it to all observables
var ViewModel = function () {
var self = this;
self.onValueChanged = function (newValue) {
// your task
alert('some work done!');
};
self.val1 = ko.observable();
self.val1.subscribe(self.onValueChanged);
self.val2 = ko.observable();
self.val2.subscribe(self.onValueChanged);
self.sel1 = ko.observable();
self.sel1.subscribe(self.onValueChanged);
self.sel2 = ko.observable();
self.sel2.subscribe(self.onValueChanged);
};
See fiddle
#Accssharma :
The first time a computed is evaluated, the ko engine detects dependencies between observables and computeds. So when you write this :
ko.computed(function(){ val1(); val2(); sel1(); sel2(); doSameWork(); });
Ko registers val1, val2 sel1 and sel2 as a dependency of the computed.
This means if val1 or val2.. changed the computed may change too.
So when an observable (eg val1) is changed the computed needs to be evaluated.
That's why the doSameWork function is called.
I hope it helps.
I need to pass data from a click: event into another div. Here is a scenario:
There is a link on one side of the page.
<a data-bind="text: Name, click: $root.editAction"></a>
On the other side of the page, there is a hidden div.
<div data-bind="if: $root.editActionShow">
<input type="text" data-bind="value: Name"/>
</div>
I need to be able to pass $data from the click: event, do that hidden div.
Perhaps I am over-thinking this, but my viewModel has many different Actions buried deep in viewModel.DataGroups.DataGroup.ActionDataGroup and there is only 1 HTML form to edit action information, so I can't figure out how to make the form only show that one particular action I want to edit.
Here is another kicker. I prefer not to add any observables to my viewModel. Reason being is that I have to do .toJS() map it at the end, and then convert JSON into XML, which must validate against a pretty strict schema, so having extra elements is a bad thing. It will not pass validation, unless I manually remove them before conversion. However, I can add this.blah = function() {} objects to my viewModel, because .toJS() strips them during conversion.
UPDATE:
Aaand solution to all this is hands down hilarious
viewModel.editAction = function(data) {
viewModel.editActionFormShow(true);
ko.applyBindings(data, $('#myHiddenDiv')[0]);
};
From what I understand, you want something like a 'click-to-edit' function, which can be solved pretty neatly with just 2 custom bindings!
The great advantage about this approach is you won't polute your viewModel with extra observables.
Bindings:
ko.bindingHandlers.hidden = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.visible.update(element, function() {
return!value; });
}
};
ko.bindingHandlers.clickToEdit = {
init: function(element, valueAccessor,allBindingsAccessor){
var value = valueAccessor(),
input = document.createElement('input'),
link = document.createElement('a');
element.appendChild(input);
element.appendChild(link);
value.isEditing = ko.observable(false);
ko.applyBindingsToNode(link,{
text: value,
hidden: value.isEditing,
click: function(){
value.isEditing(true);
}
});
ko.applyBindingsToNode(input,{
value: value,
visible: value.isEditing,
hasfocus: value.isEditing
});
}
};
ViewModel
var vm = {
name: ko.observable()
}
The HTML
<div data-bind="clickToEdit: name"></div>
Working fiddle: http://jsfiddle.net/8Qamd/
All credit goes to Ryan Niemeyer.