performing ajax action from default function throws 'cannot perform runtime binding on a null reference' - ajax

I am receiving a 'cannot perform runtime binding on a null reference' error when executing an ajax call from the default action on a controller.
My route definition is as follows:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have an MVC 5 memecontroller with the following get functions:
public async Task<ActionResult> Index()
{
return View();
}
public async Task<ActionResult> getmeme(string parameter)
{
if (parameter.Equals("improvise"))
{
var absolutelyBarbaric = new string[]{ "adapt ", "improvise ", "overcome"};
if (Request.IsAjaxRequest())
{
return Json(absolutelyBarbaric, JsonRequestBehavior.AllowGet);
}
}
throw new NotImplementedException("to lazy for other examples");
}
the html page adapt.htmlcs is as follows:
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.min.js"
type="text/javascript"></script>
<script>
$(document).ready(function () {
var options = {
url: 'getmeme?parameter=improvise'
};
$.ajax(options).done(function (data) {
var $target = $('#memes');
$target.replaceWith(data);
});
});
</script>
</head>
<body>
<div id="memes">
</div>
</body>
</html>
when I navigate to http://localhost:1234/meme/index, the page loads as normal, with the ajax function calling normally. When i navigate to http://localhost:1234/meme i can see that the index function is called in the debugger, but the ajax call throws an 'cannot perform runtime binding on a null reference' error. Why is this happening, and more importantly, how can i prevent it?
EDIT:
removed use of the ViewBag, since it had no function here

Related

Asp.NET MCV how to call a viewmodel function that performs a db connection on button click correctly?

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>
}

MVC Core ajax and return result is a view

MVC Core, NET 5, not razor pages.
On a view I have three select components (bootstrap-select). I populate them via ViewModel.
"Get request -> Controller -> return View(viewModel);"
What I want...
When I changed value in any select component I do a post request (ajax) to the same controller (other method) and return view with repopulated data.
"'Post request -> Controller -> return View(changedModel);"
As I understood when I did ajax request I should handle it result in success and other cases.
What I should to do to reload page with new data?
Is it possible to achive this with this approach?
Yes, this is possible and you do not need to reload the page, just append the returned html to wherever you want it.
$.ajax({
type: "POST",
url: {your_url},
dataType: "html",
success: function (html) {
$("#someDiv").html(html);
}
});
What I should to do to reload page with new data?
If the post action return the same view as the get action and you want to reload the whole page, I think there is no need to use ajax. You can just redirect to post action with a form submission. If the view returned by the post action is a partialview you want render to the current view, you can use it like that in #cwalvoort answer.
Based on advices of cwalvoort and mj1313
I did:
Render main page with partials. ViewModel transfered to a partial as a parameter
On main page I added eventListners to controls with JS.
When control changes - ajax request to backend happens Controller/GetPartialView
Result from ajax replace html in partial section
Programmatically show needed components, re-add eventListners
PS Really need to learn Blazor or UI Framework :)
Code samples:
// JS
document.addEventListener("DOMContentLoaded", function (event) {
BindSelectActions();
});
function BindSelectActions() {
$('#selectGroups').on('hidden.bs.select', DoPartialUpdate);
$('#selectCompanies').on('hidden.bs.select', DoPartialUpdate);
$('#selectPeriods').on('hidden.bs.select', DoPartialUpdate);
}
function DoPartialUpdate(e, clickedIndex, isSelected, previousValue) {
// ToDo: Implement common script with "CallBackend" function
$.ajax({
type: "POST",
url: 'https://localhost:44352/TestController/TestGetPartial',
// no data its a stub at the moment
// data: $('#form').serialize(),
success: function (data, textStatus) {
$("#testControls").html(data);
$('#selectGroups').selectpicker('show');
$('#selectCompanies').selectpicker('show');
$('#selectPeriods').selectpicker('show');
BindSelectActions();
}
});
}
// Controllers
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<IActionResult> Main()
{
// ViewModel = _helper -> _mediator -> query -> context
return await Task.Run(() => View(new TestViewModel()));
}
[HttpPost]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult TestGetPartial(TestViewModel model)
{
// ViewModel = _helper -> _mediator -> query -> context
var result = new TestViewModel();
result.IsPageReload = "yes";
result.TestCollection = new string[] { "A", "B", "C" };
result.Companies = new List<SelectListItem> { new SelectListItem { Value = "999",
Text = "Test" } };
// ModelState.Clear();
return PartialView("_TestPartial", result);
}
// Main and partial views
#model TestViewModel
#{
ViewData["Title"] = "Test";
}
<div id="testControls">
#await Html.PartialAsync("_TestPartial", Model)
</div>
#section Scripts {
<script type="text/javascript" src="~/js/test.js" asp-append-version="true">
</script>
}
#model TestViewModel
<form>
<div class="d-flex flex-row justify-content-between mt-4">
<div><select id="selectGroups" asp-for="Groups" asp-items="Model.Groups"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Group"></select></div>
<div><select id="selectCompanies" asp-for="Companies" asp-items="Model.Companies"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Company"></select></div>
<div><select id="selectPeriods" asp-for="Periods" asp-items="Model.Periods"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Period"></select></div>
<div><button type="button" class="btn btn-outline-dark">Import</button></div>
</div>
</form>
<div>
#{
if (null != Model.TestCollection)
{
foreach (var item in Model.TestCollection)
{
<p>#item</p>
<br>
}
}
}
</div>

Trying to load a partial view with Ajax.ActionLink

After reading a few other posts on here I've got this far with attempting to load a partial view on the click of a link (the text link may actually change to be an image once I get past this proof of concept stage). The problem is I am being directed to the partial view on click instead of it populating a div within the view I am clicking the link on:
The layout:
<script src="#Url.Content("~/scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
The View:
#Ajax.ActionLink("Item 1 Ajax", "Details", "Portfolio", new { name = "test" }, new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "row1Content",
OnComplete = "portfolioItemLoaded"
})
<div id="row1content"></div>
<script type="text/javascript">
function portfolioItemLoaded() {
alert("Done");
}
</script>
The controller:
public class PortfolioController : Controller
{
public ActionResult Details(string name)
{
return PartialView(name);
}
}
The partial (obviously named test.cshtml):
<p>Item 1 test</p>
Have I missed something obvious? It's the first time I have attempted to use AJAX in MVC, so there might be a school boy error or two.
I am planning on having several different Ajax Action Links that call different partial views, hence the reason I am passing the name in to the controller.
Code fixed
Do you wanna try this, you have a better control, if error, etc....
HTML with helper
#Html.ActionLink("Item 1 Ajax", "Details", "Portfolio", Nothing, New With {.Id = "myLink"})
JavaScript
<script type="text/javascript">
$(document).ready(function() {
$('#myLink').click(getIt);
});
function getIt() {
$.ajax({
type: "POST",
url: $(this).attr('href'),
error: function () {
//show error handler
},
success: function (r) {
$('#row1content').html(r);
}
});
return false;
}
</script>

tinymce in mvc 3 razor, Ajax.ActionLinks fail after first ajax call

I am using Tinymce inside an asp.net mvc 3 Razor application. An Ajax.ActionLink loads the tinymce editor via a call to a controller action named "GetContent". The GetContent method loads a text file from the file system. All is well. But, after I save the tinymce text via an $.ajax call, the Ajax.ActionLink no longer fires the controller method. In other words, something in the $.ajax post breaks the Ajax.ActionLink on the client so that it no longer calls the GetContent controller action.
Interestingly, the Ajax.ActionLink still loads the tinymce editor, but from the browser cache. In the example below I have 2 links "FileOne" and "FileTwo", which load two different text files. Before I call $.ajax the links load the file from disk. After I call $.ajax the links load the last "FileOne" or "FileTwo" from the browser cache.
This is the view. The $.ajax post occurs inside the tiny_mce_save_click() function, which is wired to the tinymce save button click:
#model TestTinyMCE.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#section JavaScript
{
<script src="#Url.Content("~/Scripts/tiny_mce/jquery.tinymce.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$().ready(function () {
init_tiny_mce();
});
function init_tiny_mce() {
$('textarea.tinymce').tinymce({
// Location of TinyMCE script
script_url: '/Scripts/tiny_mce/tiny_mce.js',
//javascript function called when tinymce save button is clicked.
save_onsavecallback: "tiny_mce_save_click",
encoding: "xml",
theme: "advanced",
plugins: "save",
theme_advanced_buttons1: "save",
theme_advanced_toolbar_location: "top"
});
}
function tiny_mce_save_click(tinyMceInstance) {
$.ajax({
type: 'POST',
url: '/Home/SaveContent',
data: $('form').serialize(),
success: function (data, status, xml) {
$('#results').html(data);
},
error: function (xml, status, error) {
$('#results').html(error);
}
});
return false;
}
</script>
}
#using (Html.BeginForm())
{
<ul>
#foreach (string fileName in Model.FileList)
{
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent" })</li>
}
</ul>
<div id="divContent">
#Html.Partial("GetContent", Model)
</div>
}
The partial view "GetContent" is:
#model TestTinyMCE.Models.HomeModel
#{
Layout = null;
}
<div id="divContent">
<fieldset id="fsContent">
<span id="results"></span><legend>Edit Content #Html.DisplayTextFor(m => m.FileName)</legend>
#Html.TextAreaFor(m => m.Content,
new Dictionary<string, object>{
{"class","tinymce"}, {"cols","80"}, {"rows","10"}}
)
#Html.HiddenFor(m => m.FileName)
</fieldset>
#if (#IsAjax)
{
<text>
<script type="text/javascript">init_tiny_mce();</script>
</text>
}
</div>
This is the controller. The GetContent method no longer gets called after the $.ajax post occurs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TestTinyMCE.Models;
namespace TestTinyMCE.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new HomeModel());
}
public ActionResult GetContent(HomeModel homeModel)
{
if (!string.IsNullOrEmpty(homeModel.FileName))
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
if (!System.IO.File.Exists(physicalPath))
homeModel.Content = string.Format("The file '{0}' does not exist.", physicalPath);
else
homeModel.Content = System.IO.File.ReadAllText(physicalPath);
}
return View(homeModel);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult SaveContent(HomeModel homeModel)
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
System.IO.File.WriteAllText(physicalPath, homeModel.Content);
ViewBag.Result = "The file was successfully saved.";
return View();
}
}
}
The problem is broswer caching. To prevent caching on the Ajax.ActionLink you must add AjaxOption HttpMethod = "POST". In the above code change ActionLink to
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent", HttpMethod = "POST" })</li>.
See http://forums.asp.net/t/1681358.aspx?Disable+cache+in+Ajax+ActionLink+extension+method+in+asp+net+MVC

"UpdatePanel" in Razor (mvc 3)

Is there something like UpdatePanel (in ASPX) for Razor?
I want to refresh data (e.g. table, chart, ...) automaticly every 30 seconds.
Similar to clicking the following link every 30 seconds:
#Ajax.ActionLink("Refresh", "RefreshItems", new AjaxOptions() {
UpdateTargetId = "ItemList",
HttpMethod = "Post"})
Edit:
I may should add that the action link renders a partial view.
Code in cshtml:
<div id="ItemList">
#Html.Partial("_ItemList", Model)
</div>
Code in Controller:
[HttpPost]
public ActionResult RefreshItems() {
try {
// Fill List/Model
...
// Return Partial
return PartialView("_ItemList", model);
}
catch (Exception ex) {
return RedirectToAction("Index");
}
}
It would be create if the PartielView could refresh itself.
You can try something similar to the following using Jquery (have not tested though)
<script type="text/javascript">
$(document).ready(function() {
setInterval(function()
{
// not sure what the controller name is
$.post('<%= Url.Action("Refresh", "RefreshItems") %>', function(data) {
// Update the ItemList html element
$('#ItemList').html(data);
});
}
, 30000);
});
</script>
The above code should be placed in the containing page i.e. not the partial view page. Bear in mind that the a partial view is not a complete html page.
My initial guess is that this script can be placed in the partial and modified as follows. Make sure that the ajax data type is set to html.
<script type="text/javascript">
setInterval(function()
{
// not sure what the controller name is
$.post('<%= Url.Action("Refresh", "RefreshItems") %>', function(data) {
// Update the ItemList html element
$('#ItemList').html(data);
});
}
, 30000);
</script>
Another alternative is to store the javascript in a separate js file and use the Jquery getScript function in ajax success callback.
Well, if you don't need the AJAX expierience than use the HTML tag:
<meta http-equiv=”refresh” content=”30; URL=http://www.programmingfacts.com”>
go here: http://www.programmingfacts.com/auto-refresh-page-after-few-seconds-using-javascript/
If someone wants the complete code for a selfupdating partial view have a look!
Code of the Controller:
[HttpPost]
public ActionResult RefreshSelfUpdatingPartial() {
// Setting the Models Content
// ...
return PartialView("_SelfUpdatingPartial", model);
}
Code of the Partial (_SelfUpdatingPartial.cshtml):
#model YourModelClass
<script type="text/javascript">
setInterval(function () {
$.post('#Url.Action("RefreshSelfUpdatingPartial")', function (data) {
$('#SelfUpdatingPartialDiv').html(data);
}
);
}, 20000);
</script>
// Div
<div id="SelfUpdatingPartialDiv">
// Link to Refresh per Click
<p>
#Ajax.ActionLink("Aktualisieren", "RefreshFlatschels", new AjaxOptions() {
UpdateTargetId = "FlatschelList",
HttpMethod = "Post", InsertionMode = InsertionMode.Replace
})
</p>
// Your Code
// ...
</div>
Code to integrate the Partial in the "Main"-View (ViewWithSelfupdatingPartial.cs):
#Html.Partial("_FlatschelOverview", Model)
The <meta refresh ..> tag in HTML will work for you. Its the best option
Traditional controls don't works in ASP MVC
You could do it using Jquery timers http://plugins.jquery.com/project/timers
Other option could be to use the Delay function
In your target is as simple as refresh the whole page, this SO link will be of your interest: Auto refresh in ASP.NET MVC
Hope It Helps.

Resources