How do I update the model for a WebGrid using Ajax? - html

I'm trying to create a WebGrid that allows me to update the data source with Ajax. The data source is assigned a List<> containing a model I created to hold the data. The WebGrid populates correctly and I have no problem sorting using the header of the WebGrid or paging.
The code for the View:
#model IEnumerable<TestDataModel.Models.TestData>
#{
ViewBag.Title = "Test Website";
var grid = new WebGrid(canPage: true, canSort: true, ajaxUpdateContainerId: "grid", ajaxUpdateCallback: "callBack");
grid.Bind(Model, rowCount: 10);
grid.Pager(WebGridPagerModes.All);
#grid.GetHtml(htmlAttributes: new { id = "grid" },
tableStyle: "webgrid-table",
headerStyle: "webgrid-header",
alternatingRowStyle: "webgrid-alternating-row",
rowStyle: "webgrid-row-style",
mode: WebGridPagerModes.All,
firstText: "<< First",
previousText: "< Prev",
nextText: "Next >",
lastText: "Last >>",
columns: grid.Columns("This is a place holder,
I have a lot of columns"
));
}
<div>
<br />
<p>
Page Number: <input type="text" name="pageNumber" id="pageNumber" />
</p>
<p>
<input type="button" name="pageNumberButton" id="pageNumberButton" value="Page" />
</p>
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#pageNumberButton").click(function () {
var pageNumber = $("#pageNumber").val();
$.ajax({
url: '#Url.Action("GetData", "Test")?pageNumber=' + pageNumber,
success: function (data) {
$("#grid").html(data);
},
error: function (data) {
alert("Error");
}
})
});
});
</script>
The bottom script tag is the ajax call that I'm trying to use to update the WebGrid. All that the "GetData" does is create a list pulling data from an SQL server and returns Json(testData, JsonRequestBehavior.AllowGet).
Code for Controller:
public ActionResult GetData(string pageNumber)
{
List<TestData> testData = new List<TestData>();
if (string.IsNullOrEmpty(pageNumber))
{
testData = Data.Data.TestDataGet();
}
else
{
try
{
var pageInt = Convert.ToInt32(pageNumber);
List<ParameterSQL> parameter = new List<ParameterSQL>();
parameter.Add(new ParameterSQL { Name = "#pageOffset", Value = pageInt });
testData = Data.Data.TestDataGet(parameter);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
return Json(testData, JsonRequestBehavior.AllowGet);
}
The error that I'm experiencing is when I push the button the WebGrid disappears entirely. The reason that I need to be able to update the WebGrid data source is because my table is millions of lines long and I'm only grabbing them a few thousand at a time. Once the user reaches the end of the of the list I want them to be able to load more using a SQL query that I already have working. Any help would be great, thanks.

Related

How do I insert a value in a custom field in a table in Prestashop?

I added a custom field named "deldate" in "ps_orders" table, and I added a text box on "OPC" checkout page. Now when I click on order confirm button the value in the textbox should be saved in "deldate" field of "ps_orders" table.
The textbox is showing perfectly but in which files do I need to make changes to save the textbox value in the table?
(Theme is default one.)
class/order/order.php
class OrderCore extends ObjectModel
{
public $deldate;
}
And
public static $definition = array(
'fields' => array(
'deldate'=> array('type' => self::TYPE_STRING),
),
)
Shopping-cart.tpl
<div class="box">
<div class="required form-group">
<form method="post">
<label for="Fecha de entrega deseada">{l s='Desired delivery date' mod='deldate'}</label>
<input type="text" id="deldate" name="deldate" class="form-control" value="hello" />
</form>
</div>
</div>
Ok, I figured out the solution...
If you want to add some information to the order in the checkout process you have to save this informations elsewhere, if you look the cart table are very similar to order table.
Why you have to do this? Because you don't have an order before the confirmation by customer, so until the checkout is not complete that informations can't be saved in the order table.
So, first, create the field in database, in this case you have to add in ps_orders and in the ps_cart as well.
(In your case I suggest to use a DATETIME field)
Second, override the Order class:
class Order extends OrderCore
{
public function __construct($id = null, $id_lang = null)
{
self::$definition['fields']['deldate'] = array('type' => self::TYPE_DATE);
parent::__construct($id, $id_lang);
}
}
and the Cart class:
class Cart extends CartCore
{
public function __construct($id = null, $id_lang = null)
{
self::$definition['fields']['deldate'] = array('type' => self::TYPE_DATE);
parent::__construct($id, $id_lang);
}
}
Now we have to save the field during the checkout process, so we override the OrderController:
class OrderController extends OrderControllerCore
{
public function processAddress()
{
parent::processAddress();
// Here we begin our story
if(Tools::getIsset('deldate')) // Check if the field isn't empty
{
$deldate = Tools::getValue('deldate');
// Here you must parse and check data validity (I leave to you the code)
/* ... */
// Assign the data to context cart
$this->context->cart->deldate = $deldate;
// Save information
$this->context->cart->update();
}
}
}
Now you have to 'transport' this informations from the cart to the order, this will be done through the PaymentModule class, specifically with the validateOrder method.
So, another override:
class PaymentModule extends PaymentModuleCore
{
public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown', $message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false, $secure_key = false, Shop $shop = null)
{
$result = parent::validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method, $message, $extra_vars, $currency_special, $dont_touch_amount, $secure_key, $shop);
if($result)
{
$oldcart = new Cart($id_cart);
$neworder = new Order($this->currentOrder);
$neworder->deldate = $oldcart->deldate;
$neworder->update();
return true; // important
}
else
{
return $result;
}
}
}
After all of this you have the deldate field saved. However, I absolutely don't suggest this method, it's more safe and simple with a module and hooks... But this is another story :)
This will works only with the five steps checkout.
For next code lines, God save me...
If you want to works with OPC you have to dirty your hands with JS and override the OrderOpcController.
Start with the JS, edit the order-opc.js in js folder of enabled theme, find bindInputs function and append this lines of code:
function bindInputs()
{
/* ... */
$('#deldate').on('change', function(e){
updateDelDateInput(); // custom function to update deldate
});
}
then append to the file your custom function:
function updateDelDateInput()
{
$.ajax({
type: 'POST',
headers: { "cache-control": "no-cache" },
url: orderOpcUrl + '?rand=' + new Date().getTime(),
async: false,
cache: false,
dataType : "json",
data: 'ajax=true&method=updateDelDate&deldate=' + encodeURIComponent($('#deldate').val()) + '&token=' + static_token ,
success: function(jsonData)
{
if (jsonData.hasError)
{
var errors = '';
for(var error in jsonData.errors)
//IE6 bug fix
if(error !== 'indexOf')
errors += $('<div />').html(jsonData.errors[error]).text() + "\n";
alert(errors);
}
// Here you can add code to display the correct updating of field
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
if (textStatus !== 'abort')
alert("TECHNICAL ERROR: unable to save message \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);
}
});
}
Then override the OrderOpcController, copy all the init method and change the line of code as below:
class OrderOpcController extends OrderOpcControllerCore
{
public function init()
{
// parent::init(); // comment or delete this line
FrontController::init(); // Very important!
// Then in this switch `switch (Tools::getValue('method'))` add your case
/* ... */
case 'updateDelDate':
if(Tools::isSubmit('deldate'))
{
$deldate = urldecode(Tools::getValue('deldate'));
// Here you must parse and check data validity (I leave to you the code)
/* ... */
// Assign the data to context cart
$this->context->cart->deldate = $deldate;
// Save information
$this->context->cart->update();
$this->ajaxDie(true);
}
break;
/* ... */
}
}
Obviously, is necessary the override of Order, Cart and PaymentModule as well.
PS: I hope that I didn't forget anything.
You can try also with this module
https://www.prestashop.com/forums/topic/589259-m%C3%B3dulo-selector-fecha-en-pedido/?p=2489523
Try this in the override of the class Order
class Order extends OrderCore
{
public function __construct($id = null, $id_lang = null)
{
parent::__construct($id, $id_lang);
self::$definition['fields']['deldate'] = array('type' => self::TYPE_STRING);
Cache::clean('objectmodel_def_Order');
}
}
The Cache::clean is need because getDefinition tries to retrieve from cache, and cache is set without the override on parent::__construct
I then tried to create a new empty Order and get the definition fields and it showed there, so it should save to mysql
$order = new Order();
var_dump(ObjectModel::getDefinition($order));exit;

Pass data from controller to view via JSONResult

I'd like to send ViewModel from controller to view in JSON format.
Controller:
public ActionResult Select(int pageLimiter)
{
var viewModel = new ArticlesViewModel
{
Articles = this.Service.GetArticles(0, 0, 0),
ArticlesTotal = this.Service.CountArticles(0),
Pages = new List<string>
{
"1", "2", "3"
}
};
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
View:
<ul class="articleList">
#if (#Model != null)
{
foreach (var article in #Model.Articles)
{
<li>
<header>#article.Title</header>
<nav>
<span>#article.AuthorName</span> |
<time>#article.PublishDate.ToString("")</time> |
<span>#article.CategoryName</span> |
<span>#article.Comments Comments</span>
</nav>
<section>
#article.Content
</section>
</li>
}
}
</ul>
<script type="text/javascript">
$(document).ready(function () {
GetArticles(5);
$("#selectPager").change(function () {
var selectedItem = "";
$("#selectPager option:selected").each(function () {
selectedItem = $(this).text();
});
GetArticles(selectedItem);
});
});
function GetArticles(pageLimitValue) {
$.ajax(
{
url: "/Articles/Select",
dataType: "json",
data: { pageLimiter: pageLimitValue },
async: true,
beforeSend: function () {
alert("before");
},
complete: function (data) {
#Model = SOME_MAGIC_TRICKS
}
});
}
As you can see, in the complete event are words SOME_MAGIC_TRICKS. In this place I'd like to set #Model obtained from controller. Is it possible at all? How to insert data from ajax result to view model (it's null by default)?
You are trying to modify server variable from client's code. It's not possible.
If you want to re-render your page's content on complete, you may render <ul class="articleList"> with PartialView and return same partial view instead of JsonResult. Further, oncomplete handler will update your <ul class="articleList"> with returned content.
You can send data doing serialize it may be like:
public ActionResult Select(int pageLimiter)
{
var viewModel = new ArticlesViewModel
{
Articles = this.Service.GetArticles(0, 0, 0),
ArticlesTotal = this.Service.CountArticles(0),
Pages = new List<string>
{
"1", "2", "3"
}
};
string myjsonmodel = new JavaScriptSerializer().Serialize(viewModel );
return Json(jsonmodel = viewModel, JsonRequestBehavior.AllowGet);
}
dont forget using using System.Web.Script.Serialization;
Edit:
To deserialize object try this:
#{
JavaScriptSerializer jss= new JavaScriptSerializer();
User user = jss.Deserialize<User>(jsonResponse);
}

I cannot get my jqGrid to populate

Edited with working code as per Mark's answer below.
I'm really starting to loath MVC. I have been trying all day to get a simple grid to work, but I'm having more luck banging a hole in my desk with my head.
I'm trying to implement a search page that displays the results in a grid. There are 3 drop-down lists that the user can use to select search criteria. They must select at least one.
After they have searched, the user will be able to select which records they want to export. So I will need to include checkboxes in the resulting grid. That's a future headache.
Using JqGrid and Search form - ASP.NET MVC as a reference I have been able to get the grid to appear on the page (a major achievement). But I can't get the data to populate.
BTW, jqGrid 4.4.4 - jQuery Grid
here is my view:
#model Models.ExportDatex
<script type="text/javascript">
$(document).ready(function () {
$('#btnSearch').click(function (e) {
var selectedSchool = $('#ddlSchool').children('option').filter(':selected').text();
var selectedStudent = $('#ddlStudent').children('option').filter(':selected').text();
var selectedYear = $('#ddlYear').children('option').filter(':selected').text();
var selectedOption = $('#exportOption_1').is(':checked');
if (selectedSchool == '' && selectedStudent == '' && selectedYear == '') {
alert('Please specify your export criteria.');
return false;
}
selectedSchool = (selectedSchool == '') ? ' ' : selectedSchool;
selectedStudent = (selectedStudent == '') ? ' ' : selectedStudent;
selectedYear = (selectedYear == '') ? ' ' : selectedYear;
var extraQueryParameters = {
school: selectedSchool,
student: selectedStudent,
year: selectedYear,
option: selectedOption
};
$('#searchResults').jqGrid({
datatype: 'json',
viewrecords: true,
url: '#Url.Action("GridData")?' + $.param(extraQueryParameters),
pager: '#searchResultPager',
colNames: ['SchoolID', 'Student Name', 'Student ID', 'Apprenticeship', 'Result'],
colModel: [
{ name: 'SchoolID' },
{ name: 'Student Name' },
{ name: 'StudentID' },
{ name: 'Apprenticeship' },
{ name: 'Result' }]
}).trigger('reloadGrid', [{ page: 1 }]);
});
});
</script>
#using (Html.BeginForm("Index", "Datex", FormMethod.Post))
{
<h2>Export to Datex</h2>
<div class="exportOption">
<span>
#Html.RadioButtonFor(model => model.ExportOption, "true", new { id = "exportOption_1" })
<label for="exportOption_1">VET Results</label>
</span>
<span>
#Html.RadioButtonFor(model => model.ExportOption, "false", new { id = "exportOption_0" })
<label for="exportOption_0">VET Qualifications</label>
</span>
</div>
<div class="exportSelectionCriteria">
<p>Please specify the criteria you want to export data for:</p>
<table>
<tr>
<td>School:</td>
<td>#Html.DropDownListFor(model => model.SchoolID, Model.Schools, new { id = "ddlSchool" })</td>
</tr>
<tr>
<td>Student: </td>
<td>#Html.DropDownListFor(model => model.StudentID, Model.Students, new { id = "ddlStudent" })</td>
</tr>
<tr>
<td>Year Completed:
</td>
<td>
#Html.DropDownListFor(model => model.YearCompleted, Model.Years, new { id = "ddlYear" })
</td>
</tr>
</table>
<table id="searchResults"></table>
<div id="searchResultPager"></div>
</div>
<div class="exportSearch">
<input type="button" value="Search" id="btnSearch" />
<input type="submit" value="Export" id="btnExport" />
</div>
}
Here is my Controller. As we don't have a database yet, I am just hardcoding some results while using an existing table from a different DB to provide record IDs.
[HttpGet]
public JsonResult GridData(string sidx, string sord, int? page, int? rows, string school, string student, string year, string option)
{
using (SMIDbContainer db = new SMIDbContainer())
{
var ds = (from sch in db.SCHOOLs
where sch.Active.HasValue
&& !sch.Active.Value
&& sch.LEVEL_9_ORGANISATION_ID > 0
select sch).ToList();
var jsonData = new
{
total = 1,
page = 1,
records = ds.Count.ToString(),
rows = (
from tempItem in ds
select new
{
cell = new string[]{
tempItem.LEVEL_9_ORGANISATION_ID.ToString(),
tempItem.SCHOOL_PRINCIPAL,
"40161",
"No",
"Passed (20)"
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
Is the JSON you are passing back to the grid valid? Are you passing back the information that the jqGrid needs? Why setup your jqGrid inside of an ajax call instead of inside your $(document).ready(function () { ?
Here is an example of the portion of code I use to format my json for jqGrid:
var jsonData = new
{
total = (totalRecords + rows - 1) / rows,
page = page,
records = totalRecords,
rows = (
from tempItem in pagedQuery
select new
{
cell = new string[] {
tempItem.value1,
tempItem.value2, ........
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
If you want to make your user search first, you can on the client side, set the jqgrid datatype: local and leave out the url. Then after your user does whatever you want them to do you can have the jqGrid go out and fetch the data, via something like:
$('#gridName').jqGrid('setGridParam', { datatype: 'json', url: '/Controller/getGridDataAction' }).trigger('reloadGrid', [{ page: 1}]);
If you want to pass in the search data, or other values to the controller/action that is providing the data to the jqGrid you can pass it via the postData: option in the jqGrid. To set that before going out you can set it via the setGridParam option as shown above via postData: { keyName: pairData}.
MVC and jqGrid work great...there are a ton of examples on stackoverflow and Oleg's answers are a vast resource on exactly what you are trying to do. No hole in desk via head banging required!

Data-binding MVC-4 to a Knockout.js foreach

We have a form in our web application that, in one place, asks the user to enter a list of values. We wrote this part of the form using Razor and knockout.js so that there is a textbox for each value of the list, similar to this tutorial. How can we data-bind these textboxes to our MVC model?
Here is our form:
#model OurProject.Models.Input.InputModel
#{
ViewBag.Title = "Input";
}
<h2>
Inputs</h2>
<div id="inputKOApp">
#using (Html.BeginForm())
{
<!-- snip - lots of part of our form that work correctly -->
<div class="row-fluid">
<div class="control-group">
<div class="span8 control-label">
#Html.LabelFor(model => model.POSTransactionCodes)
</div>
<div class="controls">
<!-- These are the textboxes we would like to bind to MVC -->
<ul class="pull-right" data-bind="foreach: POSTransactionCodes">
<li>
<input data-bind="value: code" />
Delete</li>
</ul>
<button class="pull-right" data-bind="click: addPOSTransactionCode">
Add another POS Transaction Code</button>
#Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { #class = "help-inline" })
</div>
</div>
</div>
<!-- snip - more cshtml that is irrelevant to the problem -->
</div>
<input type="submit" value="Submit" />
}
</div>
<script type="text/javascript" src='~/Scripts/jquery-1.8.2.min.js'></script>
<script type="text/javascript" src='~/Scripts/knockout-2.1.0.js'></script>
<script type="text/javascript" src='~/Scripts/OP/OP.js'></script>
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Form.js'></script>
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Data.js'></script>
<script type="text/javascript">
var inputApp = $('#inputKOApp')[0];
OP.Input.Form.init(inputApp);
</script>
Here is our knockout.js script, OP.Input.Input.Form.js:
extend(OP, 'OP.Input.Form');
OP.Input.Form = function (jQuery) {
var TransactionCodeView = function () {
var self = this;
self.code = "";
};
//The ViewModel for the page
var ViewModel = function () {
var self = this;
//Fields
/* snip - lots of fields that work as expected */
self.POSTransactionCodes = ko.observableArray([]); //is a list of transaction codes
//Set up with initial data
/* I'm guessing that we won't need this function anymore since MVC will populate
* everything for us, but I'll leave it in until I'm far enough along to know
* I won't need to gut lots of stuff */
self.initialize = function () {
var c = function (data, status, response) {
/* snip - lots of fields that work as expected */
if (status === "success") {
if(data.POSTransactionCodes != null) ko.utils.arrayPushAll(self.POSTransactionCodes, data.POSTransactionCodes);
self.POSTransactionCodes.valueHasMutated();
} else {
}
};
OP.Input.Data.GetInput(c);
}
//When saving, submit data to server
self.save = function (model) {
var c = function (data, status, response) {
if (status === "success") {
} else {
}
};
OP.Input.Data.SaveInput(model, c);
}
//Modifying POSTransactionCodes array
self.removePOSTransactionCode = function (POScode) {
self.POSTransactionCodes.remove(POScode);
}
self.addPOSTransactionCode = function () {
self.POSTransactionCodes.push(new TransactionCodeView());
}
};
//Connect KO form to HTML
return {
init: function (elToBind) {
var model = new ViewModel();
ko.applyBindings(model, elToBind);
model.initialize();
}
};
} ($);
Here is our MVC model:
namespace OurProject.Models.Input
{
public class InputModel : IModel
{
//Snip - lots of properties that aren't interesting for this problem
[Required]
[DisplayName("POS Transaction Codes")]
public List<double> POSTransactionCodes { get; set; }
public InputModel()
{ }
/* I ommitted a few other methods that
* aren't relevant to this problem. */
}
}
I don't see how do you send the data back to the server but you need to name your inputs in way which allows model binding:
If you're binding to a list/collection your inputs should be name like:
<input type="text" name="CollectionPropertyName[index]" />
You can read about Model Binding To A List in this article
So you just need generate proper names for your inputs:
<input data-bind="value: code, attr: { name: 'POSTransactionCodes[' + $index() + ']' }" />
You should note that the above solution may only works if you're using the submit button and send the data form-urlencoded if you are sending the data as json you may need to tweak your serialization logic to make the model binder happy:
In this case your json should something like this:
{
//...
POSTransactionCodes: [ 1 , 3 ]
//..
}
Thanks to nemesv's answer show me light on this one.
But I need to have the "normal" name and id attribute for jquery validation. So I come up with the idea of writing my own data binder.
ko.bindingHandlers.nameId = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor(),
allBindings = allBindingsAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
var bindName = $(element).data('bind-name');
bindName = bindName.replace('{0}', valueUnwrapped);
$(element).attr({name : bindName, id : bindName});
}
};
And use it in html
<input
data-bind="value: qty, nameId: $index"
data-bind-name="inventory[{0}].qty" />
jsfiddle

Binding the selected value from the DropDownList for MVC View using Razor

I have a View that works but I can not figure out how to get the selected value from the DropDownList:
#model IEnumerable<TRP_MVC_Prototype.Models.usp_TM_Select_ShortNameResult>
#using System.Web;
#using System.Web.WebPages;
#using System.Web.Mvc;
#{
ViewBag.Title = "Details";
}
#using (Html.BeginForm("Details", "ProgramSummary", FormMethod.Post, new { id = "Details" }))
{
<div id="main" style="background-color:White">
<h1 style="background-color:transparent;color:Blue;">
<a>You are logged on as: #ViewBag.Message </a>
<span class="DrpDwnLst">DrpDwnLst</span>
#Html.DropDownList("Short_Title", new SelectList(Model, "short_title", "short_title"), "--Select One--").
#Html.ActionLink("Select","Details",new { Shrt_title = ""})
<a style="color:Blue;position:absolute; right:500px"> #Html.ActionLink("Create Program Summary", "Index", "User_Guide") </a>
<a style="color:Blue;position:absolute; right:250px"> #Html.ActionLink("Edit Program Summary", "Index", "User_Guide")</a>
<a style="color:Blue;position:absolute; right:50px"> #Html.ActionLink("Delete TRP", "Index", "User_Guide")</a>
</h1>
<h1 style="background-color:transparent;color:Blue;">Select TRP to View</h1>
<h1 style="color:Gray";>______________________________________________________________________________________________________________________________________________________________________________</h1>
}
The DropDownList displays correctly but I don't know how to return the Selected value in the ActionLink. In the action link the third parameter passes the value back to the controller it currently has "" but I would like to figure out how to reference the selected value instead.
You can do it with jquery. For changing href from a element you may see this post.
You also need to handle change event for your drop down:
$('#short_title').change(function()
{
// do someting here
});
This is my answer
#Html.DropDownList("short_name", ViewBag.DetailsList as SelectList, "--Select One--", new { onchange = "dofunction(this.form.short_name);" });
function dofunction(dropdown) {
debugger;
for (i = 0; i < 194; i++) {
if (dropdown[i].selected == true) {
var Shrt_ttls = dropdown[i].value.toString()
//document.getElementById("shrtLst").value = Shrt_ttls;
$.ajax({
url: "/ProgramSummary/Details?Shrt_titles=" + Shrt_ttls,
type: 'Post',
data: Shrt_ttls,
success: function(result) {
alert( "Short Name is: " + Shrt_ttls); // process the results from the controller action
},
error: function () {
alert ( "no deal");
}
});
}
}
return true;
}
This returns the value from the javascript function to the HTTPPost for Index.
Thank you,
Bruce