Bind text input to value of observable array - javascript

I have an observable array that the user can add or remove items to. In addition I have one text input, and a DIV for each element in the array. When the user clicks on one of the DIVs I add the class "selected" to the DIV.
I want to bind the one input with the DIV that has the selected class.
Knockout:
self.tasks = ko.observableArray([]);
self.addTask = function() {
self.tasks.push(
{
id: "1",
content: ""
}
);
}
HTML:
<div data-bind="foreach: tasks">
<div class="wrapper" data-bind="text: content" class="selected"></div>
<div class="wrapper" data-bind="text: content"></div>
<div class="wrapper" data-bind="text: content"></div>
</div>
<label>Edit Task:</label> <input type="text" data-bind="value: content">
So basically every time one of the DIVs is selected I want to be able to updated the content parameter of the observable array that relates to the selected div, via the input field.

One way would be to create an observable that holds the index value of the selected task and bind your input accordingly:
<div data-bind="foreach: tasks">
<div class="wrapper" data-bind="text: content,
click: $root.selectTask,
css: {'selected': $root.selectedIndex() === $index()}">
</div>
</div>
<label>Edit Task:</label>
<input type="text" data-bind="value: tasks()[selectedIndex()].content">
You can see that the input value is bound to the content from one of the values in your tasks array. The selected class is also bound to that observable in a conditional.
Then your model would look like this (I defaulted the selectedIndex value to 0):
function Model() {
var self = this;
this.tasks = ko.observableArray();
this.addTask = function(id, content) {
self.tasks.push(
{
id: ko.observable(id),
content: ko.observable(content)
});
};
this.selectedIndex = ko.observable(0);
this.selectTask = function(task) {
self.selectedIndex(self.tasks.indexOf(task));
};
}
The selectTask function gets the new index based on where the user clicked and updates the observable. And all the DOM updating is taken care of for you after that.
Here's an example in jsFiddle: http://jsfiddle.net/MD35J/

Related

How do I bind inputs to observables in an observableArray?

I have an object (which is observable) and it has keys, each of which is observable. One of those keys is an array that contains other observables.
I want to be able to modify these values using HTML input fields. What came to my mind to do was to simply foreach through the observable array, and do value/textInput bindings to the inputs to modify them.
However, when modifying the text inputs - the value in the original doesn't change! Am I doing something wrong? Why aren't the bound values updating the way I expect?
I've broken down what I'm doing into a more generic version: http://jsfiddle.net/veqr2q6q/
<div class='liveExample'>
<div class="line-container" data-bind="foreach: {data: text, as: 'line'}">
<input type="text" data-bind="textInput: line" /><br />
</div>
<h2>Hello,</h2>
<ul data-bind='foreach: {data: text, as: "line"}'>
<li data-bind="text: line"></li>
</ul>
</div>
// Here's my data model
var ViewModel = function(first, last) {
this.text = ko.observableArray([
ko.observable(first),
ko.observable(last)
])
};
ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes Knockout get to work
I think in this scenario you want to use $rawData. I updated your fiddle here.
http://jsfiddle.net/veqr2q6q/2/
<div class='liveExample'>
<div class="line-container" data-bind="foreach: text">
<input type="text" data-bind="textInput: $rawData" /><br />
</div>
<h2>Hello,</h2>
<ul data-bind='foreach: {data: text, as: "line"}'>
<li data-bind="text: line"></li>
</ul>
</div>
As referenced in the Knockout JS docs
Usually [$rawData] will be the same as $data, but if the view model provided to Knockout is wrapped in an observable, $data will be the unwrapped view model, and $rawData will be the observable itself.

ng-repeat toggle slide, but others should be close

I am using below code for slide toggle, it
<div ng-repeat="item in $ctrl.searchitems track by $index">
<div class="quickinfo-overlap"> Content here...
<a class="btn-link" ng-click="$ctrl.quickinfoToggle(item)">quick info</a>
</div>
</div>
And I am using ng-repeat, so it is showing list, I want others list should be close or quickinfo false. so can I do?
This is the controller code:
function listingController($scope) {
var vm = this;
vm.quickinfo = false;
vm.quickinfoToggle = function(event) {
event.quickinfo = !event.quickinfo;
};
};
HTML:
<div ng-repeat="item in $ctrl.searchitems track by $index">
<div class="quickinfo-overlap"> Content here...
<a class="btn-link" ng-click="$ctrl.quickinfoToggle(item,$index)">quick info</a>
</div>
<div>
DIV that needs to be toggled on click
</div>
</div>
Javascript:
function listingController($scope) {
var vm = this;
$scope.toggleList = [];
for(var i=0;i< $scope.searchitems.length;i++)
$scope.toggleList[i] = false;
vm.quickinfoToggle = function(event,index) {
for(var i=0;i< $scope.toggleList.length;i++)
$scope.toggleList[i] = false;
$scope.toggleList[index] = true
event.quickinfo = !event.quickinfo;
};
};
while looping with ng-repeat to show the items, set ng-show:
<div class="quickinfo slide-toggle" ng-show="quickinfo == 1" ng-cloak>
Content here ....
</div>
<a class="btn-link" ng-click="quickinfo = 1">quick info</a>
Of course 1 is not fixed number, it should be unique to each item, like the index or id of the item.
The idea is when clicking the quickinfo link you assigned the clicked item's id not assign true\false to the quickinfo, and in ng-show check if the current id assigned to quickinfo is the same as this item id (or index).
Of course variable names can be changed.

Knockout binding with a handle to templated element

Knockout is just great but I'm a little bit confused on how to deal with DOM elements after they are generated. For example I have a collection of users. Each user has an Id:
var user = {
id : 123,
name : 'testUser',
age: 45
};
Using Knockout I bind my collection of described above data structure with the following html template:
<div data-bind="foreach: users">
<div class='user-wrapper'>
<span data-bind="text: name"></span>
<span data-bind="text: age"></span>
</div>
</div>
and now I want to change background color on user click:
$(".user-wrapper").click(function (e) {
//doesn't work - toggelClass is not a function
e.target.toggleClass("user-selected");
});
Once I hit a user target could be different (span or div), I need to make sure that I'm getting the right div. Moreover e.target doesn't work with "not a fucntion" error.
How can I access calling element to toggle the class?
How can I get a user id of that element to access other controls related to that id?
You should use the click binding in conjunction with the css binding:
<div data-bind="foreach: users">
<div class='user-wrapper' data-bind="click: toggleSelected, css: { 'user-selected': isSelected }">
<span data-bind="text: name"></span>
<span data-bind="text: age"></span>
</div>
</div>
Note that if you're ever tempted to use jQuery to manipulate DOM while you're using KnockoutJS (or client side MVVM libraries in general): don't. If you absolutely must, you probably need a custom binding handler, much like you'd use a directive for DOM manipulation in "that other" mvvm framework.
Here's a demo:
var user = {
id : 123,
name : 'testUser',
age: 45
};
var UserVm = function(data) {
var self = this;
self.name = data.name;
self.age = data.age;
self.isSelected = ko.observable(false);
self.toggleSelected = function() {
self.isSelected(!self.isSelected());
}
};
ko.applyBindings({ users: [new UserVm(user)] });
.user-selected { background-color: red; }
.user-wrapper:hover { cursor: pointer; background-color: pink; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="foreach: users">
<div class='user-wrapper' data-bind="click: toggleSelected, css: { 'user-selected': isSelected }">
<span data-bind="text: name"></span>
<span data-bind="text: age"></span>
</div>
</div>

ObservableArray not binding to GUI

I'm new with knockout.js and trying to fix data binding on a site that is build on Laravel and is using knockout.js.
Observable array works well and items can be pushed and popped without issues. The problem is with the binding to GUI. When items are pushed to array those are added to GUI, but nothing else works, like removing items, and also when adding more items later on those are added on the top of the GUI element list, not added after existing items on the GUI. The observable array is having correct items after push/pop/removeall, its just not reflecting to GUI.
I guess that the problem is that observable array is not binded to GUI, but I cannot figure out what could be wrong.
Stripped code:
Chat.init = function(){
Chat.viewModel = new Chat.ViewModel;
ko.applyBindings(Chat.viewModel, $('#msg_canvas').get(0));
};
Chat.ViewModel = function(){
self.messages = ko.observableArray();
self.setMessages = function(msgs){
_.each(msgs, function(msg){
self.messages.push(msg);
});
};
self.clearMessages = function(data, e){
self.messages.removeAll();
}
}
clearMessages is called via onclick: data-bind="click: $parent.clearMessages
The HTML is this:
<div id="msg_canvas" class="msg-wrap col-md-12"
style="height:274px;overflow-y:scroll;" data-bind="foreach: messages">
<div class="media msg">
<div class="media-body">
<span data-bind="text: sent_at"></span>
<small class="col-lg-10" data-bind="text: message"></small>
</div>
</div>
Any help or pointer to what could be causing the problem would be highly appreciated.
UPDATE: added inner HTML which was not included to post before
You need to have a control inside the div to hold your messages, like a <span> or <p>. Otherwise, you're simply doing the foreach without outputting the values. So your div should look something like this, using $data to access the value:
<div id="msg_canvas" data-bind="foreach: messages">
<p data-bind="text: $data"></p>
</div>
Here's a working snippet based on your code (setMessages slightly modified / hard coded with values):
ViewModel = function(){
self.messages = ko.observableArray([]);
self.setMessages = function(){
var msgs = ['message','message','message'];
_.each(msgs, function(msg){
self.messages.push(msg + ' ' + self.messages().length);
});
};
self.clearMessages = function(data, e){
self.messages.removeAll();
}
self.removeMessage = function(item){
self.messages.remove(item);
}
};
ko.applyBindings(new ViewModel());
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="msg_canvas" class="msg-wrap col-md-12"
style="height:274px;overflow-y:scroll;border: black solid 1px" data-bind="foreach: messages">
<p data-bind="text: $data"></p>
<input type="button" data-bind="click: removeMessage" value="Remove Item" />
</div>
<input type="button" data-bind="click: setMessages" value="Add Message" />
<input type="button" data-bind="click: clearMessages" value="Remove All" />

How can a data-bind to an element within a Kendo-Knockout listview?

I have a rather sophisticated template for Kendo ListView using knockout-kendo.js bindings. It displays beautifully. My problem is that I need to use the visible and click bindings in parts of the template, but I can't get them to work. Below is a simplified version of my template. Basically, deleteButtonVisible determines whether the close button can be seen, and removeComp removes the item from the array.
<div class='template'>
<div >
<div style='display:inline-block' data-bind='visible: deleteButtonVisible, event: {click: $parent.removeComp}'>
<img src='../../../Img/dialog_close.png'></img>
</div>
<div class='embolden'>#= type#</div><div class='label1'> #= marketArea# </div>
<div class='label2'> #= address# </div>
<!-- more of the same -->
</div>
The view model:
function CompViewModel() {
var self = this;
self.compData = ko.observableArray().subscribeTo("compData");
self.template = kendo.template(//template in here);
self.removeComp = function (comp) {
//do something here
}
}
html:
<div class="row" >
<div class="col-md-12 centerouter" id="compDiv" >
<div class="centerinner" id="compListView" data-bind="kendoListView: {data: compData, template: template}"></div>
</div>
</div>
finally, sample data:
{
type: "Comparable",
marketArea: "",
address: "2327 Bristol St",
deleteButtonVisible: true
},
Take in count that the deleteButtonVisible must be a property on the viewModel linked to the view.You are not doing that right now. The click element can v¡be access from the outer scope of the binding and remove the $parent.He take the method from the viewmodel. Take in count that every thing that you take on the vie must be present on the view model for a easy access.

Categories

Resources