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;
}
});
};
};
Related
I've tried several ways of doing this with no success. Would love some advice!
Goal: I have a table where each row is an order, but where within that row, if changes need to be made, a div appears underneath (in red). This needs to show/hide when a button on that row is clicked/toggled (Button is: Make Changes)
Issue: I have all the buttons working apart from the make changes toggle. Tried the visible observable, but the closest I could get was toggling the div's visibility for the whole table, not per row.
//Class to represent a row in the table
function orderDetail(order, orderChange) {
var self = this;
self.order = ko.observable(order);
self.orderChange = ko.observable(orderChange);
}
//Overall viewmodel, plus initial state
function FoodViewModel() {
var self = this;
self.foodTypes = [
{ foodType: "Please Select"},
{ foodType: "Veg"},
{ foodType: "Meat"}
];
self.orders = ko.observableArray([
new orderDetail(self.foodTypes[0], self.foodTypes[0])
]);
// Add and remove rows
self.addOrder = function() {
self.orders.push(new orderDetail(self.foodTypes[0], self.foodTypes[0]));
}
self.removeOrder = function(order) { self.orders.remove(order) }
}
ko.applyBindings(new FoodViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Orders</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div><select data-bind="options: $root.foodTypes, value: order, optionsText: 'foodType'" id="foodList"></select></div>
<div><select data-bind="options: $root.foodTypes, optionsText: 'foodType', value: orderChange" id="foodListChange" style="color: red;"></select></div>
</td>
<td>
<button class="button button2" >Make Changes</button>
</td>
<td>
<button class="button button1" href="#" data-bind="click: $root.removeOrder">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
Thanks in advance!
If you want that the user interface reacts to something in Knockout, make an observable.
In this case you want to display part of the UI conditionally (apparently to toggle an edit mode), so let's create:
an observable editMode that is either true or false, to store the UI state
a function toggleEditMode that toggles between the two states, to bind it to the button
an if: editMode and an ifnot: editMode binding, to show different parts of the UI accordingly
function OrderDetail(params) {
var self = this;
params = params || {};
self.order = ko.observable(params.order);
self.orderChange = ko.observable(params.orderChange);
self.editMode = ko.observable(true);
self.buttonCaption = ko.pureComputed(function () {
return self.editMode() ? "Done" : "Edit";
});
self.toggleEditMode = function () {
self.editMode(!self.editMode());
}
}
function OrderList(params) {
var self = this;
params = params || {};
self.foodTypes = ko.observableArray(params.foodTypes);
self.orders = ko.observableArray();
self.addOrder = function(foodType) {
self.orders.push(new OrderDetail());
}
self.removeOrder = function(order) {
self.orders.remove(order);
}
}
var vm = new OrderList({
foodTypes: [
{foodType: "Veg"},
{foodType: "Meat"}
]
});
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th style="width: 150px;">Orders</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div data-bind="ifnot: editMode">
<!-- ko with: order -->
<span data-bind="text: foodType"></span>
<!-- /ko -->
</div>
<div data-bind="if: editMode">
<select data-bind="
options: $root.foodTypes,
value: order,
optionsText: 'foodType',
optionsCaption: 'Please select…'
"></select>
</div>
</td>
<td>
<button class="button button2" data-bind="
click: toggleEditMode,
text: buttonCaption,
enable: order
"></button>
<button class="button button1" href="#" data-bind="
click: $root.removeOrder
">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
Notes
Don't make "Please Select" part of your food types. That's what the optionsCaption binding is for.
I've parameterized the viewmodels (see the params object). This will work better than hard-coding values or using long argument lists, especially if you want to use a mapping plugin later.
The "Done" button is disabled as long as no order is selected, via the enable: order binding, i.e. if the order property is empty, the enable binding will keep the button disabled.
The with: order binding serves a similar purpose. It will only display its contents when there actually is an order value to display. This will prevent rendering errors with incomplete OrderDetail instances.
I am developing my first app using Vuejs + Laravel and I am facing a problem that I couldn't solve until now!
I have an array of object and I need to edit a single of then without delete and add a new one! I have made a JS Bin to show what I need!
JS Bin
When you click in EDIT and start to typing your new value the original value edits as well but I need to change the original value only after the user hit the save button!
Anybody can help me?
PS: I will update my database and then show the new value on the table!
Is there anyway to duplicate my record as I do on the edit function without sync then?
JS
new Vue({
el: 'body',
data: {
cache: {},
record: {},
list: [
{ name: 'Google', id: 1 },
{ name: 'Facebook', id: 2 },
],
},
methods: {
doEdit: function (record) {
this.cache = record;
},
edit: function (record) {
this.record = _.cloneDeep(record);
this.cache = record;
}
}
});
HTML
<div class="container">
<form class="form-horizontal" #submit.prevent="doEdit(record)">
<div class="row">
<div class="col-md-12">
<label>Name</label>
<input type="text" class="form-control" v-el:record-name v-model="record.name">
</div>
<div class="col-xs-12" style="margin-top:15px">
<button type="submit" class="col-xs-12 btn btn-success">Save</button>
</div>
</div>
</form>
<hr>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="r in list">
<td class="text-center" style="width:90px"> {{ r.id }} </td>
<td> {{ r.name }} </td>
<td class="text-center" style="width:90px">
<span class="btn btn-warning btn-xs" #click="edit(r)"><i class="fa-fw fa fa-pencil"></i></span>
</td>
</tr>
</tbody>
</table>
</div>
You can replace the old object with the cloned-updated one.
doEdit: function (record) {
var index = _.indexOf(this.list, this.cache);
this.list.splice(index, 1, record);
}
https://jsbin.com/ruroqu/3/edit?html,js,output
If you want to save the value only after user submitted, you should not bind the record directly such as v-model="record.name".
And we can use Vue.set to change attributes of the original record.
Let's try: JS Bin
How get data-bind value in ViewsModel from Button in html table when click ?
Please help me?
Views:
<table border="1">
<thead>
<tr>
<th>
Id
</th>
<th>
Naziv
</th>
</tr>
</thead>
<tbody data-bind="foreach: customers">
<tr>
<td data-bind="text: Id_dobavljaca">
</td>
<td data-bind="text: NazivDobavljaca">
</td>
<td>
<button data-bind="click: edit, value: Id_dobavljaca">
Edit</button>
<button >
Test</button>
</td>
</tr>
</tbody>
</table>
ViewModel:
define(function (require) {
var app = require('durandal/app'), system = require('durandal/system'),
ko = require('knockout');
return {
customers: ko.observableArray([]),
activate: prikazi
}
});
function prikazi() {
var system = require('durandal/system');
var that = this;
system.log('krenu po podatke');
$.ajax({
type: 'GET',
url: '/Durandal/VratiDobavljace',
dataType: "json",
success: function (data) {
that.customers(data);
},
error: function (jq, st, error) {
alert(error);
}
});
system.log('doneo podatke');
edit = function edit1(Id_dobavljaca) {
var system = require('durandal/system');
alert(Id_dobavljaca);
var router = require('plugins/router');
router.navigate('treci/' + 123456);
};
return that.customers
}
I want to pass value ( Id_dobavljaca )in ViewsModels when click button in html table..
Thanks alot!
Martin
In DurandalJS, the object that you return from your requirejs module that is your view model is the object bound to the view. The activate function will be called when DurandalJS composes your view and view model, you can read more here.
In your current implementation the observableArray customers is a property on your view model and can be bound to your view, which is great and I assume working.
However, from looking at your current implementation you have not exposed the edit function on your view model which means it cannot be bound to the UI and used.
I have refactored your view model:
define(function(require) {
var app = require('durandal/app'),
system = require('durandal/system'),
router = require('plugins/router'),
ko = require('knockout');
var customers = ko.observableArray([]);
return {
customers: customers,
edit: function(context) {
alert(context.Id_dobavljaca);
router.navigate('treci/' + context.Id_dobavljaca);
},
activate: function() {
system.log('krenu po podatke');
return $.ajax({
type: 'GET',
url: '/Durandal/VratiDobavljace',
dataType: "json"
})
.done(function(data) { customers(data); })
.fail(function(jq, st, error) { alert(error); })
.always(function() { system.log('doneo podatke'); });
}
}
});
This refactoring exposes the customers observableArray property and the edit function. The activate function also loads your data and returns the promise back to the DurandalJS composition life cycle.
Now, you will notice that the edit function takes an argument called context, this is a knockoutjs thing. When a function is bound to the click binding the first argument passed to the function is the binding context, you can read more here.
Using this refactored view model, in your markup you want to bind your edit button to the edit function on the $root context, which is your view model.
<td>
<button data-bind="click: $root.edit">Edit</button>
<button>Test</button>
</td>
Hopefully, the snippet below can demonstrate this explanation.
var example1 = {
customers: ko.observableArray([{
Id_dobavljaca: 123,
NazivDobavljaca: 'Martin',
edit: function(context) {
alert('Id_dobavljaca: ' + this.Id_dobavljaca);
alert('Id_dobavljaca: ' + context.Id_dobavljaca);
}
}, {
Id_dobavljaca: 321,
NazivDobavljaca: 'Anish',
edit: function(context) {
alert('Id_dobavljaca: ' + this.Id_dobavljaca);
alert('Id_dobavljaca: ' + context.Id_dobavljaca);
}
}, ])
}
ko.applyBindings(example1, $('#example1')[0]);
var example2 = {
customers: ko.observableArray([{
Id_dobavljaca: 123,
NazivDobavljaca: 'Martin'
}, {
Id_dobavljaca: 321,
NazivDobavljaca: 'Anish'
}, ]),
edit: function(context) {
alert('Id_dobavljaca: ' + context.Id_dobavljaca);
}
}
ko.applyBindings(example2, $('#example2')[0]);
<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>
<p>Edit function is a property on each customer object<p>
<table id="example1" border="1">
<thead>
<tr>
<th>
Id
</th>
<th>
Naziv
</th>
</tr>
</thead>
<tbody data-bind="foreach: customers">
<tr>
<td data-bind="text: Id_dobavljaca">
</td>
<td data-bind="text: NazivDobavljaca">
</td>
<td>
<button data-bind="click: edit">
Edit</button>
<button>
Test</button>
</td>
</tr>
</tbody>
</table>
<br>
<br>
<br>
<p>Edit function is a property on the view model<p>
<table id="example2" border="1">
<thead>
<tr>
<th>
Id
</th>
<th>
Naziv
</th>
</tr>
</thead>
<tbody data-bind="foreach: customers">
<tr>
<td data-bind="text: Id_dobavljaca">
</td>
<td data-bind="text: NazivDobavljaca">
</td>
<td>
<button data-bind="click: $root.edit">
Edit</button>
<button>
Test</button>
</td>
</tr>
</tbody>
</table>
I hope this helps.
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
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>