In the index.cshtml I display the todo items with partial view:
<table class="table table-striped">
#foreach (var todo in Model.Todos)
{
<tr id="#todo.TodoId">
#{Html.RenderPartial("_TodoModel", todo);}
</tr>
}
The partial view:
<td class="col-md-1">#Html.CheckBoxFor(m => m.Complete, new { #class = "bigCheckbox", id = "completedFlag"+Model.TodoId })</td>
<td class="col-md-9">
<span class="#(Model.Complete ? "completed" : "notCompleted")">
#Model.TodoText
</span>
</td>
<td class="col-md-2 text-center">
#Html.ActionLink("Remove Todo", "Remove", "Todo", new { todoId = Model.TodoId }, new { #class = "btn btn-sm btn-danger" })
</td>
When the checkbox is checked the javascript is calling the controller and updating the completed flag, then redirecting to Index page again. The reload happens, the new css class applied to the element, but the style remains the old one.
Even on soft refresh the new style is applied.
I can change the style with javascript, just curious about why is above behaviour?
Also I feel a bit cleaner if the model changes the style should also change.
Related
I have a table with some column in a asp.net mvc core project.
My view file looks like this
<h1>Items</h1>
<div class="panel-body">
<table class="table table-bordered table-responsive table-hover">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Rating</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Items)
{
<tr onclick="location.href='#(Url.Action("ShowItemDetails", "Item", new {Id = item.FilmId}))'">
<td>#item.Id</td>
<td>#item.Title</td>
<td>#item.Rating</td>
<td>#Html.ActionLink("Edit", "Edit", "Show", new { id = item.Id }) | #Html.ActionLink("Rate this", "RateItem", new { id = item.Id }) </td>
</tr>
}
</tbody>
</table>
</div>
The problem is that when i click on a row, the controller method ShowItemDetails is called twice(!).
I can not see from the code above why this happens. Also, clicking on Edit or Rate this calls first ShowItemDetails and then immediately Edit or RateItem method in controller. Any suggestion how this can be solved?
Clicking on Edit or Rate this calls first ShowItemDetails and then immediately Edit or RateItem method because Edit is under a table row and on tablerow, you have called showitemdetails action.so, when you click on td , it gets first executed row action then td action.that's why it get called twice.
I hope, you want to show details and edit options with data of table and Edit is controller name.
Tweak your table code like below:
<tbody>
#foreach (var item in Model.Items)
{
<tr>
<td>#Html.ActionLink("Show Details","ShowItemDetails","Item",new {Id = item.FilmId})</td>
<td>#item.Id</td>
<td>#item.Title</td>
<td>#item.Rating</td>
<td>#Html.ActionLink("Edit", "Edit", "Show", new { id = item.Id }) | #Html.ActionLink("Rate this", "RateItem", new { id = item.Id }) </td>
</tr>
}
</tbody>
The problem seems to be caused be something i thought was irrelevant
having a null image cause the method to be called twice. Setting it to
solves this.
Need to add code to check for Model.Image != null
Very strange!!
I have a dropdownlist with different soccer Clubs from my database. When I select one, an ajax call will be made to show all the Games that Club will play in.
The problem is whenever I'm logged on as an AspNetUser I automatically get logged off the second I submit the dropdownlist.
When I'm not logged in everything works as it should.
I'm really confused why exactly this happens.
Controller
[Authorize]
public class WedstrijdController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
ploegService = new PloegService();
ViewBag.PloegId = new SelectList(ploegService.All(),
"Id", "Naam");
return View();
}
[AllowAnonymous]
public ActionResult IndexAjax(int? ploegId)
{
if (ploegId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
wedstrijdService = new WedstrijdService();
var wedstrijden = wedstrijdService.WedstrijdenByPloeg(Convert.ToInt16(ploegId));
return PartialView("_SearchWedstrijdenPartial", wedstrijden);
}
}
Index.cshtml
#{
ViewBag.Title = "Lijst";
}
<h2>Wedstrijden</h2>
<div class="panel panel-default">
<div class="panel-heading">Kies een wedstrijd</div>
<div class="panel-body">
#using (Ajax.BeginForm("IndexAjax", "Wedstrijd",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
LoadingElementId = "ajax-loader",
UpdateTargetId = "searchresults",
}))
{
<div class="form-horizontal">
<div class="form-group">
#Html.Label("Ploeg", htmlAttributes: new { #class = "control-label col-md-1" })
<div class="col-md-10">
#Html.DropDownList("PloegId", null, "---Kies ploeg---", htmlAttributes: new { #class = "form-control" })
<img id="ajax-loader"
src="#Url.Content("~/content/Images/ajax-loader.gif")"
style="display: none" />
</div>
</div>
</div>
}
<div id="searchresults"></div>
</div>
</div>
#section scripts
{#Scripts.Render("~/bundles/jqueryUnobtrusive")
<script>
$("#PloegId").on("change",
function() {
$("form").trigger("submit");
});
</script>
}
_SearchWedstrijdenPartial.cshtml
#model IEnumerable<VoetbalMVC.Models.Wedstrijd>
<table class="table">
<tr>
<th>
Thuisploeg
</th>
<th>
Bezoekersploeg
</th>
<th>
#Html.DisplayNameFor(model => model.Stadion)
</th>
<th>
#Html.DisplayNameFor(model => model.Datum)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Ploeg1.Naam)
</td>
<td>
#Html.DisplayFor(modelItem => item.Ploeg.Naam)
</td>
<td>
#Html.DisplayFor(modelItem => item.Stadion.Naam)
</td>
<td>
#Html.DisplayFor(modelItem => item.Datum)
</td>
<td>
#Html.ActionLink("Order tickets", "Detail", new { wedstrijdId = item.Id })
</td>
</tr>
}
</table>
Indeed, by using the $("form") jQuery selector you are targeting all form elements on the page. You can verify the forms being submitted from the client to the server with tools like Fiddler, Postman, or the developer tools of your browser (by hitting F12 or CTRL+Shift+I on Windows/Linux, or Command+Option+I on Mac).
As far as the logout form is concerned, if you haven't customized the folder/file structure of your MVC project, it should be located in Views/Shared/_LoginPartial.cshtml:
#using Microsoft.AspNet.Identity
#if (Request.IsAuthenticated)
{
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
// ...
}
}
..so it is generating a form with an ID of #logoutForm, but since you are submitting all forms at once, you are subsequently logging out the user each time.
This means you need to modify the call to only submit the form you want. Assuming the ID of your form being AjaxForm, you would do:
$("#AjaxForm").trigger("submit");
This piece of code was causing the error
$("form").trigger("submit");
As you can see this includes all forms. I suspect this means the logout button is included in some sort of form which triggers this action when logged in. Simply add an Id to your AjaxForm and only trigger the submit of that AjaxForm.
I am using ExcelReader to upload new sheet of containing data and get all old data from the DbSet.I am using Entity Framework POCO way to POST/GET data. I want to use same view for both POST/GET.I have two submit button one for saving current data and one for get all data from datacontext.Now my View Code is below I followed the steps included here!
I getting following error "System.Reflection.AmbiguousMatchException". I am guessing this because of HTNL.Beginfrom() creating it because of action result name.
Can any one help me how to resolve this issue if I need to use #Html.AntiForgeryToken();
#Html.ValidationSummary(); in my both the GET/POST call.
[MultipleButton(Name = "action", Argument = "Index")]
public ActionResult Index(HttpPostedFileBase uploadfile,dataViewModel dataView1)
{
// code for upload
}
[MultipleButton(Name = "action", Argument = "ExcelData")]
public ActionResult ExcelData()
{
ReadContext Context = new ReadContext();
return View(Context.dataext.ToList());
}
My View code:
#{
ViewBag.Title = "Read Excel File and Display in mvc5";
}
#model System.Data.DataTable
#using System.Data
<h2>Read Excel File and Display in mvc5</h2>
#using (Html.BeginForm("Index", "ReadExcel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken();
#Html.ValidationSummary();
<label class="text-info">Upload Excel File</label>
<input type="file" class="form-control" name="uploadfile" id="uploadfile" />
<input type="submit" value="submit" class="btn btn-default" name="action:Index" />
<input type="submit" value="data" class="btn btn-default" name="action:ExcelData" />
if (Model != null)
{
<table class="table table-responsive table-bordered">
<thead>
<tr>
#foreach(DataColumn column in Model.Columns)
{
<th>#column.ColumnName</th>
}
</tr>
</thead>
<tbody>
#foreach(DataRow row in Model.Rows)
{
<tr>
#foreach(DataColumn col in Model.Columns)
{
<td>#row[col.ColumnName]</td>
}
</tr>
}
</tbody>
</table>
}
}
I have created a form that has multiple sections that are hidden. A tab strip hides/shows the sections to create a page with a smaller footprint. While this makes the page a lot cleaner, it makes it hard to show errors to the user after validation. I want to make an indicator in the tabs that shows that the content in the specified tab has errors.
Main View:
<div>
<ul class="contentTabs">
<li onclick="switchTab(this)" class="selected">Contact</li>
<li onclick="switchTab(this)">Information</li>
<li onclick="switchTab(this)">Software</li>
<li onclick="switchTab(this)">Hardware</li>
<li onclick="switchTab(this)">Classification</li>
<li onclick="switchTab(this)" class="last">Solution</li>
</ul>
<div class="content">
<div id="contact" class="contentPane">
#Html.Partial("_Contact")
</div>
<div id="information" class="contentPane" style="display: none;">
#Html.Partial("_Information")
#Html.Partial("_Notes")
</div>
<div id="notes" class="contentPane" style="display: none;">
#Html.Partial("_Notes")
</div>
<div id="software" class="contentPane" style="display: none;">
#Html.Partial("_Software")
</div>
<div id="hardware" class="contentPane" style="display: none;">
#Html.Partial("_Hardware")
</div>
<div id="classification" class="contentPane" style="display: none;">
#Html.Partial("_Classification")
</div>
<div id="solution" class="contentPane" style="display: none;">
#Html.Partial("_Solution")
</div>
</div>
</div>
Partial View (Contact):
#code
Dim notifyTypes As ListItemCollection = DirectCast(ViewData("NotifyTypes"), ListItemCollection)
Dim callerTypes As ListItemCollection = DirectCast(ViewData("CallerTypes"), ListItemCollection)
Dim reportingTypes As ListItemCollection = DirectCast(ViewData("ReportingTypes"), ListItemCollection)
Dim myIncident As Library.BusinessLayer.Incident = DirectCast(Model, Library.BusinessLayer.Incident)
End Code
<table class="tableBorderless" style="width: 99%; margin: 0px auto">
<tr>
<td class="right">User Location</td>
<td class="left">
#Html.DropDownList("LocationId", DirectCast(ViewData("Locations"), SelectList), New With {.style = "width: 200px"})<br />
#Html.ValidationMessage("LocationId", New With {.class = "red"})
</td>
<td class="right">Notify</td>
<td class="left">
#For Each notificationType As ListItem In notifyTypes
#<input type="radio" name="Notify" value="#notificationType.Value" #IIf(notificationType.Selected, "checked", "") />#notificationType.Text
Next
</td>
</tr>
<tr>
<td class="right">Caller Type</td>
<td colspan="3" class="left">
#For Each callerType As ListItem In callerTypes
#<input type="radio" name="CallerType" value="#callerType.Value" #IIf(callerType.Selected, "checked", "") />#callerType.Text
Next
</td>
</tr>
<tr>
<td class="right">User Network ID</td>
<td class="left">
#Html.TextBox("UserId", myIncident.UserId, New With {.onchange = "UserId_onchange(this)", .maxlength = "30"})
</td>
<td class="right">User Name</td>
<td class="left">
#Html.TextBox("UserName", myIncident.UserName, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("UserName", New With{.class = "red"})
</td>
</tr>
<tr>
<td class="right">User Email</td>
<td class="left">
#Html.TextBox("UserEmail", myIncident.UserEmail, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("UserEmail", New With{.class = "red"})
</td>
<td class="right">User Phone</td>
<td class="left">
#Html.TextBox("UserPhone", myIncident.UserPhone, New With {.maxlength = "50"})
</td>
</tr>
<tr>
<td class="right">Reporting Type</td>
<td colspan="3" class="left">
#For Each reportingType As ListItem In ReportingTypes
#<input type="radio" name="ReportedByType" value="#reportingType.Value" #IIf(reportingType.Selected, "checked", "") />#reportingType.Text
Next
</td>
</tr>
<tr>
<td class="right">Reported by (Network ID)</td>
<td class="left">
#Html.TextBox("ReportedByUserId", myIncident.ReportedByUserId, New With {.onchange = "ReportedByUserId_onchange(this)", .maxlength = "30"})
</td>
<td class="right">Reported by Name</td>
<td class="left">
#Html.TextBox("ReportedByName", myIncident.ReportedByName, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("ReportedByName", New With {.class = "red"})
</td>
</tr>
<tr>
<td class="right">Reported by Email</td>
<td class="left">
#Html.TextBox("ReportedByEmail", myIncident.ReportedByEmail, New With {.maxlength = "50"})<br />
#Html.ValidationMessage("ReportedByEmail", New With {.class = "red"})
</td>
<td class="right">Reported by Phone</td>
<td class="left">
#Html.TextBox("ReportedByPhone", myIncident.ReportedByPhone, New With {.maxlength = "50"})
</td>
</tr>
</table>
<script type="text/javascript">
function UserId_onchange(textField) {
var parms = {UserName: textField.value};
$.ajax({
url: '#Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
type: 'POST',
dataType: 'json',
data: parms,
success: function (data) {
$("#UserName").val(data.Name);
$("#UserEmail").val(data.Email);
$("#UserPhone").val(data.PhoneWork);
}
});
}
function ReportedByUserId_onchange(textField) {
var parms = { UserName: textField.value };
$.ajax({
url: '#Url.RouteUrl(New With{.Controller = "Users", .Action = "Get"})',
type: 'POST',
dataType: 'json',
data: parms,
success: function (data) {
$("#ReportedByName").val(data.Name);
$("#ReportedByEmail").val(data.Email);
$("#ReportedByPhone").val(data.PhoneWork);
}
});
}
</script>
You could check whether appropriate tab's div has any "input-validation-error" class applied (taken you use standard DataAnnotations). Combine this into jQuery function which would run through all needed divs (probably all divs specified in your li elements) and if length of elements with "input-validation-error" class is more than 0, as #rivarolle suggested apply "error" class to li element to highlight it in your preferred way.
This would be a possible script:
$( "li" ).each(function( index ) {
var searchPattern = ("#"+$(this).text()+" .input-validation-error");
if ($(searchPattern.toLowerCase()).length > 0){
$(this).addClass("error");
}
});
css:
.error {
background-color: red;
}
Give your li elements IDs
<li onclick="switchTab(this)" id="softwareTab">Software</li>
Then pass the collection of validation objects, or, better a list of affected tab names in your ViewModel, and store the list in one or more hidden fields. Then use jQuery to parse the list and add the error class as suggested by rivarolle...
$("#softwareTab").addClass("error")
You may have to clean up later with removeClass().
There are many ways to do this, all a bit kludgy, but sometimes that is the price of a good looking page...one hidden field with a comma seperated list, one hidden field per tab with a boolean value...or pseudo-boolean.
I think, The page should be divided into partial views. Each partial view needs to be validated before proceeding to next step. For that we can write a helper Method. When user fills the data and post the section, then controller checks and fills your custom validation error collection and it can be passed on as model metadata i.e. buddy class in your model. This way , you will render the errors. i.e. we are using model-metadata to send validation errors.
If you don't want to use model approach then you need to use ViewBag collection which is dynamic collection.
Hope this helps.
What you will probably need to do is use the visibility of the various validation messages.
The way I'd approach this is by adding a custom class to the validation messages for use within jquery:
#Html.ValidationMessage("UserName", New With{.class = "red validationMesssage"})
Then in the switchTab function do something like this:
function switchTab(el)
{
var tabId=$(el).text(); //Get the tab to be searched
var isValid=true; //Set default as valid
$("#"+tabId).find(".validationMessage:visible").each(function(){
isValid=false; //this should only fire if the validation message is visible
});
if(!isValid)
$(el).addClass("errors"); //If invalid..add error class to li element.
}
You can try #Html.ValidationSummary(false) at the top MAIN view. Its better from usability perspective as well.
You could change the color of the tab headers that contain errors to red for instance.
To do this, I would switch the css of the tags.
To take the Information tab:
No error:
--> <li onclick="switchTab(this)">Information</li>
Error:
--> <li onclick="switchTab(this)" class="error">Information</li>
The CSS class "error" will change the color to red or append an image to indicate validation failure.
I have a View with a foreach loop for a list property of the model. Now, I want to be able to let the user set the value of each of the items in the list using a dropdownlist. But I'm not sure how to do that. I've used something like this when it is not in a foreach loop:
#Html.DropDownListFor(model => model.Level, new SelectList(new[] { 1, 2, 3, 4, 5 }, Model.Level))
But how do I do this when I need to reference item.Level in the loop instead? Here's my View code:
<div id="formDiv">
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Ny arbetserfarenhet</legend>
<table>
<tr>
#*<th></th>*#
<th>
Program
</th>
<th>
Nivå
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#item.Program.Name
</td>
<td>
#item.Level
</td>
</tr>
}
</table>
</fieldset>
}
</div>
I have a View with a foreach loop for a list property of the mode
I would recommend you to avoid writing loops in your views in favor of editor templates. So:
#model IEnumerable<AppName.Models.ModelName>
<div id="formDiv">
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Ny arbetserfarenhet</legend>
<table>
<tr>
<th>
Program
</th>
<th>
Nivå
</th>
</tr>
#Html.EditorForModel()
</table>
</fieldset>
}
</div>
and in the corresponding editor template (~/Views/Shared/EditorTemplate/ModelName.cshtml):
#model AppName.Models.ModelName
<tr>
<td>#Model.Program.Name</td>
<td>
#Html.DropDownListFor(
model => model.Level,
new SelectList(
Enumerable.Range(1, 5).Select(x => new { Value = x, Text = x }),
"Value",
"Text"
)
)
</td>
</tr>
So the editor template will be rendered for each element in your model (which is a collection of some type). The important part is that the editor template must be located in ~/Views/Shared/EditorTemplates and named XXX.cshtml where XXX is the type name used in your main view model collection.
Have you tried:
#Html.DropDownListFor(m => item.Level, new SelectList(new[] { 1, 2, 3, 4, 5 }, item.Level))
use this syntax:
#Html.DropDownListFor(model => model.Level, new SelectList(Model.level as System.Collections.IEnumerable, "VALUE_FIELD", "TEXT_FIELD", YOUR PROPERTY NAME)
MVC will create the loop. Just use an editor template, partial view in special folder, and the rest works like magic.
Editor Template
#model Models.AdditionalAccountingLine
#Html.HiddenFor(m => m.IsRequired)
#Html.DropDownListFor(m => m.FieldValue, new SelectList(#Model.FieldValueOptions, "Key", "Value"), "")
View
#Html.EditorFor(m => m.AdditionalAccountingLines);