I have a few viewmodels, which contains a few functions like:
public override string ToString()
{
return $"{M_IV_ID} {M_IV_INVOICEAMOUNT} {M_IV_MANDANT} {M_IV_TOTALAMOUNT} {M_IV_VEHICLE_PURCHASE} {M_IV_URGENT}";
}
And update / insert functionalities. All of them work. I wrote them separately from my Asp.NET project and tested them since I wanted to do my back end first.
Now I tried to use those functions in my Asp.NET application:
First I get Data from the db in my Controller:
M_IV_INVOICE invoice = QueryBuilder.GetFirstOrDefault(new M_IV_INVOICE(), #"WHERE M_IV_ID IN (75693)");
And pass it to my view:
public ActionResult Index()
{
return View(invoice);
}
Now I tried to use my toString() method just to see if it works:
<form id="formUpdate" runat="server">
<div>
<asp:button id="Button" class="btn btn-primary" runat="server">Update</asp:button>
</div>
</form>
#{
string MyFunction()
{
return Model.ToString();
}
}
#section Scripts
{
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
var button = document.getElementById('Button');
button.addEventListener('click', function () {
var test = "#MyFunction()";
console.log(test);
});
});
</script>
}
And if I do this, it works, and I get my model values printed in the browser console.
If I try to use my Update function instead of the toString() method:
public bool Update()
{
using (IDbConnection conn = ConnectionManager.GetConnection())
{
try
{
conn.Query(QueryBuilder.BuildUpdateForEntity(this, string.Format(" WHERE {0} = {1}", GetKey().Item1, GetKey().Item2)));
return true;
}catch(Exception e)
{
// TODO: Exceptionhandling
}
}
return false;
}
I get a Ora Exception right when the page loads, no button click performed:
Oracle.DataAccess.Client.OracleException: ORA-00936: missing expression
I struggle with understanding why, though.
The select I perform in my controller to get the data in the first place is working just fine, and as I said, if I print my model on button click, it works too.
What am I missing?
Edit:
It seems like the function is directly called while the eventhandler is added and since it has no data at the time, the ORA error is occurring.
But why? I don't understand this behavior.
Another Edit:
It has indeed something to do with the model isn't ready or something:
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
var button = document.getElementById('Button');
button.addEventListener('click', function () {
setTimeout(() => { console.log("#MyFunction()") }, 3000);
});
});
</script>
I added the delay and no error on loading the page.
But that would be just a band aid, not a solution.
If someone has an idea how to prevent that behavior, I would appreciate it.
The #{}symbol is used to write server-side C# or VB code with HTML code, so in the javascript, when you use the #MyFunction() function, the Myfunction will execute. You could set a break point in the MyFunction and the button click event to check it, no matter using the ToString() method or the Update() method, they will work when the DOMContent Loaded. So, when use the Update() method, you will meet the missing expression error.
Based on your description, you want to do some action (call the server-side function/method) when user clicks the button, right? If that is the case, I suggest in the controller you can add the Myfunction and Update action methods, then, in the button click event, you can use JQuery get(), post() or Ajax method to call the action method, refer the following code:
Controller:
[HttpPost]
public JsonResult AjaxMethod(string name)
{
PersonModel person = new PersonModel
{
Name = name,
DateTime = DateTime.Now.ToString()
};
return Json(person);
}
and View Page:
<input type="text" id="txtName" />
<input type="button" id="btnGet" value="Get Current Time" />
#section Scripts{
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
$(function () {
$("#btnGet").click(function () {
$.ajax({
type: "POST",
url: "/Test/AjaxMethod",
data: { name: $("#txtName").val() },
success: function (response) {
alert("Hello: " + response.name + " .\nCurrent Date and Time: " + response.dateTime);
},
failure: function (response) {
alert(response.responseText);
},
error: function (response) {
alert(response.responseText);
}
});
});
});
</script>
}
I have ajax that return partial view with strongly type
I want in the success to get the values of the model.
is it possible?
the code return:
return View("Test", Model);
in the ajax:
I want to get the model in the data varible
success: function (data) {
data.
}
Your Partial View would need to return JSON data for you to be able to access the data like that.
In your controller (I'm assuming this is a HTTPPost call):
return Json(new { id = 1, name = "Test" });
In your JS Ajax call:
success: function(data) {
alert(data.name); //alerts 'Test'
}
update
OK, if you want to have the Partial View returned and the model, you could return the View as you already are then convert the model to a JSON string to be accessed and used in JS in the View maybe? Here's a rough example...
so in Controller:
using System.Web.Script.Serialization;
...
var jsonstring = new JavaScriptSerializer().Serialize(Model);
...
ViewBag.JsonString = jsonString;
then in the Partial View:
#{
var jsonString = ViewBag.JsonString;
}
<script>
var data = JSON.parse("#jsonString");
alert(data.name); //alerts 'Test'
</script>
No, for that you need to return JsonResult back from controller action which would be like:
return Json(new {response = Model });
Now it ajax success, you can access the result from json object which is returned :
success: function (data) {
console.log(data);
}
Try this is Ajax form
OnSuccess = "ShowMessage()"
and script is
<script>
function ShowMessage() {
document.getElementById('info').value='YOUR_MESSAGE';
setTimeout(function () {
$("#info").hide();
}, 3000);
}
<script>
and Your Html tag should be like this
<div id="info"></div>
I have the following in my view
#using (Ajax.BeginForm("Search", "Home", null,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "gridContent",
}, new { #class = "search" }))
{
<input type="submit" value="Search" />
}
<div id="gridContent">
</div>
This is what returns /Home/Search
#model List<TestTable.Models.People>
#{
Layout = null;
}
#{
var grid = new WebGrid(Model, canPage: true, canSort: true, rowsPerPage: 5, ajaxUpdateContainerId: "tableDiv"); grid.Pager(WebGridPagerModes.NextPrevious);
}
<div id="tableDiv">
#grid.GetHtml(
columns: grid.Columns(
grid.Column("Name", " Name")
))
</div>
This works good in MVC3, however MVC4 sends a script on every new search,
causing one new additional request for each submit button click for every paging and sorting query.
Here is how it looks:
"http://localhost:59753/Home/Search".
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297281115"
"http://localhost:59753/Home/Search".
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297284491"
"http://localhost:59753/Home/Search?sort=Name&sortdir=ASC&__swhg=1394297284490"
Any ideas how to fix that?
Thanks in advance!
The reason this is happening is because the WebGrid control injects the following script into your DOM every time you render it (in your case every time you submit the AJAX form because the WebGrid is situated in a partial that you are injecting in your DOM):
<script type="text/javascript">
(function($) {
$.fn.swhgLoad = function(url, containerId, callback) {
url = url + (url.indexOf('?') == -1 ? '?' : '&') + '__swhg=' + new Date().getTime();
$('<div/>').load(url + ' ' + containerId, function(data, status, xhr) {
$containerId).replaceWith($(this).html());
if (typeof(callback) === 'function') {
callback.apply(this, arguments);
}
});
return this;
}
$(function() {
$('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {
var self = $(this);
var containerId = '#' + self.data('swhgcontainer');
var callback = getFunction(self.data('swhgcallback'));
$(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
$(containerId).swhgLoad($(this).attr('href'), containerId, callback);
return false;
});
})
});
function getFunction(code, argNames) {
argNames = argNames || [];
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) {
fn = fn[parts.shift()];
}
if (typeof (fn) === "function") {
return fn;
}
argNames.push(code);
return Function.constructor.apply(null, argNames);
}
})(jQuery);
</script>
This script is baked into the WebGrid helper and there's not much you could do against it once you enable AJAX on your WebGrid. In this script you will undoubtedly notice how it subscribes to the click event of the pagination anchors in a lively manner:
$(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
$(containerId).swhgLoad($(this).attr('href'), containerId, callback);
return false;
});
which is all sweet and dandy except that every time you click on the submit button you are injecting this script into your DOM (because your WebGrid is in the partial) and basically you are subscribing to the click event of the pagination anchors multiple times.
It would have been great if the authors of this WebGrid helper have left you the possibility to replace this delegate with a standard click handler registration which would have been ideal in this case as it wouldn't create multiple event registrations, but unfortunately the authors didn't left you with this possibility. They just assumed that the WebGrid would be part of the initial DOM and thus their script.
One way would be to subscribe to the OnBegin handler of the Ajax form submission and simply undelegate the existing event handlers because they will be overwritten once you refresh the DOM:
#using (Ajax.BeginForm("Search", "Home", null,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
OnBegin = "callback",
HttpMethod = "POST",
UpdateTargetId = "gridContent",
}, new { #class = "search" }))
{
<input type="submit" value="Search" />
}
<div id="gridContent"></div>
<script type="text/javascript">
var callback = function (a) {
$('#tableDiv').parent().undelegate('#tableDiv a[data-swhglnk="true"]', 'click');
};
</script>
But to be honest, personally I just hate all this automatically generated scripts and simply never use any Ajax.* helpers stuff as well as activating AJAX on the WebGrid. I prefer to unobtrusively AJAXify the elements I want using jQuery which provides me with far greater control over what's happening. This way I would simply have externalized the bunch of automatically generated javascript by the WebGrid helper into a separate js file that I would have included in my View and there wouldn't be any needs of unregistering and cleaning the mess of the duplicate event handlers created by following the standard way of doing things.
A bit late but I had a similar problem and couldn't find any description of it so thought I'd add it here in case it can help somebody.
No matter what I tried the sorting and paging requests were always duplicated. I tried fixes as described here but even before I had done any type of AJAX update I got the duplication.
I put that problem on hold and after failing to style the pagination to my satisfaction I created my own as a partial view. When deleting the old pagination the duplication was no more..... Haven't taken the time to try and figure out why, just happy it is solved.
So I removed this:
#grid.Pager(mode: WebGridPagerModes.All, firstText: "First", previousText: "Prev", nextText: "Next", lastText: "Last")
As I said, in case it helps someone.
Looks like the paging and sorting links are bound using "on"/"live" event every time the grid is rendered. It could be solved unbinding the events of the elements of the grid before rendering the grid html or on the ajaxUpdateCallback method.
$('#tableDiv').andSelf().unbind();
I have solved this for MVC 5, you would need to use ajax call rather than using the ajax form, catch the ajax response and replace the partial page's DOM generated by the webgrid helper using below:
var data = data.replace('<script type="text/javascript">', '<script type="text/javascript"> $(".table").undelegate();');
$('#YourParentDivIDWherePartialIsRendered').undelegate();
$.ajax
(
{
contentType: "application/json; charset=utf-8",
type: 'POST',
url: '/YourController_Name/YourAction_Name',
data: JSON.stringify(YourModel),
success: function (data) {
//Added to undelegate the old events tagged to the partial view's grid.
var data = data.replace('<script type="text/javascript">', '<script type="text/javascript"> $(".table").undelegate();');
$('#YourParentDivIDWherePartialIsRendered').undelegate();
$('#accountSearch-grid').html(data);
$(document).foundation();
},
error: function (xhr, status, error) {
alert(error);
}
});
put this script to your Index.cshtml or js file
<script type="text/javascript">
(function($) {
$.fn.swhgLoad = function(url, containerId, callback) {
url = url + (url.indexOf('?') == -1 ? '?' : '&') + '__swhg=' + new Date().getTime();
$('<div/>').load(url + ' ' + containerId, function(data, status, xhr) {
$containerId).replaceWith($(this).html());
if (typeof(callback) === 'function') {
callback.apply(this, arguments);
}
});
return this;
}
$(function() {
$('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {
var self = $(this);
var containerId = '#' + self.data('swhgcontainer');
var callback = getFunction(self.data('swhgcallback'));
$(containerId).parent().delegate(containerId + ' a[data-swhglnk="true"]', 'click', function() {
$(containerId).swhgLoad($(this).attr('href'), containerId, callback);
return false;
});
})
});
function getFunction(code, argNames) {
argNames = argNames || [];
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) {
fn = fn[parts.shift()];
}
if (typeof (fn) === "function") {
return fn;
}
argNames.push(code);
return Function.constructor.apply(null, argNames);
}
})(jQuery);
then, processing your grid html string in class
string html = _grid.GetHtml(
columns: _columns,
...
).ToHtmlString();
Regex reg1 = new Regex("<script(.|\n)*?</script>", RegexOptions.IgnoreCase);
string _script = reg1.Match(html).Value.ToString();
html = html.Replace(_script, "");
in the index file:
#MvcHtmlString.Create(#html)
that' all
Actually the solution $('#tableDiv').parent().off('click', '#tableDiva[data-swhglnk="true"]'); is working perfectly but it remains the __swhg in the call URL of pagination so here is the code for removing the extra __swhg in the call of page using AJAX.
$(document).ajaxComplete(function () {
$('a[data-swhglnk="true"]').click(function () {
$(this).attr("href", $(this).attr("href").replace(/(^|&)__swhg=([^&]*)/, ''));
});
});
If anyone is going through these and still having problems, I believe I found the real issue. The script was never being rendered twice on the page for me so the accepted answer didn't make sense.
The issue was instead with this line:
$('table[data-swhgajax="true"],span[data-swhgajax="true"]').each(function() {
If the pager is not in the footer of the table (by defining it in the setup of the webgrid) and is instead defined by calling grid.pager() it will put the pager in a span. This means when the above line is called it binds the click event to the parent of the table (gridContent) and to the parent of the span (gridContent).
There are a few options, but what I opted to do was essentially what top answer said, and remove the delegates for that element like so:
$("#gridContent").off("click", "**");
And then rebind the same exact click function, but only bind it to the span. So the line referenced above I changed to:
$('span[data-swhgajax="true"]').each(function () {
And it works as intended. This will of course break any pagers on the same page that are part of the table.
First Remove ajax Update Callback from Web Grid and add following java script code below to web grid or web grid Container div:
$("#WebgridContainerDiv table a").click(function (event) {
event.preventDefault();
var href = $(this).attr("href");
$.ajax({
url: href,
dataType: 'html',
success: function (data) {
$("#WebgridContainerDiv").empty();
$("#WebgridContainerDiv").html(data);
}
})
});
So I have a scenario where I am have knockout binding on my main page.
Index.cshtml:
<div data-bind="foreach breadcrumbs">
<div>
<script>
var IndexViewModel = function () {
var self = this;
self.breadcrumbs = ko.observableArray();
};
ko.applyBindings(IndexViewModel);
</script>
And a Partial View that loads inside the Index.cshtml. The partial view has its own knockout bindings:
<div id="someId">
</div>
<script>
var PartialViewModel = function () {
var self = this;
self.someFunction = function(){
// Access Breadcrumbs here
};
};
ko.applyBindings(PartialViewModel, "someId");
</script>
I want to access the breadcrumbs observableArray from the second partial view and add items to them. I am not even sure if this possible. Please help. I am also using sammy.js but for this purpose its not that relevant.
I don't like having to have dependencies between view models, so I've previously used a jQuery pubsub plugin to allow view models to talk to each other in a decoupled manner. You can see an example of it in this answer
Basically, when you update the breadcrumb, call publish with the new array, and the other view model would subscribe to that event.
An easy solution (but not very clean one) would be to store the indexViewModel in a global variable.
Index.cshtml:
var IndexViewModel = function () {
var self = this;
self.breadcrumbs = ko.observableArray();
};
// we create a global variable here
window.indexViewModel = new IndexViewModel();
ko.applyBindings(window.indexViewModel);
And then you can access this indexViewModel from your partial view model:
var PartialViewModel = function () {
var self = this;
self.someFunction = function(){
// Access Breadcrumbs here
// better use some wrapping functions
// instead of accessing the array directly
window.indexViewModel.breadcrumbs.push({});
};
};
ko.applyBindings(new PartialViewModel(), "someId");
</script>
Do we have better ways to handle it?
#foreach (var item in Model)
{
<div id="divDetail#{#item.CategoryId}"/>
#Ajax.ActionLink(
item.CategoryName,
"GetDetails",
new { id = item.CategoryId },
new AjaxOptions() { UpdateTargetId = string.Format("divDetail{0}", item.CategoryId) })
}
</div>
}
I would use the HTML.ActionLink helper method to generate the link and then use my custom jQuery ajax call to get the data. The advantage of doing this is i have full control so that i can do some manipulation of the response data before showing in the detail div.
I added a CSS class to the link so that i can be more specific (in selection of element) when binding my functionality.
#foreach (var item in Model)
{
<div id='divDetail#(item.ID)'></div>
#Html.ActionLink(item.CategoryName, "GetDetails", new { #id = item.CategoryId}, new {#id= "link-"+item.CategoryId, #class="lnkItem" })
}
and the script is
<script type="text/javascript">
$(function () {
$(".lnkItem").click(function (e) {
e.preventDefault();
var itemId = $(this).attr("id").split("-")[1]
$.get($(this).attr("href"), { id: itemId }, function (data) {
//i am free to do anything here before showing the data !
$("#divDetail" + itemId).html(data);
})
});
});
</script>