I am trying to set focus on an input with knockout after the click event is fired but couldn't find a clean way to handle it without coupling with the DOM. Here is the JS code I have:
(function() {
var vm = {
text: ko.observable(),
items: ko.observableArray([])
}
vm.addItem = function() {
vm.items.push(vm.text());
vm.text(null);
}
ko.applyBindings(vm);
}());
This is my DOM:
<input type="text" data-bind="value: text" />
Send
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
Here is the JsFiddle sample: http://jsfiddle.net/srJUa/1/
What I want it to set focus on the input after the vm.addItem is completed. Any idea how this can be done cleanly, for example with a custom knockout binding?
Knockout has a built-in binding for manipulating the focus: The "hasfocus" binding.
So you just need to create a boolean property on your viewmodel and bind it on your input and set the property to true if you want to focus the input.
Or in your case you can binding directly to your text property, so when it is does not have any text it should has the focus:
<input type="text" data-bind="value: text, hasfocus: !text()" />
Demo JSFiddle.
OK, I have solved the issue by leveraging the hasfocus binding:
(function() {
var vm = {
text: ko.observable(),
items: ko.observableArray([]),
isFocused: ko.observable()
}
vm.addItem = function() {
vm.items.push(vm.text());
vm.text(null);
vm.isFocused(true);
}
ko.applyBindings(vm);
}());
HTML:
<input type="text" data-bind="value: text, hasfocus: isFocused" />
Send
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
Working sample: http://jsfiddle.net/srJUa/2/
Not sure if this is the best way, though.
Related
I'm creating simple application in KnockoutJS, here i need more input elements to update in a single button click. I have used ko.applyBindings(viewModel), but it throws below error.
Uncaught Error: You cannot apply bindings multiple times to the same element.
at applyBindingsToNodeInternal (VM88 knockout-debug.js:3287)
I have found one solution, if i use ko.cleanNode(node) it solves my problem. But i don't want this because, i have many input elements. So obviously i need to clean each node for every button click. Any other solution in generic to use applyBindings for same element or else, i need to change core?
applybind = function() {
viewModel = {
nValue: ko.observable(10)
}
//ko.cleanNode(document.getElementById("txt")); // Don't want this.
ko.applyBindings(viewModel);
}
ko.applyBindings({
nValue: ko.observable(100)
}); //initial load.
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" id="txt" data-bind="value: nValue" />
<input type="button" value="update value" onclick="applybind()" />
In general ko.applyBinding method should be called only once. After that you can simply change observable values and changes will immediately be reflected in the UI.
I've changed your snipped accordingly.
viewModel = {
nValue: ko.observable(100)
}
applybind = function() {
viewModel.nValue(10);
}
ko.applyBindings(viewModel); //initial load.
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" id="txt" data-bind="value: nValue" />
<input type="button" value="update value" onclick="applybind()" />
At times I have elements from a view model that I am accessing multiple times in the view and I would like to be able to assign an alias from the view context to shorten things up.
I'm looking for something that would be similar to the foreach as alias or with, but that I am able to use on a single arbitrary access.
As an example, in the following I might want to alias $root.form().budget.budgetEndDate to endDate
<input id="foo" type="text" data-bind="dateTimePicker: $root.form().budget.budgetEndDate" />
Then I could rewrite the binding as
<input id="foo" type="text" data-bind="dateTimePicker: endDate" />
Two options.
First, for demo purposes only but not quite recommended, you ould pollute the global namespace:
function Root() {
this.form = ko.observable(new Form());
}
function Form() {
this.budget = {
budgetEndDate: ko.observable("dummy budget end date")
};
window["endDate"] = this.budget.budgetEndDate;
}
ko.applyBindings(new Root());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div>
endDate outside form: <b data-bind="text: endDate"></b>
</div>
<div data-bind="with: form">
<div>inside form: <b data-bind="text: endDate"></b></div>
<div data-bind="with: budget">
<div>inside form.budget: <b data-bind="text: endDate"></b></div>
</div>
</div>
Second, you could place it on $root:
function Root() {
this.form = ko.observable(new Form());
this.endDate = this.form().budget.budgetEndDate;
}
function Form() {
this.budget = {
budgetEndDate: ko.observable("dummy budget end date")
};
}
ko.applyBindings(new Root());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div>
endDate outside form: <b data-bind="text: $root.endDate"></b>
</div>
<div data-bind="with: form">
<div>inside form: <b data-bind="text: $root.endDate"></b></div>
<div data-bind="with: budget">
<div>inside form.budget: <b data-bind="text: $root.endDate"></b></div>
</div>
</div>
However, on a subjective note, I'd consider this a design smell and/or an XY-problem. The place you bind to budgetEndDate should probably be the right context / view to access it directly. You should consider moving that observable to another part of your view model.
I have a listview that I bind manually:
$("#List").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataSource: new kendo.data.DataSource({
data: []
})
});
Initially I bind the list and later I load it with some data:
$("#ActButton").on("click", function(e) {
.
.
var list = $("#List").data("kendoListView");
list.dataSource.data(data);
list.refresh();
In my template being bound I have a control that I want to initialize as a numeric textbox. I was hoping to use data attributes, but it doesn't recognize:
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="#= Amount #" value="" />
How can I initialize the numeric textbox within a listview?
Note: I'm not using MVVM so I was using kendo.bind("body") to wire up the UI initially, but not using data-bind for the additional wire up of data.
I'm previously work on similar problem, i have a dojo example where i have a list view (kind of) and custom template with show/hide textbox and numeric textbox input. Click on the Product Name or Amount will make the textbox or numericTextbox visible, changing the textbox or numericTextbox value will also update the label value as it refering to the same thing on the dataSource.
First of all i'm usually work with kendo observable as vm like
var vm = kendo.observable({
dataSource: new kendo.data.DataSource({
Id: "id",
data: []
}),
act: function(){
var data = [
{id:1, productName: "Item A", amount: 1, isEditName: false, isEditAmount: false },
{id:2, productName: "Item B", amount: 2, isEditName: false, isEditAmount: false },
];
var list = $("#list").data("kendoListView");
list.dataSource.data(data);
list.refresh();
},
toggleProductName : function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditName;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditName",!editable);
},
toggleAmount: function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditAmount;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditAmount",!editable);
},
});
Create the listView and bind the page to vm
$("#list").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataBound: function(e){ kendo.bind($("#list"),vm); },
dataSource: vm.dataSource
});
kendo.bind($("#example"),vm);
Then here goes the html :
<div id="example">
<div id="list"></div>
<button id="act" data-bind="click:act">Act now</button>
</div>
My item Template :
<script type="text/x-kendo-template" id="ItemTemplate">
<tr>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-bind="visible: dataSource.get(#=id#).isEditName, value: dataSource.get(#=id#).productName " style="width:100px"/>
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditName, click: toggleProductName"> close </label>
<label data-id="#=id#" data-bind="invisible: dataSource.get(#=id#).isEditName, click: toggleProductName, text: dataSource.get(#=id#).productName"> #= productName #</label>
</td>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="100" style="width:100px" data-bind="visible: dataSource.get(#=id#).isEditAmount, value: dataSource.get(#=id#).amount" />
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditAmount, click: toggleAmount"> close </label>
<label data-id="#=id#" data-bind="invisible:dataSource.get(#=id#).isEditAmount, click: toggleAmount, text: dataSource.get(#=id#).amount"></label>
</td>
</tr>
</script>
The main thing here is that binding the template with the vm again on databound (so it can access the vm properties as well as updating the value from the input)
I bind the row to their respective record on dataSource
This is just a workaround for me, i hope it could help you as well
I have a basic input field with a knockout value:
<input type="text" data-bind="value: mytext"/>
However I want to perform som logic to my viewmodel after receiving a value for "mytext".
Initially I thought of some kind of post processing event ala "valueUpdate", but basically I just want to run a function after "enter" og "space" is hit. Do I need to write a new bindingHandler or is there a more straight forward knockout-apropriate way of doing this?
Basically what I´m trying to do is a combination of the jquery/autocomplete/multible and Ryan Niemeyers knockout-sortable example http://jsfiddle.net/rniemeyer/vgXNX .
My is in the div.container after the div.item, replacing the "Add task", like:
<div class="container">
<div class="item" data-bind="sortable:{template:'tagsTmpl',data:myTags, allowDrop:true"></div>
<input data-bind="value: mytext, event: {keypress: handleKey}"/>
<!-- Line above replacing this: Add Tag -->
</div>
basically I just want to run a function after "enter" og "space" is hit.
You could use the event binding.
ko.applyBindings({
mytext: ko.observable("initial value"),
handleKey: function(data, event) {
if (event.keyCode == 0x20) {
console.log("Space has been pressed!");
}
return true;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="text" data-bind="value: mytext, event: {keypress: handleKey}" />
I know how to bind to a property, but how do i bind to a property like:
Parent.Child
Using the hello world example on Knockout JS.com:
Html:
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
<h2>ChildProperty: <span data-bind="text: parentProperty.childProperty"> </span>!</h2>
Javascript:
var ViewModel = function(first, last) {
this.firstName = ko.observable(first);
this.lastName = ko.observable(last);
this.parentProperty = ko.observable(
{
childProperty: "I am a child Property"
});
this.fullName = ko.computed(function() {
// Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
return this.firstName() + " " + this.lastName();
}, this);
};
ko.applyBindings(new ViewModel("Planet", "Earth"));
I would like to create a binding to the childProperty.
I created a jsfiddle here
Thanks
So so very close!
You want
<span data-bind="text: parentProperty().childProperty"> </span>
Your updated fiddle http://jsfiddle.net/szk2e/2/
Adding an answer here as this is the best fit to my particular situation...
There is a situation where Tim's answer won't work. This is when the parent property can be undefined.
For example, if you're using the common pattern of itemsSource and selectedItem (where the user selects a single item from a list) selectedItem will be undefined on first evaluation, and whenever the user has undone their selection. Using the binding text:selectedItem().SomeProperty will "break" knockout, preventing bindings from being evaluated. Note, trying to short circuit this using the visible binding (e.g., text:selectedItem().SomeProperty, visible:selectedItem) will NOT work.
In this case, you have to use the with binding to switch the binding context to the value of the property. So, using OP's example...
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
<h2 data-bind="with:parentProperty">ChildProperty:
<span data-bind="text: childProperty"></span>!
</h2>
Note that the behavior for this binding is (from the docs)
The with binding will dynamically add or remove descendant elements depending on whether the associated value is null/undefined or not
If you also need to hide the container depending on whether the property is undefined or not, then you should use the <!-- ko --> virtual element to surround the container. More information can be found here.