Please find below code from .cshtml page
Below code gives databind for observables but not for observable array.
var getLocationDetails = function (locationCode)
{
return $.getJSON('#Url.Action("MasterDetails")',
{ locationcode: locationCode });
};
var setModelData = function (self, locationDetails)
{
self.UneditedLocationDetails = _(locationDetails).cloneDeep();
for (var key in locationDetails)
{
locationDetails[key] = ko.observable(locationDetails[key]);
}
function LocationModifyViewModel()
{
var self = this;
initAjax(self);
var initModel = setModelData.bind(self);
var initModel = setModelData.bind(self);
self.TextboxLocationcode.subscribe(function (val)
{
getLocationDetails(val).success(function (locationDetails) {
setModelData(self, locationDetails);
});
In the above code databinded to locationDetails, where I want to add observablearray values to locationDetails or else if possible to new variable.
Related
I have used jquery dataTable in knockout.js.In that i am generating data rows from function with remove link.It will remove row form table as well form observable array.The remove link works once and remove the row from table but when i try to remove another one they do not remove it.
Here you can check http://jsfiddle.net/zongweil/PLUKv/1/
$(document).ready(function () {
/* Custom binding */
ko.bindingHandlers.dataTable = {
init: function (element, valueAccessor) {
var binding = ko.utils.unwrapObservable(valueAccessor());
// If the binding is an object with an options field,
// initialise the dataTable with those options.
if (binding.options) {
$(element).dataTable(binding.options);
}
},
update: function (element, valueAccessor) {
var binding = ko.utils.unwrapObservable(valueAccessor());
// If the binding isn't an object, turn it into one.
if (!binding.data) {
binding = {
data: valueAccessor()
};
}
// Clear table
$(element).dataTable().fnClearTable();
// Rebuild table from data source specified in binding
$(element).dataTable().fnAddData(binding.data());
}
};
/* Object code */
function GroupMember(id, name, isGroupLeader) {
var self = this;
self.id = id;
self.name = name;
self.isGroupLeader = ko.observable(isGroupLeader);
self.link = ko.computed(function () {
return "/#user/" + self.id;
});
self.nameWithLink = ko.computed(function () {
return '' + self.name + '';
});
self.actions = ko.computed(function () {
return '<a class="btn btn-danger" data-bind="click: function() {removeMember(' + self.id + ')}">' + '<i class="icon-minus-sign"></i>' + '</a>';
});
}
/* View model */
var groupViewModel = {
groupMembers: ko.observableArray([
new GroupMember("1", "Abe", true),
new GroupMember("2", "Bob", false),
new GroupMember("3", "Bill", false)])
};
groupViewModel.membersTable = ko.computed(function () {
var self = this;
var final_array = new Array();
for (var i = 0; i < self.groupMembers().length; i++) {
var row_array = new Array();
row_array[0] = self.groupMembers()[i].nameWithLink();
row_array[1] = self.groupMembers()[i].actions();
final_array.push(row_array);
}
return final_array;
}, groupViewModel);
groupViewModel.removeMember = function (id) {
var self = this;
self.groupMembers.remove(function (groupMember) {
return groupMember.id == id;
});
};
ko.applyBindings(groupViewModel);
});
When you call fnClearTable in your custom binding's update function, you clear a part of your DOM behind knockout's back.
You then add new DOM elements by calling fnAddData.
Your buttons work using the click binding. For the click binding to work, knockout has to applyBindings.
If you want to keep using both the dataTable and a click binding to work with the DOM, you'll have to manually apply bindings every time you make a change. In your init method, let knockout know you're taking care of descendant bindings:
return { controlsDescendantBindings: true };
In your update method, apply bindings by hand:
ko.applyBindingsToDescendants(viewModel, element);
This makes sure your click bindings will work again.
Here's your example with this code added: http://jsfiddle.net/5t15rhyq/
I have a working knockout js script, myValues is an array result of a ko.computed, then I pass it down to myValues2 which calls a myFunction to make each object in that array as "observable". This works but I wanted to call myFunction in self.myValues so that I wouldn't have to create another observableArray myValues2. Can you help me combine the script of myValues and myValues2 so that I can finally delete self.myValues2. Here is my code:
var myFunction = function (id, name, amount, automatic) {
var self = this;
self.Id = ko.observable(id);
self.Name = ko.observable(name);
self.Amount = ko.observable(amount);
self.Automatic = ko.observable(automatic);
};
self.myValues = ko.computed(function () {
return ko.utils.arrayFilter(self.completeList(), function (ded) {
return ded.automatic() == true;
});
});
self.myValues2 = ko.observableArray();
self.myValues2(ko.utils.arrayMap(self.myValues(), function (dd) {
return new myFunction(dd.id(), dd.name(), dd.amount(), dd.automatic());
}));
self.completeList()
how defined this array?
Because you began to filter this list by automatic field and then to fill other list. Strange order.
return ded.automatic() == true;
I have the following code:
var ObjectViewModel = function (testObject) {
//debugger;
var self = this;
self.id = testSet.id;
self.details = testOject.details;
self.children = ko.observableArray(testObject.children);
self.childCount = ko.computed(function() {
return self.children().length;
});
self.addObject = function () {
//debugger;
// Pending UI
// Call API here
// On success, complete
self.children.push(dummyObject);
self.childToAdd("");
}.bind(self);
}
/ etc
However in childCount, this.children() is undefined. I'm trying to let the view show the length of the children array in real-time, so as the user adds/removes items, the count is updated. Any idea why this isn't working?
You can pass what the value of this should be when the function is executed to the computed function with the last parameter:
this.childCount = ko.computed(function() {
return this.children().length;
}, this);
You could also store a reference to this outside of the computed:
var self = this;
this.childCount = ko.computed(function () {
return self.children().length;
});
I am building a sidebar to filter a main view, like for instance the one at John Lewis. I have the code working but it ain't pretty.
I know there are several SO questions on similar lines but I can't quite fathom my own use case.
I need to get the names of the checkboxes from the server ( eg via JSON ) to dynamically create observableArrays on my ShopView.
Here's how it is:
var data = {
'gender' : [ ],
'color' : [ ]
};
var filterMapping = {
create: function( obj ) {
return ko.observableArray( obj.data );
}
}
var ShopView = new function() {
var self = this;
ko.mapping.fromJS( { filters: data }, filterMapping, self );
// this is the bit I don't like
this.filterChange = ko.computed(function () {
for( var key in self.filters ) {
var obj = self.filters[key];
if( ko.isObservable(obj)){
obj();
}
}
});
this.filterChange.subscribe( function( ) {
//make AJAX request for products using filter state
});
}
My HTML looks as you'd expect:
Gender
<ul>
<li><input type="checkbox" value="male" data-bind="checked: filters.gender" />Male</li>
<li><input type="checkbox" value="female" data-bind="checked: filters.gender" />Female</li>
</ul>
As I say, it works, but it's not nice. In an ideal world I could subscribe to this.filters, eg
this.filters.subscribe( function() {
//make AJAX request for products using filter state
});
NB I'm not trying to do the filtering on the client side - just update my viewmodel when the dynamically-bound checkboxes change.
Any ideas? thanks!
First, the mapping plugin should be treated as an aid to code duplication. I don't think its a good idea to think of the mapping plugin as a solution in and of itself; at least not directly. It also obscures what is happening when you post your code on SO, since we can't see the models you are working with. Just a thought.
Now, ff you want to get dynamic filters from the server, and use them to filter a list of items (like you would in a store), I would do it something like this (here is the fiddle):
var FilterOption = function(name) {
this.name = name;
this.value = ko.observable(false);
};
var Filter = function(data) {
var self = this;
self.name = data.name;
options = ko.utils.arrayMap(data.options, function(o) {
return new FilterOption(o);
});
self.options = ko.observableArray(options);
self.filteredOptions = ko.computed(function() {
var options = []
ko.utils.arrayForEach(self.options(), function(o) {
if (o.value()) options.push(o.name);
});
//If no options, false represents no filtering for this type
return options.length ? options : false;
});
};
var ViewModel = function(data) {
var self = this;
self.items = ko.observableArray(data.items);
filters = ko.utils.arrayMap(data.filters, function(i) {
return new Filter(i);
});
self.filters = ko.observableArray(filters);
self.filteredItems = ko.computed(function() {
//Get the filters that are actually active
var filters = ko.utils.arrayFilter(self.filters(), function(o) {
return o.filteredOptions();
});
//Remove items that don't pass all active filter
return ko.utils.arrayFilter(self.items(), function(item) {
var result = true;
ko.utils.arrayForEach(filters, function(filter) {
var val = item[filter.name.toLowerCase()];
result = filter.filteredOptions().indexOf(val) > -1;
});
return result;
});
});
};
The next obvious step would be to add support for items that had multiple properties, but or options properties, but this should give you the basic idea. You have a list of filters, each with any number of options (which stack additively), and you use a computed items array to store the result of filtering the items.
Edit: To get the items using an ajax subscription, you would replace the FilteredItems prop with a computed that gets the selected filters, and then subscribe to it, like this:
var ViewModel = function(data) {
var self = this;
self.items = ko.observableArray(data.items);
filters = ko.utils.arrayMap(data.filters, function(i) {
return new Filter(i);
});
self.filters = ko.observableArray(filters);
self.selectedFilters = ko.computed(function() {
ko.utils.arrayFilter(self.filters(), function(o) {
return o.filteredOptions();
});
});
self.selectedFilters.subscribe(function() {
//Ajax request that updates self.items()
});
};
The code is as follows: EmployeeModel is the viewModel and the problem is that when I change an item's property - deletedFlag in employees (obs array), deletedItems is not updated.
How can i fix this?
function Employee(data) {
this.employeid = ko.observable(data.employeid);
this.name = ko.observable(data.name);
this.isactive = ko.observable(data.isactive);
this.deletedFlag = ko.observable(false);
}
var EmployeeModel = function () {
var self = this;
self.employees = ko.observableArray([]);
self.deletedItems = ko.computed(function () {
return ko.utils.arrayFilter(self.employees(), function (item) {
return item.deletedFlag == true;
});
}, this);
}
EDIT: and the following code marks one item from the array for deletion
self.removeEmployee = function (employee) {
employee.deletedFlag(true);
};
The property deletedFlag is an observable, therefore you need to retrieve its current value by invoking it as a function (you cannot compare it directly to any value):
self.deletedItems = ko.computed(function () {
return ko.utils.arrayFilter(self.employees(), function (item) {
return item.deletedFlag() == true; // <===
});
}, this);