How to Bind data from Razor to Knockout? - javascript

I am using KnockOut JS and MVC.
How do I bind the data from # RAZOR view to my databind in KO.
I tried doing this but did not return anything at the CONTROLLER.
What I am trying to achieve here is that the Razorview renders the rows in Step3(View).I want to bind these rows to the KNOCKOUT so that I can pass this data to the Step4(View).
Is there a better way??
VIEW:
<tbody data-bind="foreach:ApprTable">
#for (int i = 0; i < UserWRInfo.Count; i++)
{
<tr>
<td style="text-align: center">
<button type="button" class="btn btn-primary" data-bind="click: $root.submit.bind($data,'#i')">Start</button>
</td>
<td style="text-align: center" data-bind="value: Appropriation">#UserWRInfo[i].AppropriationNumber</td>
<td style="text-align: center" data-bind="value: PriorityDate">#UserWRInfo[i].PriorityDate.ToString("MM/dd/yyyy")</td>
<td style="text-align: center" data-bind="value: Location">#UserWRInfo[i].Sect #UserWRInfo[i].Township #UserWRInfo[i].Range#UserWRInfo[i].RangeDirectionID</td>
<td style="text-align: center" data-bind="value: Source">#UserWRInfo[i].Source</td>
#if (UserWRInfo.Count == UserOwner.Count)
{
<td style="text-align: center" data-bind="value: Owner">#UserOwner[i].ToString()</td>
}
else
{
<td style="text-align: center" data-bind="value: Owner"></td>
}
<td style="text-align: center" data-bind="value: Use">#UserWRInfo[i].UseDescription</td>
<td style="text-align: center" data-bind="value: StartedBy"></td>
<td style="text-align: center" data-bind="value: RequiredReporting">#UserWRInfo[i].isAnnualReportRequired</td>
</tr>
}
</tbody>
JS:
function RowData(Appropriation, PriorityDate, Location, Source, Owner, Use, StartedBy, RequiredReporting) {
var self = this;
self.Appropriation = ko.observable(Appropriation);
self.PriorityDate = ko.observable(PriorityDate);
self.Location = ko.observable(Location);
self.Source = ko.observable(Source);
self.Owner = ko.observable(Owner);
self.Use = ko.observable(Use);
self.StartedBy = ko.observable(StartedBy);
self.RequiredReporting = ko.observable(RequiredReporting);
}
function Step3ViewModel() {
var self = this;
self.ApprTable = ko.observableArray();
self.ApprTable.push(RowData());
self.submit = function (buttonid) {
var ApprData = ko.toJSON(self.ApprTable());
$.post("/Step/Step4", { "AD": ApprData }, function (data) {
}, 'json');
}
}
ko.applyBindings(new Step3ViewModel());

You're creating a single element observable array by calling the RowData constructor, but you're not passing any parameters in.
self.ApprTable = ko.observableArray([new RowData()]);
The function definition requires a lot of parameters
function RowData(Appropriation, PriorityDate, Location, Source, Owner, Use, StartedBy, RequiredReporting)
You're not actually putting any data into the observables.

You are mixing the stuff. If you want to init the view from razor data, you can do it in two ways:
First, generate JSON in the controller and load it to knockout view model using "knockout-mapping" plugin, for example your view may contain:
<script>
$(function () {
ko.mapping.fromJS(#Html.Raw(ViewBag.jmodel), null, viewModel);
});
</script>
of course you should prepare ViewBag.jmodel in your controller, like this:
JsonSerializer js = JsonSerializer.Create(new JsonSerializerSettings());
var jw = new StringWriter();
js.Serialize(jw, <your object here>);
ViewBag.jmodel = jw.ToString();
Other way is to generate code that will add data row by row like you attempted to do:
var viewModel=new Step3ViewModel();
ko.applyBindings(viewModel);
#for (int i = 0; i < UserWRInfo.Count; i++)
{
viewModel.ApprTable.push(new RowData(#UserWRInfo[i].AppropriationNumber, /*other values here to create full row of data row*/)
}
This way will generate as many viewModel.ApprTable.push(...) lines as you have in your data set.
And of course, your table with data and knockout binding should NOT contain any razor code!!!
<tbody data-bind="foreach:ApprTable">
...
<td style="text-align: center" data-bind="value: Appropriation"></td>
...
</tbody>

Related

knockout js bindng issue when using template binding

lets say i have a following code
var Names = ['test1','test2','test3'];
function ViewModel() {
var self = this;
self.RegistraionInfo = ko.observableArray(Names);
self.ChangeSelection = function (data,event) {
fnChangeSelection(data);
}
self.tableRows = ko.observableArray([]);
self.addNewRow = function () {
self.tableRows.push(new tableRow('', self));
}
self.addNewRow();
}
function tableRow(number, ownerViewModel) {
var self = this;
self.number = ko.observable(number);
self.remove = function () {
ownerViewModel.tableRows.destroy(this);
}
ko.applyBindings(new ViewModel());
and in html
<table >
<thead>
<tr class="active">
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="template:{name:'data-tableRow', foreach: tableRows}"></tbody>
<tfoot>
<tr>
<td >
<img id="btnAddRowProcedure1" src=#Url.Content("~/Content/img/plus.png") data-bind="click: addNewRow" />
</td>
</tr>
</tfoot>
</table>
<script id="data-tableRow" type="text/html">
<tr>
<td >
<img id="btnDelete" src=#Url.Content("~/Content/img/close.png") data-bind="click: function(){ $data.remove(); }" />
</td>
<td><select data-bind="options:$root.RegistraionInfo, , event:{ change:$root.ChangeSelection}"></select></td>
</tr>
</script>
what im trying to do here is when user clicks the #btnAddRowProcedure1 link a new tablerow will be added. and inside the table row there is a dropdown that is bound to the Names array.when the user changes the dropdown selection the ChangeSelection function is called.
The Problem is that i want knock out to send currently selected Names array element as Data but knockout sending the tableRow as data.Is there a way to overcome this problem.Im still not sure why this works like this.
UPDATE
After the #Roy J changes i was able to get this to work.but now i have stumbled upon another problem (sorry im a noob at knockout).I want to add a button when ever the user changes the selection so i have added the following code to the ViewModel
self.displayAddBtn = ko.computed(function () {
if (self.tableRows.length == 0)
{
return false;
}
return self.tableRows[self.tableRows.length-1].selected() !="";
}, self);
this code is only executed once.when the user changes the selection this is code is not executed.can some one tell me how i can make this work
You need to have a value binding on your select. That's where the new value will show up when your change function is called.
var Names = ['test1', 'test2', 'test3'];
function ViewModel() {
var self = this;
self.RegistraionInfo = ko.observableArray(Names);
self.ChangeSelection = function(data, event) {
console.debug("Changed to:", data.selected());
}
self.tableRows = ko.observableArray([]);
self.addNewRow = function() {
self.tableRows.push(new tableRow('', self));
}
self.addNewRow();
}
function tableRow(number, ownerViewModel) {
var self = this;
self.number = ko.observable(number);
self.remove = function() {
ownerViewModel.tableRows.destroy(this);
};
self.selected = ko.observable(); // new! bound to select
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table>
<thead>
<tr class="active">
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="template:{name:'data-tableRow', foreach: tableRows}"></tbody>
<tfoot>
<tr>
<td>
<img id="btnAddRowProcedure1" src=#Url.Content( "~/Content/img/plus.png") data-bind="click: addNewRow" />
</td>
</tr>
</tfoot>
</table>
<template id="data-tableRow">
<tr>
<td>
<img id="btnDelete" src=#Url.Content( "~/Content/img/close.png") data-bind="click: function(){ $data.remove(); }" />
</td>
<td>
<select data-bind="options:$root.RegistraionInfo, value:selected, event:{ change:$root.ChangeSelection}"></select>
</td>
</tr>
</template>

Automatically update field in current row of an observablearray from textInput field

I have a table bound to a view model. When I select the table row, a field (notes) is updated from this:
<tbody data-bind="foreach: namespace.PersonResults.model">
<tr data-bind="click: $root.selectItem, css: {selected: $root.isSelected($data)}">
<td data-bind="text: Forename"></td>
<td data-bind="text: Surname"></td>
<td data-bind="text: PostCode"></td>
<td data-bind="text: Notes" style="display: none"></td>
</tr>
</tbody>
The field in the same div as the table (this is a single text area that should be updated when selecting the row on the table above, and update the table by the time the user chooses another row).
<textarea data-bind="textInput: editNotes"></textarea>
the viewModel is currently doing this:
var resultsViewModel = function() {
var self = this;
self.model = ko.observableArray();
self.editNotes = ko.observable();
self.selectItem = function(record) {
self.selectedItem(record);
self.editNotes(record.Notes);
}
self.getData () {
// some ajax stuff to populate the table
}
}
This works fine for displaying the notes in the textarea, but how do I get this to go the other way, and populate the field in the observableArray if the user has altered the contents of the textarea?
You can just bind a td to the same property as the textarea. E.g.:
var resultsViewModel = function() {
var self = this;
self.editNotes = ko.observable('initial value');
}
var vm = {
selectedResult: ko.observable(null),
results: [new resultsViewModel(), new resultsViewModel()]
};
vm.selectResult = function(result) { vm.selectedResult(result); };
ko.applyBindings(vm);
.selected { background-color: pink; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table><tbody data-bind="foreach: results">
<tr data-bind="css: { selected: $root.selectedResult() === $data }">
<td data-bind="text: editNotes"></td>
<td><button data-bind="click: $root.selectResult">Select</button></td>
</tr>
</tbody></table>
<!-- ko with: selectedResult -->
<textarea data-bind="textInput: editNotes"></textarea>
<!-- /ko -->
You need to bind to the value of a textarea:
<td>
<textArea data-bind="value: $data.Notes"></textArea>
</td>
I've knocked up a quick and simplified demo in this fiddle
Edited to add:
Here's an updated fiddle that's more in line with what you want: fiddle

Trigger controller action when selecting item in table with KODataTable MVC

I'm presenting data in a table by calling an Action(that returns a list in Json) through an AJAX call.
Output:
http://imageshack.com/a/img673/3484/zxZIqy.png
What i would like to do is to make each user (rows in table) linkable to an edit page (Admin/Edit/Id). Either by simply clicking on them, or by having an Edit-link at the end of each row.
I don't know how to achieve this. It would be easy with ordinary razor syntax. But this template seams to be nice to work with to achieve this sort of dynamic datatable.
I'm working with a template called KODataTable to make this table with both searching and sorting ability.
View..
<div id="kodt">
<div>
<input type="text" data-bind="value: searchText, valueUpdate: 'afterkeydown'" />
<select data-bind="value: selectedColumn, options: columns"></select>
<button data-bind="click: search">Search</button>
</div>
<div>
<table class="table table-striped table-hover">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: $data, click: function() { $parent.sort($index()) }" style="cursor: pointer"></th>
</tr>
</thead>
<tbody data-bind="foreach: currentRows">
<tr data-bind="foreach: $parent.columns, click: function () { $root.selectRow($data); }, css: { 'success': $root.selectedRow() == $data }">
<td data-bind="text: $parent[$data]" style="cursor: pointer; text-align: center"></td>
</tr>
</tbody>
</table>
</div>
<div>
<button data-bind="click: firstPage">First</button>
<button data-bind="click: prevPage">Prev</button>
Page <span data-bind="text: currentPage() + 1"></span> of <span data-bind="text: pageCount"></span>
<button data-bind="click: nextPage">Next</button>
<button data-bind="click: lastPage">Last</button>
</div>
</div>
Script..
<script>
var users = new Object();
$.getJSON("/Admin/GetUsers", function (data) {
users = data;
var TableDataVM = new KODataTable({
columns: ["Id", "Username", "RoleId", "CompanyId", ""],
rows: users,
});
ko.applyBindings(TableDataVM, document.getElementById("kodt"));
});
</script>
Right now, it looks like when a user clicks a row, $root.selectRow($data); is invoked, which passes the row object's data over to some function in the ViewModel called selectRow(). If this function exists, you could use it to $.post the row (representing the user object) to the MVC controller in this function, and use the response to redirect to the Edit view.
var vm = function() {
var self = this;
var selectRow = function(rowClicked) {
var data = {
Id = rowClicked.Id,
Username = rowCicked.Username,
// etc.
};
// post the data to some MVC controller - something remotely like this
$.ajax({
url: '/Admin/Edit/' + rowClicked.Id,
data: ko.toJSON(data),
type: 'POST',
success: function (result) {
window.location.href = result.url;
}
});
};
};

How can I append text with table tr using knockout

I have got a assignment to implement knockout js to my application. I have a table like
<table>
<tr>
<th>
Name
</th>
<th>
Category
</th>
<th>
Price
</th>
<th></th>
</tr>
<tr>
<td>
Iphone
</td>
<td>
SmartPhone
</td>
<td>
50000
</td>
</tr>
</table>
There are three textbox for creation of this field.
<div id="create">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Prize" id="prize"/>
</div>
When I am typing on this textboxes i want to show this on the table as a new tr.. How can I do this? DEMO
Reference Link
What you want to do is define a viewmodel that contains the data for an individual item, and another viewmodel that contains the rest of the interactions (list of items, how to add new ones, etc).
var Item = function (Name, Category, Price) {
var self = this;
self.Name = ko.observable(Name);
self.Category = ko.observable(Category);
self.Price = ko.observable(Price);
}
var ViewModel = function () {
var self = this;
self.ItemToAdd = ko.observable(new Item());
self.Items = ko.observableArray([]);
self.addItem = function (item) {
self.Items.push(item);
self.ItemToAdd(new Item());
}
};
var vm = new ViewModel();
vm.addItem(new Item('Iphone', 'SmartPhone', 50000));
ko.applyBindings(vm);
In your html, your table body will look like this:
<tbody data-bind="foreach: Items">
<tr>
<td data-bind="text: Name"/>
<td data-bind="text: Category"/>
<td data-bind="text: Price"/>
</tr>
</tbody>
what this does is loops through each item in the Itemlist and creates a <tr> for each one and binds the values of the Item object in the observableArray to the <td> elements.
to add new items to the table in your markup:
<div data-bind="with: ItemToAdd">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Price" id="price"/>
<button data-bind="click: $parent.addItem">Add</button>
</div>
this sets the context of the div element to a new Item object, and when you click the Add button, it calls the parent context's (ViewModel) addItem function, and automatically passes the context item for the div element (ItemToAdd). Then its just a matter of pushing it on to the observableArray and the table will update with the new item.
Updated Fiddle: http://jsfiddle.net/BJQgw/4/
if this was a for-real application, you would perform some sort of validation prior to adding the item to the list (preferably using knockout-validation)

how to bind elements from multiple javascript files in html using knockoutjs

even though this is primary question, i cant solve this even with several attempts. (but im new to this area)
what i need is i have 2 separate java script files and i want to bind from them.
<table style="width: 100%;">
<tr>
<td class="auto-style1">Time For Work: </td>
<td><input id="Text2" type="text" data-bind="value: TimeForWork" /></td>
</tr>
<tr>
<td class="auto-style1">Rest </td>
<td><input id="Text4" type="text" data-bind="value: Rest" /></td>
</tr>
<td class="auto-style1">Project Code </td>
<td ><select id="Select1" data-bind='options: Projects' style="width: 312px"></select>
<button data-bind="click: AddProjects">Cancel</button>
</td>
<td><input id="Text6" type="text" data-bind="value:Test" />
</table>
<script src="Scripts/TimeRecord.js"></script>
<script src="Scripts/ProjectDetail.js"></script>
my java scripts : TimeRecord.js
var ViewModel = {
CheckIn: ko.observable(),
CheckOut: ko.observable(),
Lunch: ko.observable(),
Rest: ko.observable(),
WorkOnProject: ko.observable(),
//Projects: ko.observableArray()
};
ViewModel.TimeForWork = ko.dependentObservable(function () {
return ViewModel.CheckIn() ? ViewModel.CheckOut() ? parseFloat(this.Lunch()) ? parseFloat(this.CheckOut()) - parseFloat(this.CheckIn()) - parseFloat(this.Lunch()) : parseFloat(this.CheckOut()) - parseFloat(this.CheckIn()) : 0 : 0;
}, ViewModel);
ViewModel.RemainHour = ko.dependentObservable(function () {
return ViewModel.TimeForWork() ? ViewModel.Rest() ? ViewModel.WorkOnProject() ? parseFloat(this.TimeForWork()) - parseFloat(this.Rest()) - parseFloat(this.WorkOnProject()) : parseFloat(this.TimeForWork()) - parseFloat(this.Rest()) : parseFloat(this.TimeForWork()) : 0
}, ViewModel);
ProjectDetail.js
var projectLine = function () {
var self = this;
//self.RemainHour = ko.observable();
self.Test = "abc";
self.Projects = ko.observableArray();
self.AddProjects = function () {
alert('abc');
}
}
ko.applyBindings(new projectLine());
in here TimeRecord.js values are binding as i expected. but ProjectDetail values are not binding. even self.Test value does not display. what dd i do wrong?
ko.applyBindings can only be called once per section. If you don't pass a second parameter, the section is the entire page.
Do this something like that.
<div id="one">
<input data-bind="value: name" />
</div>
<div id="two">
<input data-bind="value: name" />
</div>
<script type="text/javascript">
var viewModelA = {
name: ko.observable("Anurag")
};
var viewModelB = {
name: ko.observable("Chaurasia")
};
ko.applyBindings(viewModelA, document.getElementById("one"));
ko.applyBindings(viewModelB, document.getElementById("two"));
</script>
That might help you.
In this situation can create another View model and using that view model can access other view model functions.
var ModelHome={
TimeRecord:ViewModel ,
Project :projectLine ,
};
ko.applyBindings(ModelHome);

Categories

Resources