Post method doesn't work with html form in MVC - html

Why is that the $.post() method doesn't work ? I press the button but it doesn't post to EditClassLessonHour.
Here is the View Code:
<div class="jumbotron">
<form id="editClassLessonHour">
#Html.HiddenFor(model => model.ID)
<span> Classes : </span>
<select name="classes" id="classes">
#foreach (var item in ViewBag.classes as List<SelectListItem>)
{
<option value="#item.Value">#item.Text</option>
}
</select>
<br /><br />
<div class="input-group">
<span class="input-group-addon" name="lessons" id="lessons">Lessons : </span>
#Html.DropDownListFor(x => x.LessonName, (IEnumerable<SelectListItem>)ViewData["lesson"], new { #class = "form-control", placeholder = "Lesson", aria_describedby = "txtClassNumber" })
#Html.ValidationMessageFor(model => model.LessonName)
</div>
<br />
<div class="input-group">
<span class="input-group-addon" name="hours" id="hours">Hour : </span>
#Html.DropDownListFor(x => x.Hour, (IEnumerable<SelectListItem>)ViewData["hour"], new { #class = "form-control", placeholder = "Lesson", aria_describedby = "txtClassNumber" })
#Html.ValidationMessageFor(model => model.Hour)
</div>
<br />
<input type="button" value="Kaydet" id="kaydet" class="btn btn-success" onclick="post(); " />
</form>
<br />
</div>
<script>
$.post('/ClassLessonHour/EditClassLessonHour',{
classId: $('#classes').val(),
lessonId: $('#lessons').val(),
hourId: $('#hours').val()
}, function (result) {
$('#message').html(result.message);
});
</script>
Here is Controller Code
[HttpPost]
public ActionResult EditClassLessonHour(int classId, int lessonId, int hourId)
{
}
What can I do? I want that when I press the button, the form should be posted to the Controller.

onclick="post();"
You don't have a function called post. You don't have any function at all. You're just executing an AJAX POST request as soon as the page loads.
Remove that onclick attribute entirely and bind your AJAX call to the button's click event. Something like this:
$(function () {
$('#kaydet').on('click', function () {
$.post('/ClassLessonHour/EditClassLessonHour',{
classId: $('#classes').val(),
lessonId: $('#lessons').val(),
hourId: $('#hours').val()
}, function (result) {
$('#message').html(result.message);
});
});
});
Though it seems likely that there are more problems here. For example, this:
$('#lessons').val()
The id="lessons" element is a <span>, which doesn't have a "value". You want to target the <select> being generated by #Html.DropDownListFor, not the <span> that's near it. Examine your HTML in the browser to see what is being generated by that server-side code. It may have an id or name that you can use in your jQuery selector. (This same problem applies to your #hours element as well.)

Related

How to create single page search form with ajax in MVC Razor

I'm a beginner in the ways of MVC / Razor... Basically I have 2 pages just for showing a search result from an API that I'm consuming with Json.
Index.cshtml: (Just a form)
#model ConsultaInterna.Models.SearchApi
<div class="jumbotron">
<h2>Consulta Interna</h2>
Placa
#using (Html.BeginForm("Search", "Home"))
{
<input asp-for="Model.Name" class="form-control" name="name" id="txtName" />
<input class="form-control" type="submit" id="ok" />
}
and Search.cshtml (Which also have the form. I want to leave just this page, right now I can't because if I load directly from Search.cshtml it gives me an error like
"Unexpected character encountered while parsing value: <. Path '', line 0, position 0. " (Obviously because I'm trying to load labels with a null search)
this is Search.cshtml
#model ConsultaInterna.Models.SearchApi
<div id="loader" class="spinner" style="display:none">OIE</div>
<div class="header formPesquisa">
#using (Ajax.BeginForm("Search", new AjaxOptions()
{
HttpMethod = "POST",
LoadingElementId = "loader",
UpdateTargetId = "resultado"
}))
{
<input placeholder="Placa" asp-for="Model.Name" name="name" id="txtName" width="30" />
<input class="pesquisa" type="submit" id="enviar" value="Ok" />
}
<div class="panel">Placa #Model.Name</div>
<div class="panel">Last Name / Model.LastName</div>
ALSO Important, the Ajax form isn't working properly... the loader won't show up.
You need to move your loader div into the Ajax.BeginForm.
You need also need to add the resultado div to display your result.
#using (Ajax.BeginForm("Search", "Home", new AjaxOptions()
{
HttpMethod = "POST",
LoadingElementId = "loader",
UpdateTargetId = "resultado"
}))
{
<div id="loader" class="spinner" style="display:none">OIE</div>
<input placeholder="Placa" asp-for="Model.Placa" class="pesquisa placa" maxlength="7" name="placa" id="txtPlaca" width="30" />
<input placeholder="CPF" asp-for="Model.CPF" class="pesquisa cpf" maxlength="11" name="cpf" id="txtCPF" />
<input class="pesquisa" type="submit" id="enviar" value="Enviar" />
<div id="resultado"></div>
}
<div class="panel">Placa #Model.Name</div>
<div class="panel">Last Name / Model.LastName</div>
</div>
You also need to make sure you have added the relevant reference to the specific unobtrusive ajax library. As an example:
BundleConfig
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive-ajax.min.js",
"~/Scripts/jquery.validate*"));
_layout.cshtml
#Scripts.Render("~/bundles/jqueryval")
Example controller action
[HttpPost]
public ActionResult Search(string placa, string cpf)
{
return new JsonResult { Data = "found", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}

How to correctly iterate elements?

I'm iterating my Model.Payments collection (which is an public IEnumerable<Payments> Payments { get; set; }):
#using (Html.BeginForm())
{
<div class="payments">
#foreach (var payment in Model.Payments)
{
Html.RenderPartial("_Payment", payment);
}
</div>
<input type="submit" value="Aggiorna" />
}
And this is my _Payment partial:
#model MyProject.Models.Payments
<div class="payment-row">
<span>ActivityID:</span> #Html.TextBoxFor(x => x.ActivityID)
<span>PaymentType:</span> #Html.TextBoxFor(x => x.PaymentType)
<span>Amount:</span> #Html.TextBoxFor(x => x.Amount)
</div>
But it doesn't create a proper HTML. Name/ID are the same, so once I postback, I can't retrieve data.
Where am I wrong?
The correct approach is to use the EditorFor() method, which will correctly prefix your inputs with the collection indexer
Rename you partial view to Payments.cshtml (to match the name of the class), and move it to the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTempatesFolder)
Then you view becomes
#using (Html.BeginForm())
{
<div class="payments">
#Html.EditorFor(m => m.Payments)
</div>
<input type="submit" value="Aggiorna" />
}
The EditorFor() method generates the correct html for each item in the collection, and will generate inputs such as <input name="Payments[0].ActivityID" ... /> rather than <input name="ActivityID" ... /> which you are currently generating.
As a side note, you should consider using #Html.LabelFor() to generate a <label> associated with your form controls, rather than using a <span>.

Binding in ASPNET MVC CORE works with #for but not to #foreach [duplicate]

In my view model, I have a list of objects. I iterate these objects, and create controls for each of them. In the situation below, I want to show people a textbox and a button for each object. When the user clicks the button, a post is made, and I can save the data in my controller.
In the UI, a user can change the form they want, and click save.
My problem is the model is null when it's posted to the controller..
My Razor code:
using (Html.BeginForm())
{
foreach (var contributor in Model.Contributor)
{
#Html.HiddenFor(model => contributor.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => contributor.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => contributor.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
}
My view model code
public class ProfileModel
{
public string Message { get; set; }
public List<PublisherModel> Publisher { get; set; }
public List<ContributorModel> Contributor { get; set; }
public ContributorModel NewContributor { get; set; }
}
My controller code
[HttpPost]
public ActionResult Mine(ProfileModel model, string newuser)
{
//
}
How to fix it?
I guess I have to expand my view model with a way to store the changes in some way. But I really can't see how.
Right now all the properties in the ProfileModel is null when it reaches the controller.
Any ideas?
Basically the problem is that default model binder is unable to bind collection items correctly in foreach loop. In other words, it names the element incorrectly and that's why the collection displays as null in parameters.
I'm sure there are all kinds of different workarounds, helpers and stuff but I'm not familiar with those, so I just use for loop instead of foreach, this way the elements are named correctly.
Try this:
#for (int i = 0; i < Model.Contributor.Count(); i++)
{
#Html.HiddenFor(model => Model.Contributor[i].Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.Contributor[i].FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.Contributor[i].FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
I suggest you to use a debugging tool to see if elements have correct name attribute, in your case they should look like Contributor[0].Id, Contributor[0].FirstName etc.
You can use PartialView for Contributor object.
PartialView:
#model Contributor
using (Html.BeginForm("ContributorUpdate", "YourController"))
{
#Html.HiddenFor(model => Model.Id)
<div class="formrow">
#Html.ValidationSummary(true)
</div>
<h2>#Html.TextRaw("AuthorInfo", "Author")</h2>
<div class="formrow">
#Html.EditorFor(model => Model.FirstName)
<div class="formvalidation">
#Html.ValidationMessageFor(model => Model.FirstName)
</div>
</div>
<div class="formrow right">
<input type="hidden" name="formsubmitted" value="true" />
<input type="submit" class="button" value="#Html.Text("ButtonText", "Save")" />
</div>
}
View will be:
#foreach (var contributor in Model.Contributor)
{
#{Html.RenderPartial("Conributor", contributor);}
}
And controller code:
[HttpPost]
public ActionResult Mine(Conributor conributor, string newuser)
{
//
}

ASP.NET MVC Redirect to same page after submit button is clicked

some friends and I have started a team project but we are stuck at this point. We made a function of the site to add comments under every post. The problem is that when submit is clicked it adds the comment but doesn't refresh the page which causes some problems. The author stays "anonymous" and when refresh button is clicked it shows an alert:
The page that you're looking for used information that you entered. Returning to that page might cause any action you took to be repeated. Do you wish to continue?
However, if we just type the URL again and click ENTER everything is good: the author shows up and the comment appears only once. So the only solution for us is to redirect to the same page. Here is the form:
<div style="text-align: left">
<div><i>Leave your comment</i>
</div>
<form method="post">
<fieldset>
<p>
<textarea rows="10" class="form-control" type="text" id="1" name="commentText" style="height: 100px"></textarea>
</p>
<p>
<input type="submit" name="buttonSubmit" onclick="redirect" value="Add comment" class="btn btn-default" style="" />
</p>
</fieldset>
</form>
</div>
This is how the comments are printed on the page:
<h3>Comments</h3>
<br>
<div>
#foreach (var comment in ViewBag.Comments) {
<section class="row">
<article class="post col-md-12">
<div class="about">
Posted on <i>#comment.Date</i>
#if (comment.AuthorId != null) { #: by <i>#comment.Author.FullName</i>
}else { #: by <i>anonymous</i>
}
</div>
<div class="body">#comment.Text</div>
</article>
</section>
}
</div>
Thanks in advance!
Use the #Ajax.BeginForm() helper.
In the AjaxOptions(), you'll want to set the Httpmethod to "Post", InsertionMode.InsertBefore (or After depending on where you want new comments), and UpdateTargetId = "your-comment-div".
You'll need to include jquery.unobtrusive-ajax.js so the Ajax helper works correctly. You can use the nuget package manager to pull it in. Be sure to add it to your jquery bundle.
Then, you need to create the post action in your controller to accept the comment params and return a partial view.
Doing thIs keeps the Ajax confined to a razor helper and controller method.
View:
#using (Ajax.BeginForm("AjaxAddComment",
"MyController",
/* route params */,
new AjaxOptions()
{
InsertionMode = InsertionMode.InsertBefore,
UpdateTargetId = "commentsDiv",
HttpMethod = "Post"
}, htmlAttributes: new { /* ... */ }))
{
<div class="form-horizontal">
#* comment form... *#
</div>
}
...
<div id="commentsDiv">
<div class="comment">...</div>
<div class="comment">...</div>
</div>
Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public async Task<PartialView> AjaxAddComment(string comment, /*...other params*/)
{
var newComment = ... // comment viewModel
// add comment to db
await db.SaveChangesAsync();
return PartialView("_CommentPartial", newComment);
}
_CommentPartial:
<div class="comment">
...
</div>

PartialViews - Not Using Shared Site.css in Editor/Display Templates - ASP.NET MVC 3

I'm using the JQuery UI Tabs functionality in my Open Source Project. I'm doing this to learn MVC3 (And various other technologies). Now I've got that all working. The problem is my Partial Views within each tab have links off the the relevant CRUD functionality. I've set these CRUD views up as Display and Editor Templates. Its these that are not picking up the _Layout.cshtml references to the Site.css.
EDIT START
I've found in the "Add View" scaffolding functionality that when you click the Create as a partial view box that the master page functionality disappears, ie greys out, BUT in Razor I thought if this is empty it uses the _viewstart file, which loads the_Layout?
EDIT END
Here is my Dashboard.cshtml code with the JQuery UI Tab logic:
<script type="text/javascript">
$(document).ready(function() {
$("#tabs").tabs();
getContentTab (1);
});
function getContentTab(index) {
var url='#Url.Content("~/SiteAdmin/AjaxGetTab")/' + index;
var targetDiv = "#tabs-" + index;
var ajaxLoading = "<img id='ajax-loader' src='#Url.Content("~/Content")/ajax- loader.gif' align='left' height='28' width='28'>";
$(targetDiv).html("<p>" + ajaxLoading + " Loading...</p>");
$.ajax({
type: 'get',
url: url,
cache: false,
success: function(result) {
$(targetDiv).html(result);
}
});
}
<div id="tabs">
<ul>
<li>Transaction Type </li>
<li>Direction Type</li>
<li>User Type</li>
<li>Currency Type</li>
</ul>
<div id="tabs-1">
</div>
<div id="tabs-2">
</div>
<div id="tabs-3">
</div>
<div id="tabs-4">
</div>
</div>
Here is my AjaxGetTab Action Method if you need to know how i decide to create tabs and create the list objects:
/// <summary>
/// AJAX action method to obtain the correct Tab to use.
/// </summary>
/// <param name="index">Tab number</param>
/// <returns>Partial View</returns>
public ActionResult AjaxGetTab(int id)
{
string partialViewName = string.Empty;
object model = null;
//--Decide which view and model to pass back.
switch (id)
{
case 1:
partialViewName = "_TransactionType";
model = db.TransactionTypes.ToList();
break;
case 2:
partialViewName = "_DirectionType";
model = db.DirectionTypes.ToList();
break;
case 3:
partialViewName = "_UserType";
model = db.UserTypes.ToList();
break;
case 4:
partialViewName = "_CurrencyType";
model = db.CurrencyTypes.ToList();
break;
case 5:
partialViewName = "_tabError";
break;
}
return PartialView(partialViewName,model);
}
At the moment I'm working on TransactionType so here is the _TransctionType.cshtml code for the PartialView:
#model IEnumerable<Accounts.Models.TransactionType>
<p>
#Html.ActionLink("Create New", "CreateTransactionType")
</p>
<table>
<tr>
<th>
Record Status
</th>
<th>
Description
</th>
<th>
Created Date
</th>
<th>
Amended Date
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.RecordStatus)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreatedDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.AmendedDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.id }) |
#Html.ActionLink("Details", "Details", new { id=item.id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.id })
</td>
</tr>
}
Now the "Edit" & Delete ActionLink has an EditorTemplate and the Details has a DisplayTemplate folder with the required TransactionType.cshtml Its these views which the _Layout Site.css isnt being applied to. Here is example code from the "Edit" code base:
_EditTransactionType.cshtml:
#model Accounts.Models.TransactionType
#using (Html.BeginForm())
{
#Html.EditorForModel()
<p>
<input type="submit" value="Save" />
</p>
}
And here is the TransactionType.cshtml which sits in /Views/SiteAdmin/EditorTemplate:
#model Accounts.Models.TransactionType
<fieldset>
<legend>Transaction Type</legend>
<div class="editor-label">
#Html.LabelFor(model => model.RecordStatus)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RecordStatus)
#Html.ValidationMessageFor(model => model.RecordStatus)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
</fieldset>
Now I could just put a reference to the Site.css in each Template, but is there a cleaner way of doing this? Am I missing something?
_ViewStart is only applied to Views that are rendered, this is determined on how you render the view. E.G using RenderPartial or returning a PartialView from a controller returns only the contents (and nest partials) of the PartialView that you are targetting.
If the _LayoutFile applied to every view and every partial view then you would end up with pages like so:
<html>
<head />
<body>
<html>
<head />
<body>
<!-- Actual Partial View Content -->
</body>
</html>
</body>
</html>
When a page is rendered all of the the _layout, the view to be rendered, any partial views and any nest partials or editor/display templates are built into a single page and returned to the client so any style sheets that are referenced by the _Layout master will be applied to this now flattened heirarchy of (Partial)Views.
Have you inspected the output HTML to make sure that is as expected? It may not be a problem with the views.
I really don't see why you complicated using jqueryui tabs so much adding onclick then a switch etc.
<div id="tabs">
<ul>
<li><span>Transaction Type</span></li>
<li><span>Direction Type</span></li>
<li><span>User Type</span></li>
<li><span>Currency Type</span></li>
</ul>
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#tabs').tabs({
spinner: '<img src="../../Content/Images/tabsppinner.gif" alt="" /> #Loading...'
});
});
</script>
Then you would just have one controller with actions defined for every tab.