Knockout Sending ViewModel as parameter - asp.net-mvc

I am experimenting with Knockout with TypeScript and am trying to send a view model that contains another model as parameters to a function like:
TypeScript:
export interface IEmployee {...}
export interface ICompany {...}
export class ViewModel() {
constructor(company : ICompany) {}
public setAsOwner(parent : ViewModel, person : IEmployee) {
parent.company.updateOwner(person.id);
}
}
// In a different file
ko.applyBindings(new ViewModel(new ICompany()));
HTML:
<ul data-bind="foreach: employees">
<li>
#*Employee details...*#
<button data-bind="click: $root.setAsOwner.bind($parent, $data)">
Set as new owner
</button>
</li>
</ul>
However I keep getting an error saying that parent.company is undefined. What is the right way to pass the view model as a parameter to its own function?

The click binding handler function automatically receives the current employee item as the first parameter, and if you call $parent.setAsOwner from a wrapper function, the parent view model is available to the method via this.
So change the setAsOwner method to:
public setAsOwner(person : IEmployee) {
this.company.updateOwner(person.id);
}
and the HTML to:
<ul data-bind="foreach: employees">
<li>
#*Employee details...*#
<button data-bind="click: function(employee) { $parent.setAsOwner(employee); }">
Set as new owner
</button>
</li>
</ul>

Related

How to send this Razor data to a javascript function?

I have the following code:
foreach (var interest in Model.Interests)
{
<div>
<span>#interest.SubjectName</span>
#if (interest.Description.Length > 2)
{
<a data-bind="click(DATA)" href="#" title="View Details">details</a>
}
</div>
}
I'm using Razor because I need to allow users without javascript to have read-only access to the site, so using a knockout/JS loop here isn't an option.
In this line:
<a data-bind="click(DATA)" href="#" title="View Details">details</a>
I'd like to take the current interest (razor object) and send it into the click function. I just don't know the syntax.
How can this be achieved?
Depends on what representation of the object you want to pass to your function. If JSON will suffice, try this:
<a data-bind='#String.Format("click({0})", Newtonsoft.Json.JsonConvert.SerializeObject(interest))'
href="#" title="View Details">details</a>
Then in your click function you can get the object like this:
function click(data) {
var interest = JSON.parse(data);
}

add item to observable array in viewmodel from another partial view (MVC)

I am new to knockoutJS. I am working on an MVC application where I want to implement knockoutJS but the scenario is bit different.
I have a page where I am showing a list. I have 3 links on the page and on click of them I am adding partial views to page accordingly. What I want to do is that whenever I add values/data to partial views, the list which is on page should be updated with knockout. In other words I want to add value to observable array when I save data from partial view.
Please let me know if this is possible or I should keep it in jquery only.
Here is the code:
Main view:
<input type="button" value="Add Partial View" onclick="LoadPartial();" />
<div id="dvContent"></div>
<h4>People</h4>
<ul data-bind="foreach: people">
<li>
Name at position <span data-bind="text: $index"> </span>:
<span data-bind="text: name"> </span>
Remove
</li>
</ul>
<button data-bind="click: addPerson">Add</button>
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script>
function LoadPartial() {
$.ajax({
url: "/home/index",
dataType:"html",
type: "GET",
success: function (data) {
$("#dvContent").html(data);
}
});
}
</script>
<script>
function AppViewModel() {
var self = this;
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function () {
self.people.push({ name: "New at " + new Date() });
};
self.removePerson = function () {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
</script>
Partial View:
<table>
<tr>
<td>Add new Row</td>
<td><input type="button" value="Add" data-bind="click: addPerson"/></td>
</tr>
</table>
Thanks,
JsHunjan
It is easy to accomplish with Knockout. You need to show some code that you have tried though if you want to get some help. I will post a general answer but it isn't going to fix your use case exactly, just basically -
Create an object to hold your new item, you can do this either in the parent or the child view model, but if you do it in the child you need to pass it back to the parent.
Once you hit a save button or add or whatever in the child view model just do a .push() into the observableArray that you created ex... - myObservableArray.push(newItem());
Knockout will recognize all of the changes taking place and perform the actions you want automatically.
Hope this helps.

knockoutjs 'foreach' bind can not get a property which is defined as a class?

I'm new to use Knockoutjs
I have a viewModel defind as below:
// this is a subclass
var Order = {
selfdata: {
id : ko.observable(),
goodsName : ko.observable(''),
color : ko.observable(''),
size : ko.observable(''),
count: ko.observable(''),
orderDate: ko.observable(''),
remarks: ko.observable('')
},
}
// this is a viewModel
function ShoppingCar() {
var self = this;
self.orders = ko.observableArray([]);
self.show = function (msg) {
return msg.id;
}
//init
$.get('/Shopping/GetOrders', null, function (data) {
var items = $.map(data, function (item) { return Order.init(item) });// I write a init function to init order
self.orders(items);
});
}
//then I use Knockoutjs foreach binding as below:
<ul data-role="listview" data-theme="e" data-divider-theme="d">
<!-- ko foreach: orders -->
<li data-role="list-divider" data-bind="text: $data.selfdata.id">//!I get null value here</li>
<li><a href="#">
<h3 data-bind="text: $data.isValid"></h3>
<p><strong data-bind="text: $data.selfdata.color">//! here has null value too!</strong></p>
<p class="ui-li-aside"><strong>12:14</strong>PM</p>
</a></li>
<!-- /ko -->
So, my question is, how can I display order.selfdata in a ko's foreach?
Have you tried just putting:
selfdata.id
Also, have you debugged and made sure that you are actually populating the Orders object correctly?
You need to close your strong tag. Its getting wiped out by the comments
<p><strong data-bind="text: $data.selfdata.color">//! here has null value too!</strong></p>
should be
<p><strong data-bind="text: $data.selfdata.color"></strong></p>
You also don't need the $data. selfdata.color should be working fine, assuming the data is actually being passed into the observable correctly.
If fixing the comments and removing $data don't work I would use a browser debugger to check and see what data is actually in the observable object.

Multiple ViewModels with Knockout and ASP.NET MVC4 SPA

I'm new to ASP.NET MVC SPA and Knockout.js os maybe it's a simple mistake I made...
Situation: I have two partialviews in my website and I want that every partialview has his own Knockout ViewModel so I won't get a huge ViewModel.
My current ViewModel:
/// <reference path="../_references.js" />
function MobileDeliveriesViewModel() {
var self = this;
// Data
self.currentDelivery = ko.observable();
self.nav = new NavHistory({
params: { view: 'deliveries', deliveryId: null }
});
// Test
self.foo = "FooBar"
self.bar = "BarFoo"
self.nav.initialize({ linkToUrl: true });
// Navigate Operations
self.showDeliveries = function () { self.nav.navigate({ view: 'deliveries' }) }
self.showCustomers = function () { self.nav.navigate({ view: 'customers' }) }
}
function BarFooViewModel() {
var self = this
//MobileDeliveriesViewModel.call(self)
self.bar2 = "BarFooTwo"
}
ko.applyBindings(new MobileDeliveriesViewModel());
ko.applyBindings(new MobileDeliveriesViewModel(), $('#BarFoo')[0]);
ko.applyBindings(new BarFooViewModel(), document.getElementById('BarFoo'));
My Index.cshtml:
<div data-bind="if: nav.params().view == 'deliveries'">
#Html.Partial("_DeliveriesList")
</div>
<div class="BarFoo" data-bind="if: nav.params().view == 'customers'">
#Html.Partial("_CustomersList")
</div>
<script src="~/Scripts/App/DeliveriesViewModel.js" type="text/javascript"></script>
My CustomerPartialView:
<div id="BarFoo" class="content">
<p data-bind="text: bar"></p>
<p data-bind="text: bar2"></p>
<button data-bind="click: showDeliveries, css: { active: nav.params().view == 'deliveries' }">Deliveries</button>
</div>
My DeliveriesPartialView:
<div class="content">
<p data-bind="text: foo"></p>
<button data-bind="click: showCustomers, css: { active: nav.params().view == 'customers' }">Customers</button>
</div>
If I run this, it won't recognize the bar2 propertie in my BarFooViewModel...
I have tried 2 different applyBindings at the end of my ViewModel.
Anybody got an idea or is their a better way/pattern to do this?
are there JS errors on page?
nav.params().view
but params: { view: 'deliveries', deliveryId: null } - it's not function.
and if you want use a few view models on single page - check this http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+KnockMeOut+%28Knock+Me+Out%29 acticle. you have to use "stopBinding"
It looks like you are applying multiple data bindings to the same sections.
ko.applyBindings(new MobileDeliveriesViewModel();
This will bind to all elements one the page.
ko.applyBindings(new MobileDeliveriesViewModel(), $('#BarFoo')[0]);
this will try to bind to all elements inside the div
ko.applyBindings(new BarFooViewModel(), document.getElementById('BarFoo'));
This will also try to bind to all elements inside the div.
To keep things simple, you should try to bind a single view model to a single html section. I've found that trying to bind two view models in the same html section has been hard to get work correctly and trouble shoot.
Jack128's answer also makes some good points.

Mapping: foreach binding work only the first time

I have the following snippet JQuery inside an HTML file:
$.getJSON("/events/", function (data) {
viewModel = ko.mapping.fromJS(data);
ko.applyBindings(viewModel);
});
The code is executed when, for example, the user presses a button and returns JSON like:
{"Events":[{"Name":"Event1"},{"Name":"Event2"},{"Name":"Event3"}]}
This result is linked (using KnockoutJS) to:
<ul data-bind="foreach: Events">
<li><span data-bind="text: Name"></span></li>
</ul>
Everything works fine with the first call to $.GetJSON. I get what I want, which is (browser output):
Event1
Event2
Event3
But in subsequent calls to "$. GetJSON" I get the following error in Firebug:
NotFoundError: Node was not found.
containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
And I get no list item.
What I can be doing wrong?
Thank you very much in advance.
The whole point of Knockout is to handle the interaction between your view and view model. In this case, you're trying to update a list of events in response to a button click. The button click should be linked to a function in your view model, while the list should be linked to an observable array that's updated when you click the button. I've put this together in a fiddle here: http://jsfiddle.net/mbest/uZb3e/ and the important parts are below:
<button type=button data-bind="click: updateEvents">Update</button>
<ul data-bind="foreach: data.Events">
<li><span data-bind="text: Name"></span></li>
</ul>
Javascript:
var viewModel = {
data: ko.mapping.fromJS({"Events":[]}),
updateEvents: function() {
var self = this;
$.getJSON("/events/", function (data) {
ko.mapping.fromJS(newData, self.data);
});​
}
};
ko.applyBindings(viewModel);​
My friend Thomas Brattli found the solution:
<ul data-bind="template: { name: 'template', foreach: Events }"></ul>
<script id="template" type="text/html">
<li><span data-bind="text: Name"></span></li>
</script>
Thanks !

Resources