Strongly Typed Model with MvcMailer - asp.net

i am facing troubles passing strongly typed model to MvcMailer view. I am using asp.net mvc3 and installed MvcMailer3 using nuget.
There is no error messages and message is sent successfully but the data fields are not populated. I am doing everything also tried using ViewBag fields but the same problem- that is message sent successfully but fields are not populated...
please help...i am stuck for two days!
here is my controller and view code....
//Controller code
public ActionResult Index()
{
string invoicenumber = "BC-00000002";
IEnumerable<Quantum.Models.usp_MasterPrintInvoice_Result> mpi = db.usp_MasterPrintInvoice(invoicenumber);
IEnumerable<PrintDetailObject> printdetailobj = from anmpi in mpi select new PrintDetailObject { Head = anmpi.Head, ContributorName = anmpi.Name, RegistrationNumber = anmpi.RegistrationNumber, InvoiceNumber = anmpi.InvoiceNumber, InvoiceDate = anmpi.Date, Amount = anmpi.Amount, PaymentMonth = anmpi.Month, ReceivedBy = anmpi.Operator };
ViewData.Model = printdetailobj.FirstOrDefault();
IUserMailer mailer = new UserMailer();
mailer.Welcome().Send();
return View();
}
View Code
#model Quantum.Models.PrintDetailObject
#using Mvc.Mailer
<h2>Invoice</h2>
<fieldset>
<div class="print-details-page">
<legend>#Html.DisplayFor(model => model.Head)</legend>
<div class="display-label">InvoiceNumber</div>
<div class="display-field">
#Html.DisplayFor(model => model.InvoiceNumber)
</div>
<div class="clear"></div>
<div class="display-label">Date</div>
<div class="display-field">
#Html.DisplayFor(model => model.InvoiceDate)
</div>
<div class="clear"></div>
<div class="display-label">Recieved From:</div>
<div class="display-field">
#Html.DisplayFor(model => model.ContributorName)
</div>
<div class="clear"></div>
<div class="display-label">Registration Number:</div>
<div class="display-field">
#Html.DisplayFor(model => model.RegistrationNumber)
</div>
<div class="clear"></div>
<div class="display-label">Amount:</div>
<div class="display-field">
#Html.DisplayFor(model => model.Amount)
</div>
<div class="clear"></div>
<div class="display-label">Amount in Text:</div>
<div class="display-field">
#Html.DisplayFor(model => model.AmountText)
</div>
<div class="clear"></div>
<div class="display-label">Month:</div>
<div class="display-field">
#Html.DisplayFor(model => model.PaymentMonth)
</div>
<div class="clear"></div>
<div class="display-label">Recieved By:</div>
<div class="display-field">
#Html.DisplayFor(model => model.ReceivedBy)
</div>
<div class="clear"></div>
<br />
<p>Received with many thanks.</p>
</div>
</fieldset>
I am only get the following text as the email body:
Invoice
InvoiceNumber
Date
Recieved From:
Registration Number:
Amount:
Amount in Text:
Month:
Recieved By:
Received with many thanks.

hi i have just got the insight to answer my own question.
actually when i used ViewData.Model inside of the "SendEmail" Controller the model is available to its own view namely the "Index" view instead of the razor view used with the "UserMailer" Controller which is named "Welcome" in my case.
To pass the Model to "UserMailer" action we need to do the following modification:
//IUserMailer code
...
public interface IUserMailer
{
MvcMailMessage Welcome(PrintDetailObject myModel);
}
//UserMailer code
public virtual MvcMailMessage Welcome(PrintDetailObject myModel)
{
//ViewBag.Data = someObject;
ViewData.Model = myModel;
return Populate(x =>
{
x.Subject = "Welcome";
x.ViewName = "Welcome";
x.To.Add("cmmwahid#hotmail.com");
});
}
//and finally controller code
public ActionResult Index()
{
string invoicenumber = "BC-00000002";
IEnumerable<Quantum.Models.usp_MasterPrintInvoice_Result> mpi = db.usp_MasterPrintInvoice(invoicenumber);
IEnumerable<PrintDetailObject> printdetailobj = from anmpi in mpi select new PrintDetailObject { Head = anmpi.Head, ContributorName = anmpi.Name, RegistrationNumber = anmpi.RegistrationNumber, InvoiceNumber = anmpi.InvoiceNumber, InvoiceDate = anmpi.Date, Amount = anmpi.Amount, PaymentMonth = anmpi.Month, ReceivedBy = anmpi.Operator };
IUserMailer mailer = new UserMailer();
mailer.Welcome(printdetailobj.FirstOrDefault()).Send();
return View();
}
here is the final output as a email body:
Invoice
Generous Contribution
InvoiceNumber
BC-00000002
Date
15/02/2014
Recieved From:
Some Donor
Registration Number:
M104/6
Amount:
$23.00
Amount in Text:
Twenty Three Dollars Only
Month:
May/2014
Recieved By:
someuser
Received with many thanks.

Related

Illegal characters in path in Html.RenderAction

I am trying to call child action from view as below
#{
Html.RenderAction("Render", "ProgressBar", new { total = 10, completed = 3 });
};
and my controller code is as below
public class ProgressBarController : Controller
{
// GET: ProgressBar
[ChildActionOnly]
public ActionResult Render(int total, int completed)
{
ViewBag.Total = total;
ViewBag.Completed = completed;
ViewBag.Percent = ((completed * 100) / total).ToString("0.#####");
return PartialView();
}
}
and partial view for Render
<div class="section-progress-wrapper">
<div class="section-progress-info clearfix">
<div class="section-progress-label float-l">
<span class="section-progress-output">#ViewBag.Completed</span>
<span class="section-progress-desc">of</span>
<span class="section-progress-total">#ViewBag.Total</span> modules completed
</div>
<div class="section-progress-icon float-r"></div>
</div>
<div class="section-progress-box">
<div id="progressBar" class="section-progress-range" style="width:#string.Format("{0}%",ViewBag.Percent)"></div>
</div>
</div>
but for some reason I am getting exception saying
System.ArgumentException: Illegal characters in path.
I am not able to figure out what I am doing wrong. Can someone please help?

ASP.NET Core 2.1 File Upload Form doesn't reach controller

This is my controller
[ValidateAntiForgeryToken]
[HttpPost("Save")]
public async Task<ActionResult> SaveAsync(UploadDocumentViewModel Input)
{
var filePath = $"{this.hostingEnvironment.WebRootPath}/documents";
foreach(var item in Input.Files)
{
var fileName = ContentDispositionHeaderValue.Parse(item.ContentDisposition).FileName;
var fullFilePath = Path.Combine(filePath, fileName);
using(var stream = new FileStream(fullFilePath, FileMode.Create))
{
await item.CopyToAsync(stream);
}
}
return this.Ok();
}
This is the form that I am uploading from
#using (Html.BeginForm("Save", #ViewContext.RouteData.Values["controller"].ToString(), FormMethod.Post, new { enctype = "multipart/form-data",id = "form" }))
{
#Html.AntiForgeryToken()
<div class="col-sm-12">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
#Html.LabelFor(m => m.Categories)
#Html.DropDownListFor(m => m.Categories, (SelectList)Model.Categories, "", new { #class = "form-control col-sm-12" })
#Html.ValidationMessageFor(m => m.Categories)
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<input id="Files" type="file" name="Files" multiple />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<div class="clearfix">
<div class="pull-right">
<input type="submit" id="submit" value="Save" class="btn btn-primary" />
#Html.ActionLink("Cancel", "Index", #ViewContext.RouteData.Values["controller"].ToString(), new { }, new { #class = "btn btn-outline-secondary" })
</div>
</div>
</div>
</div>
</div>
</div>
}
When I am trying to upload my file, I get transferred to a 404 page. However, If I remove the async Task items from my controller, I can reach the controller just fine.
I am trying to follow this Microsoft tutorial but I can't seem to get it to work:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1
Your form is attempting to generate a route for a "Save" action, but you action name is actually SaveAsync. It worked when you removed the async stuff, because you likely changed the method name to Save as well at that point.
It's not traditional to name actions with the Async suffix, even if they are async (unlike other types of methods). As a result, I'd recommend simply removing the Async part of the method name, and you'll be fine. Otherwise, you need to change the reference in your form from "Save" to "SaveAsync" to match the name of the action.
FWIW, you're killing yourself using the HtmlHelper methods here. Use the tag helpers instead. For example, your from could be as simple as:
<form method="post" asp-action="Save" enctype="multipart/form-data">
...
</form>

ASP.NET MVC 5 Form Validation and Error Handling

Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)

How to use partial views from different models in ASP.NET MVC 5?

Hi I am trying to code a simple blog with using ASP.NET MVC 5 Framework. I have done CRUD operations of Posts. I mean I can add new articles and manage them. When I wanted to add comments to articles, I stuck. Comments will be added to Details pages of articles. So I should add Create comment page to Details page.
I used Code First model and I have two models. Articles and Comments. I decided to use partial views to enter comments. But result is an error:
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Article but this dictionary requires a model item of type 'Blog.Models.Comment'. Comments can not be added. I created 2 PartialViews, one of them is _CreateComments PartialView and other one is _Index PartialView
Details View:
#model Blog.Models.Article
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Article</h4>
<hr />
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.Title)
</dt>
<dd>
#Html.DisplayFor(model => model.Title)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Author)
</dt>
<dd>
#Html.DisplayFor(model => model.Author)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Date)
</dt>
<dd>
#Html.DisplayFor(model => model.Date)
</dd>
<dt>
#Html.DisplayNameFor(model => model.ArticleContent)
</dt>
<dd>
#Html.DisplayFor(model => model.ArticleContent)
</dd>
</dl>
</div>
<div class="jumbotron">
#Html.Partial("~/Views/Comments/_CreateComments.cshtml", new Blog.Models.Comment())
#Html.Partial("~/Views/Comments/_Index.cshtml", new List<Blog.Models.Comment> { new Blog.Models.Comment() })
</div>
_CreateComment PartialView
#model Blog.Models.Comment
#using (Html.BeginForm("Create"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Comment</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CommentContent, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CommentContent, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CommentContent, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
_Index PartialView
#model IEnumerable
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Date)
</th>
<th>
#Html.DisplayNameFor(model => model.CommentContent)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.CommentContent)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CommentId }) |
#Html.ActionLink("Details", "Details", new { id=item.CommentId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CommentId })
</td>
</tr>
}
</table>
You have to use a ViewModel for that purpose. In POST, it will get the needed information. And in GET you will send the needed information.
For example: in GET you will send the article and needed areas. But in POST you will get the comment and commenter's name and date etc.
There are numerous issues with your code.
#using (Html.BeginForm()) means it will post back to the
Details() method (assuming that's the action which generated the
view), so it would need to be #using (Html.BeginForm("Create"))
The controls your generating will have name attributes such as
name="Item2.Date" which have no relationship to your Comment
class (Comment does not have a property named Item which is a
complex object with a property named Date)
The default value of a DateTime property is 1.1.0001 00:00:00
meaning that you have not initialized the value (e.g. Date =
DateTime.Today;) but its not clear why you would need the user to
enter a date anyway - surely that would be set to today's date in
the controller immediately before you save the comment.
You have not indicated what scripts
#Scripts.Render("~/bundles/jqueryval") is generating, but assuming
you using the jQueryUI datepicker, you will need at least
jquery-{version}.js and jquery-ui-{version}.js (plus jquery.validate.js and jquery.validate.unobtrusive.js for validation)
You have a table in your form which will not display anything other
that the initial values of a new Comment (and exactly the same as
is being displayed in the textboxes) so it seems a bit pointless
Note also your view does not display any existing comments for an article which seems unusual.
There a numerous ways so solve this including ajax so the user could stay on the same page and continue to add more comments, but based on what appears to be your current UI, then the model in Details view should be just Article and use a partial to render a form for a new Comment
Controller
public ActionResult Details(int? id)
{
....
Articles article = db.Articles.Find(id);
....
return View(article);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Comment model)
{
if (ModelState.IsValid)
{
model.Date = DateTime.Today; // set date before saving
db.Comments.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
....
}
View
#model yourAssembly.Article
// Render properties of Article
....
// Add form for creating a new comment
#Html.Partial("_Comment", new yourAssembly.Comment)
// Add required scripts including jquery ui
and the partial view (_Comment.cshtml)
#model yourAssembly.Comment
#using (Html.BeginForm("Create"))
{
#Html.LabelFor(m => m.Content, new { #class = "control-label col-md-2" })
#Html.TextBoxFor(m=> .m.Content, new { #class = "form-control" } })
#Html.ValidationMessageFor(m=> m.Content, new { #class = "text-danger" })
<input type="submit" value="Create" class="btn btn-default" />
}
Side notes:
Do not use Tupple in your views, especially for a view that
involves editing since it generates name attributes which have no
relationship to your model. Always use view models.
You do not generate a control for the CommentId property so there
is no point including it in you [Bind] attribute (and in fact
means someone could post back a value and result in your code
throwing an exception.
Using <dl>, <dt> and <dd> tags are not appropriate in your
view (they are for A description list, with terms and
descriptions). Use <div> and <span> elements.

Hide / View Edit view form

I'm working on a small ASP.NET MVC 4 application in combination with MongoDB. Currently I have 4 views: Index, Create, List, Edit. Create is a form to put data in the database. List is a list to display the data. Edit is a form to edit the data. These three views are rendered inside the Index view (RenderAction).
The goal is to display only two views inside the index view. A combination of Index with Create, or a combination of Index with Edit.
At this moment I'm having problems with the Edit View (inside the controller):
[HttpGet]
public ActionResult Edit(string id)
{
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}
Edit view:
#model MvcApplication1.ViewModels.UpdateCarViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>UpdateCarViewModel</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Make)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Make)
#Html.ValidationMessageFor(model => model.Make)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NumberOfDoors)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NumberOfDoors)
#Html.ValidationMessageFor(model => model.NumberOfDoors)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DailyRentalFee)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DailyRentalFee)
#Html.ValidationMessageFor(model => model.DailyRentalFee)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Index View:
#model MvcApplication1.ViewModels.InsertCarViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
#{Html.RenderAction("Create", Model);}
</div>
<div>
#{Html.RenderAction("List", Model);}
</div>
<div>
#{Html.RenderAction("Edit", Model);}
</div>
</body>
</html>
Obviously the Edit view needs an ID to display, and it gets an error now when I use RenderAction, because there is no ID when I start the application. I want to hide this view when it is not needed, and only display this view when it is needed. How can I achive this without Javascript / Jquery.
Do I need an if/else statement inside my ActionResult?
The simplest and quickest thing to do would be to just check if id has a value
[HttpGet]
public ActionResult Edit(string id)
{
if (String.IsNullOrEmpty(id))
{
return null;
}
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
return View(car.ConvertToUpdateViewModel());
}
That's not a problem, in MVC Edit Controller typically has an id param, so to eliminate your problem you can just check if id is existing, something like this:
[HttpGet]
public ActionResult Edit(string id)
{
Car car = CarRentalContext.Cars.FindOneById(new ObjectId(id));
if (car != null)
{
return View(car.ConvertToUpdateViewModel());
}
//if we get this far show other view
return View();
}

Resources