I have a entity called WorkOrder which gets assigned to an Employee.
I want send an email notification when the workorder has been asigned. This can happen on my MVC Create or Edit Action (POST).
The problem i have is i have to do checks to see if the value has changed in the Edit to determine if i should send an email.
Is there a better place to call the SendEmail Function, like in the Entity Model itself?
If you are talking about posting from a view, you could create and bind the existing value to a hidden field in your form when loading the view. Then, on the POST to your action you can check to see if the value from the field is different from the one that is on the hidden field.
Example of View:
#using (Html.BeginForm("MyAction", "MyController")
{
#Html.HiddenFor(m => m.CurrentValue)
#Html.TextBoxFor(m => m.Value)
<input type="submit" value="submit" />
}
Example of Action GET
public ActionResult MyAction()
{
var viewModel = GetModelFromSomeWhere();
viewModel.CurrentValue = viewModel.Value;
return this.View(viewModel);
}
Example of Action POST
[HttpPost]
public ActionResult MyAction(ViewModel model)
{
if (model.Value != model.CurrentValue)
{
// It has changed! Send that email!
}
}
Related
I'm trying to enable posting Comments from the view where I display a Forum Post by id. That's the model which my view receives:
#model PostViewModel
the Model has and "Id" property, which I send to a controller with javascript:
<script>
$("#target").click(function () {
$("#formPlace").load('#Url.Action("AddComment","Posts", new { postId = Model.Id })');
});
</script>
That's the controller action I send the "postId" to:
[HttpGet]
public ActionResult AddComment(int postId)
{
var comment = new CommentViewModel();
comment.PostId = postId;
return this.PartialView("_AddComment", comment);
}
This view returns a form in which the user has to fill the comment's content:
#model MvcTemplate.Web.ViewModels.Posts.CommentViewModel
<div>
#using (Html.BeginForm("AddComment", "Posts", FormMethod.Post))
{
<div>Enter your comment here:</div>
<div>
#Html.TextAreaFor(x => Model.Content)
</div>
<input type="submit" name="Submit" />
}
</div>
When the view receives the model its "PostId" is still correct = what I set it with the javascript. However after the user submits the form, the CommentViewModel which is sent to the controller has 0(default int value) for "PostId". This is the controller action:
[HttpPost]
public ActionResult AddComment(CommentViewModel viewModel)
{
// transfer the view model to db model and save;
}
Any idea how I can keep the correct PostId?
When you submit the form to the AddComment action method, the default model binder will try to bind the form field values to the properties of your CommentViewModel object. Your form has an input field for the Content property, but you do not have one for the PostId. So the browser will send the value of only form element with name Content.
If you want PostId, You need to keep the Post id value in the form. Since user does not need to see/edit this, you may keep this in a hidden input field in the form.
You can use the Html.HiddenFor helper method to generate the hidden field with the name and value for the corresponding property.
#using (Html.BeginForm("AddComment", "Posts", FormMethod.Post))
{
<div>Enter your comment here:</div>
<div>
#Html.TextAreaFor(x => Model.Content)
</div>
#Html.HiddenFor(s=>s.PostId)
<input type="submit" name="Submit" />
}
Or just a hidden input element markup (the helper ultimately generate this only)
<input type="hidden" name="PostId" value="#Model.PostId" />
You need to put hidden field with PostId value in your form. You are only posting content. Alternatively your post action AddComment should have url parameter postId, so form's action url will include postId.
You can set your PostId in a TempData and can get in subsequent post action in controller. If you want to get it from view you need to set it in a hidden field as suggested by Shyju.
Controller:
public ActionResult MyController()
{
ViewBag.DateNow = DateTime.Now.ToString("yyyy-MM-dd");
}
[HTTPPost]
public ActionResult MyController(string fromDate)
{
ViewBag.DateNow = fromDate;
}
View:
#using (Html.BeginForm("MyController", "Account", FormMethod.Post))
{
//datepicker class: bootstrap-datepicker.js
<input id="fromDate" type="text" class="datepicker" />
<buttontype="submit" value="Search" class="btn btn btn-primary">
Search
</button>
}
What I'm trying to achieve is before POST the data that pass into ViewBag.DateNow is the current date and it successfully bring in to the view. However when I'm trying to fill up the input form with (eg: 2016-05-10) and click on the Search button. But seems like the fromDate string return NullReferenceException. I'm trying out with some solution online but I still can't get it right and that's why I decided to get this posted up. Thanks in advance!
For this to work properly you need to specify the name attribute in your textbox. It needs to be the same value as the input variable in your HTTP post action method, namely fromDate. Currently the id attribute is set to fromDate:
<input id="fromDate" name="fromDate" type="text" value="#ViewBag.DateNow" />
If you do not specify this name attribute then when you post your form fromDate will always be null. Specifying it like above will make sure that fromDate will always have a value (if entered).
I want to go a bit off-topic here, I would like to suggest that you make use of view models for your form submissions. Instead of having individual input variables in your action method you can just have your view model as input parameter.
I wrote an answer as to what view models are here, please go and read it if you have the time:
What is ViewModel in MVC?
Working on your example, I would have a view model that contains just one property, namely FromDate. FromDate will contain the value in your textbox. It is setup as a string because you want to pass it a formatted date value:
public class TestModel
{
public string FromDate { get; set; }
}
This value will be set in your HTTP get action method and the view model will be sent to the view:
public ActionResult Index()
{
TestModel model = new TestModel();
model.FromDate = DateTime.Now.ToString("yyyy-MM-dd");
return View(model);
}
In your view you will accept this view model and create the form accordingly:
#model WebApplication_Test.Models.TestModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.FromDate)
<button type="submit">Search</button>
}
When you submit this form, you need an HTTP post action method to handle the submission. Because the view is bound to the view model, the action method will accept it as an input parameter:
[HttpPost]
public ActionResult Index(TestModel model)
{
// Do what you need to do
string date = model.FromDate;
return View(model);
}
Your way of doing it is also correct. I have just shown you an alternative way to do it. Some day you might have a huge form with many input values, then my approach will be 'cleaner'.
Try this:
1) Replace with [HttpPost] instead of [HTTPPost]
2) You should add name=" " for input like this:
<input id="fromDate" name="fromDate" type="text" class="datepicker" />
When I post the form by clicking on the save button, it hits the post method but the model parameter is always null.
Controller:
[HttpPost]
public ActionResult Edit(QuestionMaster question)
{
if (questionLogic.Update(model))
{
return RedirectToAction("List");
}
return View();
}
View:
#using (Html.BeginForm()) {
<fieldset>
<legend>QuestionMaster</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Question)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Question)
#Html.ValidationMessageFor(model => model.Question)
</div>
</fieldset>
<p><input type="submit" value="Save" /></p>
}
You have not posted your model for QuestionMaster but from the view code it appears to contain a property named Question which is typeof string. The problem is that your POST method parameter is also named question which causes model binding to fail and the object is null.
Rename the parameter to anything but a name of a property in your model, for example
[HttpPost]
public ActionResult Edit(QuestionMaster model)
The reason why your model was null on postback is because of how model binding works
The DefaultModelBinder initializes a new instance of
QuestionMaster
The posted form's name/value pairs are then checked. If a matching
property name is found, the value of that property is set.
In your case your posting back Question="The text you entered" The
model binder finds the parameter named question (i.e. a match) and
sets it to "The text you entered", but question is typeof
QuestionMaster (a complex object, not a string) so binding fails
and the model becomes null.
Pass HtmlFieldPrefix in your EditorFor.
#Html.EditorFor(model => model.Question, new ViewDataDictionary() { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "QuestionMaster" }})
This way the names of the fields will be correct and the model binder will be able to bind them.
From the code you've given above, your View is missing a model.
You can add the model to the view as below:
#model QuestionMaster
This code is typically the first line in your view.
Other than that, can you explain the scope of model in your controller action? Where is model defined? If it isn't defined, you should understand that using #Model.someValue in the view is fine, but accessing model in your controller won't work unless your posted model parameter is called model.
Assuming that may be another reason for your form being "null", try changing your controller to:
[HttpPost]
public ActionResult Edit(QuestionMaster question)
{
if (questionLogic.Update(question))
{
return RedirectToAction("List");
}
return View();
}
You can do it what vortex told you or just change your parameter name to anything but "question"
[HttpPost]
public ActionResult Edit(QuestionMaster question2)
or
[HttpPost]
public ActionResult Edit(QuestionMaster model)
or
[HttpPost]
public ActionResult Edit(QuestionMaster anything)
I have a MVC3 application using Entity Framework as the Model layer.
In the EmployeeController, I have:
public ActionResult GetEmployeeEdit(String id)
{
// Get the desired Employee
var model = GetEmployees().FirstOrDefault(o=>o.EFolderid == id);
return View("EmployeeEdit", model);
}
private IQueryable<Employee> GetEmployees()
{
// Returns IQueryable<Employee>
return _employeeService.GetTable();
}
In EmployeeEdit I have:
#model Metastorm.Domain.Models.Employee
#{
ViewBag.Title = "Employee Edit";
}
#using (Html.BeginForm("SaveEmployee", "Employee", FormMethod.Get, Model))
{
<fieldset>
<legend>Edit Employee</legend>
<br />
#Html.Label("firstName", "First Name: ")
#Html.EditorFor(o => #Model.NameFirst)
<br />
#Html.Label("lastName", "Last Name: ")
#Html.EditorFor(o => #Model.NameLast)
</fieldset>
<br />
<input class="button" id="submit" type="submit" value = "Save Employee" />
}
Now back to the EmployeeController:
[HttpGet]
public ActionResult SaveEmployee(Employee employee)
{
if (ModelState.IsValid)
{
// Get the Employee Model again from Entity, Update and save
// Unfortunately, the employee object's FolderId value is null
}
// Just getting a model to satisfy the function
var model = GetEmployees().FirstOrDefault();
return View("EmployeeEdit", model);
}
The problem I'm having is that all properties on the employee object are null, except for Employee.NameFirst and Employee.NameLast, which happen to be the properties that were exposed in the View with Html.EditorFor.
In summary, I get an Employee model object, which is fully hydrated. I pass this model from the Controller to the view. In the view, selected fields are allowed to be updated. The Employee model is then passed back to the Controller where updates are persisted.
My question is how do I keep the Employee model that was originally passed from the Controller to the View intact. In other words, I want to have the model
Try to use this code:
[HttpGet]
public ActionResult SaveEmployee([Bind(Include = "NameFirst,NameLast,EFolderid")] Employee employee)
{
if (ModelState.IsValid)
{
// Do your staff
// The employee object's FolderId value will be not null
}
// Just getting a model to satisfy the function
var model = GetEmployees().FirstOrDefault();
return View("EmployeeEdit", model);
}
This will force the data members "NameFirst, NameLast, EFolderid" of the object employee to be persistent. You need to declare in the include statement all the members that you want to be preserved during the Http request.
You should also add this code in your view, inside the Form, or the EFolderid data will be lost:
#Html.HiddenFor(o=> #Model.EFolderid)
Add an "HiddenFor" declaration foreach member that you want to preserve.
To obtain model properties in a Post action, you need include all the properties inside the form.
It is necessary for a correct model serialization.
I went a different route. I created a view model, then us AutoMapper to map the Employee object's properties to the view model. I pass the view model to the view, make changes, then pass the view model back to the Action. Then I can then user AutoMapper to map the view model back to a new instance of the Employee Object, then persist the changes.
I have a form which I want to post to ensure the page refreshes on posting the data (not the ajax way). The problem is really I only want to post the Id of the record and extract that in the controller method. I'm finding that the form is posting everything (I might not be able to resolve that since the same form is used for updating). But I'd like to be able to have the variable pop into the parameter of controller method rather than extracting from the FormCollection. I've tried the various parameters below, but all are being passed null. Any idea what the problem is?
I have the following in my controller method:
public ActionResult Delete(FormCollection collection)
{
var idToDelete = collection["Current.CommissionStructureId"].ToInt32();
}
// tried the following but none of them bind
public ActionResult Delete(int? Current_CommissionStructureId, int? CommissionStructureId, int? Id, int? id)
{
// none of the above are binding (set to null)
}
You should use HttpPost only for deleting of records
#using (Html.BeginForm()) {
<input type="hidden" name="CommissionStructureId" value="#item.CommissionStructureId" />
<p>
<input type="submit" value="Delete" />
</p>
<p>
#Html.ActionLink("Back to List", "Index")
</p>
}
[HttpPost]
public ActionResult Delete(int CommissionStructureId)
{
CommissionStructure commissionStructure = db.CommissionStructures.Find(CommissionStructureId);
db.CommissionStructures.Remove(commissionStructure);
db.SaveChanges();
return RedirectToAction("Index");
}
When your form value is named Current.CommissionStructureId the default modelbinder will bind it only to a class parameter called Current with a property called Id.
So your options are
Create a small class with one property Id and use it as your parameter type
or
Write a custom modelbinder