I'm a new programmer for knockout.
Here's a question about ko.observableArray while practicing.
I give an zero-based index array for initial data that will show with JSON while clicking button.
But when I try to update any value from input field but I cannot get the new JSON after clicking button.
And I think the problem is that there's no index in my array.How can I get the new JSON after clicking
function ViewModel(inputs){
this.inputs = ko.observableArray(inputs);
this.getData = function(){
this.jsonData(ko.toJSON(this.inputs));
};
this.jsonData = ko.observable('');
};
var initialData = [ 'Jan', 'Feb', 'Mar', 'etc' ];
var viewModel = new ViewModel(initialData);
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<ul data-bind="foreach: inputs">
<li>
The current item is: <input type='text' data-bind="value: $data"></b>
</li>
</ul>
<textarea rows='5' cols='60' data-bind='value: jsonData'> </textarea>
<button data-bind='click: getData'>get Data</button>
I thing due to $index you can't get this updated JSON
if you add key value pair in "initialData" you will get updated JSON data
Here blow working code :
function ViewModel(inputs) {
this.inputs = ko.observableArray(inputs);
this.getData = function () {
this.jsonData(ko.toJSON(this.inputs));
};
this.jsonData = ko.observable('');
};
var initialData = [{ name: 'Jan' }, { name: 'Feb' }, { name: 'Mar' }, { name: 'etc' }];
var viewModel = new ViewModel(initialData);
ko.applyBindings(viewModel);
<ul data-bind="foreach: inputs">
<li>
The current item is: <input type='text' data-bind="value: name"><br />
</li>
</ul>
<textarea rows='5' cols='60' data-bind='value: jsonData'> </textarea>
<button data-bind='click: getData'>get Data</button>
Related
<div data-bind="with: SimpleListModel">
<form data-bind="submit: addItem" >
New item:
<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
<p>Your items:</p>
<select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>
</div>
<div data-bind="with: SimpleListModel2">
<div data-bind="foreach: baselist">
<div>
<span data-bind="text: basename"></span>
<div data-bind="foreach: subItems">
<span data-bind="text: subitemname"></span>
Del
</div>
</div>
<button data-bind="click:$parent.addChild">Add</button>
</div>
</div>
this is the viewmodel
var SimpleListModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd()); // Adds the item. Writing to the "items" observableArray causes any associated UI to update.
this.itemToAdd(""); // Clears the text box, because it's bound to the "itemToAdd" observable
}
}.bind(this); // Ensure that "this" is always this view model
};
var initialData = [
{ basename: "Danny", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
},
{ basename: "Sensei", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
}];
var SimpleListModel2 = function(baselist) {
var self= this;
self.baselist= ko.observableArray(baselist);
self.addChild = function(list) {
alert(list.basename);
}.bind(this);
self.removecard = function (data) {
//tried
data.baselist.subItems.remove(data);
data.subItems.remove(data);
$.each(self.baselist(), function() { this.subItems.remove(data) })
};
};
var masterVM = (function () {
var self = this;
self.SimpleListModel= new SimpleListModel(["Alpha", "Beta", "Gamma"]);
self.SimpleListModel2= new SimpleListModel2(initialData);
})();
ko.applyBindings(masterVM);
This is a small code snippet i constructed of my project. Can someone make remove card work? last two increments of my questions are of the same type. but this question is the highest i reach.
removecard doesn't work now in this scenario at least for me.
Use $parents[index] to get to specific parent. http://knockoutjs.com/documentation/binding-context.html.
$parents[0] --> parent
$parents[1] --> grand parent
etc
var initialData = [
{ basename: "Danny", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
},
{ basename: "Sensei", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
}];
var SimpleListModel2 = function(baselist) {
var self= this;
self.baselist= ko.observableArray(baselist);
self.addChild = function(list) {
alert(list.basename);
}.bind(this);
self.removecard = function (data) {
//tried
console.log(data);
};
};
var masterVM = (function () {
var self = this;
self.SimpleListModel2= new SimpleListModel2(initialData);
})();
ko.applyBindings(masterVM);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="with: SimpleListModel2">
<div data-bind="foreach: baselist">
<div>
<span data-bind="text: basename"></span>
<div data-bind="foreach: subItems">
<span data-bind="text: subitemname"></span>
Del
</div>
</div>
<button data-bind="click:$parent.addChild">Add</button>
</div>
</div>
I need some help here: https://jsfiddle.net/vhaurnpw/
I want to do a simple list, which is filterable by the input on top and updates itself..
JS/Knockout:
var viewModel = {
query: ko.observable(''),
places: ko.observable(data),
search: function(value) {
viewModel.places = [];
console.log(value)
for (var i = 0; i < data.length; i++) {
if(data[i].name.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
viewModel.places.push(data[i]);
}
console.log(viewModel.places);
}
}
};
viewModel.query.subscribe(viewModel.search);
ko.applyBindings(viewModel);
HTML:
<form acion="#" data-bind="submit: search">
<input placeholder="Search" type="search" name="q" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off">
</form>
<ul data-bind="foreach: places">
<li>
<span data-bind="text: name"></span>
</li>
</ul>
List will be rendered, but it when you type something, it doesn't show you the result.
Instead when you lookup the console, you will see the console.log and it updates just fine!
so how do i refresh my HTML? :(
There are following issues in your code.
places needs to be an ObservableArray and not an Observable so that you can track the addition/removal from the Observable Array. So, change code From places: ko.observable(data) To places: ko.observableArray(data),
viewModel.places is function, so when you assign another value like viewModel.places = [], it is assigning an empty array to the viewModel.places. In order to modify the value of the viewModel.places, you need to call it as a function like viewModel.places([]);
Note: Your code doesn't add the data back in case the textbox is cleared, I hope you got the solution to the problem and you can resolve this issue as well.
Complete Working Code:
var data = [
{ name: 'Isartor'},
{ name: 'Sendlinger Tor'},
{ name: 'Marienplatz'},
{ name: 'Stachus'},
{ name: 'Bayerischer Rundfunk'},
{ name: 'Google München'},
{ name: 'Viktualienmarkt'},
{ name: 'Museumsinsel'},
{ name: 'Tierpark Hellabrunn'},
{ name: 'Allianz Arena'},
{ name: 'Olympia-Park'},
{ name: 'Flaucher-Insel'}
];
var viewModel = {
query: ko.observable(''),
places: ko.observableArray(data),
search: function(value) {
viewModel.places([]);
console.log(value)
for (var i = 0; i < data.length; i++) {
if(data[i].name.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
viewModel.places.push(data[i]);
}
console.log(viewModel.places);
}
}
};
viewModel.query.subscribe(viewModel.search);
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<form acion="#" data-bind="submit: search">
<input placeholder="Search" type="search" name="q" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off">
</form>
<ul data-bind="foreach: places">
<li>
<span data-bind="text: name">asd</span>
</li>
</ul>
I am trying to create a list of editable inputs from a list of items. I want the user to be able to edit any of the items, but if they change there mind they can click a button and reset it back to the way it was.
So far I have everything working except the reset.
my html
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy(item)"/>
<button ng-click="reset(item)">
x
</button>
</div>
{{list}}<br>
{{selected_item_backup}}
</div>
my controller
function TestController($scope) {
$scope.selected_item_backup = {};
$scope.list = [ { value: 'value 1' }, { value: 'value 2' }, { value: 'value 3' } ];
$scope.reset = function (item) {
// i know this wont work for many reasons, it is just an example of what I would like to do
item = $scope.selected_item_backup;
};
$scope.copy = function (item) {
angular.copy(item, $scope.selected_item_backup);
};
}
and here is a fiddle
http://jsfiddle.net/ryanmc/1ab24o4t/1/
Keep in mind that this is a simplified version of my real code. My objects will have many editable fields each. Also they are not indexed, and so the index cannot be relied on. I just want to be able to assign the original item on top of the new and have it replaced.
This is work solution jsfiddle
function TestController($scope) {
$scope.list = [{
value: 'value 1'
}, {
value: 'value 2'
}, {
value: 'value 3'
}];
var orgiinalList = [];
angular.forEach($scope.list, function(item) {
orgiinalList.push(angular.copy(item));
});
$scope.reset = function(index) {
$scope.list[index] = angular.copy(orgiinalList[index]);
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" />
<button ng-click="reset($index)">
x
</button>
</div>
{{list}}
<br>
</div>
Changing your reset function so that it looks like this:
$scope.reset = function(index) {
$scope.list[index].value = "value "+ (index+1);
};
will make it so that your 'reset' buttons restore what would be those original values...
Angular controller:
function TestController($scope) {
$scope.selected_item_backup = {};
$scope.list = [{
value: 'value 1'
}, {
value: 'value 2'
}, {
value: 'value 3'
}];
$scope.reset = function(index) {
$scope.list[index].value = "value " + (index+1);
};
$scope.copy = function(item) {
angular.copy(item, $scope.selected_item_backup);
};
}
HTML:
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy(item)" />
<button ng-click="reset($index)">
x
</button>
</div>
</div>
The values of your array are directly modeled by those inputs - therefore as a user types changes into those inputs they are directly manipulating the $scope.list array so you cant really use it as a reference... Hope that makes sense.
You can't use global functions as controllers in newer versions of angular but you can register your controllers in your application:
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy($index,item)"/>
<button ng-click="reset($index,item)">
x
</button>
</div>
{{list}}<br>
{{selected_item_backup}}
function TestController($scope) {
$scope.selected_item_backup = [];
$scope.list = [ { value: 'value 1' }, { value: 'value 2' }, { value: 'value 3' } ];
$scope.reset = function (index) {
$scope.list[index].value = $scope.selected_item_backup[index];
};
$scope.copy = function (index,item) {
// backup the old value
$scope.selected_item_backup[index] = item.value;
};
}
I have been following some tutorials and trying to follow knockout.js. I am not able to edit any data that is newly added.
My JS Code:
var data = [
new ListData("Programmer", 1),
new ListData("Testing", 2),
new ListData("DBA", 3),
new ListData("Lead", 4),
new ListData("Manager", 5)
];
function ListData(desig, id) {
return {
Desig: ko.observable(desig),
Id: ko.observable(id)
};
}
var viewModel = {
list: ko.observableArray(data),
dataToAdd: ko.observable(""),
selectedData: ko.observable(null),
addTag: function () {
this.list.push({ Desig: this.dataToAdd() });
this.tagToAdd("");
},
selecData: function () {
viewModel.selectedData(this);
}
};
$(document).on("click", ".editData", function () {
});
ko.applyBindings(viewModel);
My View Code:
<input type="text" data-bind="value: dataToAdd" />
<button data-bind="click: addData">
+ Add
</button>
<ul data-bind="foreach: list">
<li data-bind="click: $parent.selecData">
<span data-bind="text: Desig"></span>
<div>
Edit
</div>
</li>
</ul>
<div id="EditData" data-bind="with: selectedData">
Designation:
<input type="text" data-bind="value: Desig" />
</div>
I am able to edit the data which already exists like - Programmer, Testing, DBA...but if I add a new data..I am not able to edit. Please assist.
Your addData (you named it addTag in the code, but call addData in the HTML) function doesn't construct new elements the same way your initial data creation does. Note: since your ListData constructor explicitly returns an object, new is superfluous.
addData: function () {
this.list.push(ListData(this.dataToAdd(), ""));
},
I try to search by name in observable array. Here's my code:
<input class="form-control" data-bind="value: Query, valueUpdate: 'keyup'" autocomplete="off">
And my code in ViewModel
viewModel.Query = ko.observable('');
viewModel.search = function(value) {
viewModel.TestList.removeAll();
for (var x in viewModel.TestList) {
if (viewModel.TestList[x].Name.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
viewModel.TestList.push(viewModel.TestList[x]);
}
}
}
viewModel.Query.subscribe(viewModel.search);
First: I would like to search by name string.
Two: Is there any other sollutions to not remove all elements from the view? I mean when query string is empty, there should be all list again.
Now I have error message:
TypeError: viewModel.TestList[x].Name is undefined
Use a computed observable array to show search results, along these lines:
var viewModel = {
items: [ { Name: "Apple part" }, { Name: "Apple sauce" }, { Name: "Apple juice" }, { Name: "Pear juice" }, { Name: "Pear mush" }, { Name: "Something different" } ]
};
viewModel.Query = ko.observable('');
viewModel.searchResults = ko.computed(function() {
var q = viewModel.Query();
return viewModel.items.filter(function(i) {
return i.Name.toLowerCase().indexOf(q) >= 0;
});
});
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input class="form-control" data-bind="value: Query, valueUpdate: 'keyup'" autocomplete="off">
<h3>Search results:</h3>
<ul data-bind="foreach: searchResults">
<li data-bind="text: Name"></li>
</ul>
<h3>All items:</h3>
<ul data-bind="foreach: items">
<li data-bind="text: Name"></li>
</ul>
This also removes the need for a subscription or seperate function.
This example utilizes:
A regular observableArray for storing all items (this list is always the same, regardless of your search query);
A read-only computed observable which returns a filtered array of items that match your query;
The array filter method (you call it on the observableArray, but KO just forwards it to the underlying array);
As you can see in the example, items will always contain all objects, and searchResults is just a filtered read-only view on that array.