I'm trying to clear and reset my input and select fields using knockoutJS. below is a snippet on how it kinda works but edited for brevity.
Javascript Code
view = (function()
{
var self = this;
self.anItem = ko.observable(new AnItem());
ko.applyBindings(self)
self.addItem = function()
{
self.somewhere.push(this);
self.anItem = new AnItem(); /////this doesn't clear the form
}
})();
function AnItem()
{
this.Name= "";
this.Type= 1
}
Html Code
<tfoot data-bind="with: anItem">
<tr>
<td><input type="text" data-bind="value= Name" /></td>
<td><select data-bind="options: pretendThisIsPopulated, optionsValue:'Value', optionsText:'Text', value:Type" /></td>
<td>add</td>
</tr>
</tfoot>
self.anItem(new AnItem()); //should say instead
You can clear those like this.
Edited
view = (function()
{
var self = this;
var AnItem = function (name,type)
{
var $this = this;
this.Name = ko.observable(name | "");
this.Type = ko.observable(type | 1);
};
self.anItem = ko.observable(new AnItem("Name",1));
ko.applyBindings(self)
self.addItem = function()
{
self.somewhere.push(this);
self.anItem = new AnItem("",1);
};
})();
Use self.addItem(); to clear the data where you want.
Related
I continue learning knockout and continue facing weird issues I don't know how to overcome.
I have the following html page and js script:
HTML:
<div data-bind="debug: $data, foreach: objects">
<span hidden="hidden" data-bind="value: type.id"></span>
<input type="text" data-bind="value: type.title" />
<button type="button" data-bind="click: $parent.removeObject">- </button>
</div>
<div class="control-group form-inline">
<select data-bind="options: availableTypes, optionsValue: function(item) {return item;},
optionsText: function(item) {return item.title;}, value: itemToAdd.type,
optionsCaption: 'Select types...'"></select>
<button type="button" data-bind="click: addObject">+</button>
</div>
</div>
JS:
function model() {
var self = this;
var types = [new Type("1"), new Type("2"), new Type("3")];
var objects = [new Object("1")];
self.objects = ko.observableArray(objects);
self.usedTypes = ko.computed(function() {
return types.filter(function(type) {
for (var j = 0; j < self.objects().length; j++) {
if (self.objects()[j].type.id === type.id) {
return true;
}
}
return false;
});
}, self);
self.availableTypes = ko.computed(function() {
return types.filter(function(type) {
for (var j = 0; j < self.usedTypes().length; j++) {
if (self.usedTypes()[j].id === type.id) {
return false;
}
}
return true;
});
}, self);
self.itemToAdd = new Object();
self.addObject = function() {
self.objects.push(self.itemToAdd);
self.itemToAdd = new Object();
};
self.removeObject = function(object) {
self.objects.remove(object);
};
};
function Object(type) {
var self = this;
self.type = new Type(type);
}
function Type(id) {
var self = this;
self.id = id;
self.title = id;
}
ko.applyBindings(new model());
I simplified model to show the error. The thing is that knockout claims it is illegal to call do this:
<span hidden="hidden" data-bind="value: type.id"></span>
Because it can't find property id in context. As far as I can see it is there and everything ok with it.
Could, please, anybody point me at my mistakes?
p.s. Here is a JsFiddle
ADDITION
Thanks to #Daryl's help I was able to localize the issue. If I replace
self.itemToAdd = new Object();
self.addObject = function() {
self.objects.push(self.itemToAdd);
self.itemToAdd = new Object();
};
with:
self.itemToAdd = new Object();
self.addObject = function() {
self.objects.push(new Object(1));
self.itemToAdd = new Object();
};
though, the following code still doesn't work:
self.itemToAdd = new Object("1");
self.addObject = function() {
self.objects.push(self.itemToAdd);
self.itemToAdd = new Object();
};
It seems itemToAdd objects is populated incorrectly from html elements it's binded to. But I still don't know what exactly is wrong.
You've allowed your type dropdown to be unset. When knockout shows the caption, it clears the actual value. This means that, by rendering the UI, your itemToAdd.type is cleared.
Your second approach solves this by not using the data-bound instance.
Furthermore:
I wouldn't overwrite the Object constructor if I were you... Find a different name.
Make sure your itemToAdd has observable properties if you want to do two-way binding to the UI.
I want to build a HTML page that allows the user to construct an object that can then be posted as JSON to an internally hosted service (similar to the Chrome Advanced Rest Client). The user must be able to add and remove properties.
My model is not correct because each property is treated like an object with the properties 'name' and 'value'. I end up with an array of objects instead of an object with properties.
Here is a snippet of the HTML:
<table>
<thead>
<tr>
<th>Property Name</th>
<th>Property Value</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: myFieldList">
<tr>
<td><input data-bind="value: name" /></td>
<td><input data-bind="value: value" /></td>
<td><span class="removeVar" data-bind="click: removeProperty">Remove property</span></td>
</tr>
</tbody>
</table>
<p>
<span id="addVar" data-bind="click: addProperty">Add Property</span>
</p>
<textarea name="tasks" data-bind="value: ko.toJSON(myFieldList)"></textarea>
Here is the JS:
<script type="text/javascript">
function dynamicProperty(name, value) {
var self = this;
this.name = name;
this.value = value;
}
function fieldModel() {
var self = this;
//start with 2 empty properties
self.myFieldList = ko.observableArray([
new dynamicProperty("", ""),
new dynamicProperty("","")
]);
var noTracker = self.myFieldList.length;
self.removeProperty = function (dynamicProperty) {
self.myFieldList.remove(dynamicProperty);
}
self.addProperty = function () {
noTracker++;
self.myFieldList.push(new dynamicProperty(this.name,this.value));
}
}
ko.applyBindings(fieldModel);
</script>
What I get in the textarea is output like this:
[{"name":"test name 1","value":"test value 1"},{"name":"test name 2","value":"test value 2"}]
What I want is output like this:
{"test name 1":"test value 1","test name 2":"test value 2"}
I fear this is rather trivial but in my defense I am very new to JS and Knockout, so any help you can offer would be hugely appreciated. Thanks.
You may want to do something like this to get it done .
Example : https://jsfiddle.net/9aLvd3uw/79/
HTML
<table>
<thead>
<tr>
<th>Property Name</th>
<th>Property Value</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: myFieldList">
<tr>
<td><input data-bind="textInput: name" /></td>
<td><input data-bind="textInput: value" /></td>
<td><span class="removeVar" data-bind="click: removeProperty">Remove property</span></td>
</tr>
</tbody>
</table>
<p>
<span id="addVar" data-bind="click: addProperty">Add Property</span>
</p>
<textarea name="tasks" data-bind="value: myFieldList2"></textarea>
JS
function dynamicProperty(name, value) {
var self = this;
self.name = ko.observable(name || '');
self.value = ko.observable(value || '');
}
function fieldModel() {
var self = this;
self.name = ko.observable();
self.value = ko.observable();
self.myFieldList = ko.observableArray([
new dynamicProperty("test_name_1", "test value 1"),
new dynamicProperty("test_name_2","test value 2")
]);
var noTracker = self.myFieldList.length;
self.myFieldList2 = ko.computed(function () {
var string = '{';
ko.utils.arrayForEach(self.myFieldList(), function (item) {
string += item.name() + ': ' + item.value() + ',';
});
string = string.replace(/,\s*$/, "");
string+='}';
return string;
});
self.removeProperty = function (dynamicProperty) {
self.myFieldList.remove(dynamicProperty);
}
self.addProperty = function () {
noTracker++;
self.myFieldList.push(new dynamicProperty('',''));
}
}
ko.applyBindings(fieldModel);
What you need is a "reducer".
A simple (naive too) implementation would be this:
function reduce(input, step, init) {
for(var i = 0; i < input.length; i++) {
init = step(init, input[i]);
}
return init;
}
Then you call it like this:
var in = [{"name":"test name 1","value":"test value 1"},{"name":"test name 2","value":"test value 2"}];
var out = reduce(in, function(result, item) {
result[item.name] = item.value;
return result;
}, {});
console.log(out);
What it does is that it iterates through your array and "accumulates" the result of each step in a single item. Could be the sum of numbers in an array where the "accumulator" would be a number instead of an object.
I advise you don't write your own but instead use lodash, it comes with a _.reduce function that's optimized.
I have this View-Model structure:
function PluginViewModel(name, code) {
var self = this;
self.name = ko.observable(name);
self.code = ko.observable(code);
}
function item() {
var self = this;
self.id = ko.observable();
self.listIndex = ko.observable();
self.value = ko.observable();
self.label = ko.observable();
self.tabPos = ko.observable();
self.plugins = ko.observableArray();
};
function TableViewModel() {
var self = this;
self.columns = ko.observableArray();
self.filteredColumns = ko.observableArray();
// To bind View Fields List
self.selectedColumn = ko.observable();
self.selectColumn = function (p) {
self.selectedColumn(p);
}
// To bind Table Columns List
self.tmpColSelected = ko.observable();
self.tmpColSelect = function (p) {
self.tmpColSelected(p);
}
// Plugin selected
self.selectedPlugin = ko.observable();
self.selectPlugin = function (p) {
self.selectedPlugin(p);
}
// New Plugin
self.newPlugin = ko.observable();
self.addPlugin = function () {
self.selectedColumn().plugins.push(self.newPlugin());
$('#dialogAddPlugin').wijdialog('close');
}
}
function SchemaViewModel() {
var self = this;
self.name = ko.observable();
self.tables = ko.observableArray();
self.selectedTable = ko.observable();
self.selectTable = function (p) {
self.selectedTable(p);
}
}
Then this is binding code:
schema = new SchemaViewModel();
ko.applyBindings(schema);
There is a Master-Detail scenario where an unordered html list is binded with selectedColumn field of tableViewModel.
When a list item is selected, in Detail view I can add plugins with an Add button that shows a Wijmo dialog.
This is the code of dialog:
<div id="dialogAddPlugin" title="Plugin editor" data-bind="with: newPlugin">
<textarea data-bind="value: code, valueUpdate: 'input'"></textarea>
<div>
Name: <input data-bind="value: name, valueUpdate: 'input'" />
<button id="btnSaveNewPlugin" data-bind="click: $parent.addPlugin">Save</button>
</div>
</div>
The problem is that addPlugin method is never called even if view model appears to be correctly setted.
What's wrong?
Thank you
I have a knockout array that I use to populate a table. I also have inputs that I use to data-bind to a variable in Filtered Array. . .I need to use these inputs to filter my array and only display that array....how can I do this in my FilteredAray below where the ? is.
<td><input data-bind="value: First, valueUpdate: 'afterkeydown'" /></td>
<td><input data-bind="value: Second, valueUpdate: 'afterkeydown'"/></td>
<td><input data-bind="value: Third, valueUpdate: 'afterkeydown'" /></td>
<td><input data-bind="value: Fourth, valueUpdate: 'afterkeydown'"/></td>
Knockout View Model:
self.First = ko.observable('');
self.Second = ko.observable('');
self.Third = ko.observable('');
self.Fourth = ko.observable('');
self.FilteredArray = ko.computed(function () {
var First = self.First();
var Second = self.Second();
var Third = self.Third();
var Fourth = self.Fourth();
? Filter self.PeopleArray()
}, self);
I am trying to filter an observable array PeopleArray() based on the inputs
You're looking for array filter. It returns you a new array with only the results that return true form the callback.
self.array = ko.obserableArray();
self.filter1 = ko.observable();
self.filter2 = ko.observable();
self.filter3 = ko.observable();
self.filter4 = ko.observable();
self.array = ko.observableArray();
self.filteredArray = ko.computed(function () {
return ko.utils.arrayFilter(self.array(), function (item) {
//logic for filter1
//logic for filter2
//logic for filter3
//logic for filter4
//if result matches the filter for return true, if not test next filter
});
});
});
This is similar to, but different from other questions around this topic.
I have a table with a list of records, each having a select checkbox.
In the table header I have a "Select All" checkbox.
When the user checks/unchecks "Select All" the records are selected/unselected. This works fine.
However, I need to deselect my "Select All" checkbox when one or more of the records are deselected.
My markup:
<table>
<thead>
<tr>
<th>Name</th>
<th><input type="checkbox" data-bind="checked: SelectAll" /></th>
</tr>
</thead>
<tbody data-bind="foreach: $data.People">
<tr>
<td data-bind="text: Name"></td>
<td class="center"><input type="checkbox" data-bind="checked: Selected" /></td>
</tr>
</tbody>
</table>
My script (edited):
function MasterViewModel() {
var self = this;
self.People = ko.observableArray();
self.SelectAll = ko.observable(false);
self.SelectAll.subscribe(function (newValue) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(newValue);
});
});
}
my.Person = function (name, selected) {
var self = this;
self.Name = name;
self.Selected = ko.observable(false);
}
This works
http://jsfiddle.net/AneL9/
self.SelectAll = ko.computed({
read: function() {
var item = ko.utils.arrayFirst(self.People(), function(item) {
return !item.Selected();
});
return item == null;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
});
but will give you a ordo n ^ 2 problem when selecting deselecting all, you can use a pasuable computed to get around that
http://www.knockmeout.net/2011/04/pausing-notifications-in-knockoutjs.html
edit: You can also extend the computed with a throttle, this way you avoid the ordo n^2 problem
.extend({ throttle: 1 })
http://jsfiddle.net/AneL9/44/
You should make SelectAll computed observable like this:
self.SelectAll = ko.computed({
read: function() {
var persons = self.People();
for (var i = 0, l = persons.length; i < l; i++)
if (!persons[i].Selected()) return false;
return true;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function(person){
person.Selected(value);
});
}
});
and strip SelectAll.subscribe out.
http://jsfiddle.net/Yqj59/