Html.ActionLink object parameters + MVC4 - asp.net-mvc-4

I am calling Edit action from my view that should accept an object as parameter.
Action:
[HttpPost]
public ActionResult Edit(Organization obj)
{
//remove the lock since it is not required for inserts
if (ModelState.IsValid)
{
OrganizationRepo.Update(obj);
UnitOfWork.Save();
LockSvc.Unlock(obj);
return RedirectToAction("List");
}
else
{
return View();
}
}
From View:
#foreach (var item in Model) {
cap = item.GetValForProp<string>("Caption");
nameinuse = item.GetValForProp<string>("NameInUse");
desc = item.GetValForProp<string>("Description");
<tr>
<td class="txt">
<input type="text" name="Caption" class="txt" value="#cap"/>
</td>
<td>
<input type="text" name="NameInUse" class="txt" value="#nameinuse"/>
</td>
<td>
<input type="text" name="Description" class="txt" value="#desc"/>
</td>
<td>
#Html.ActionLink("Edit", "Edit", "Organization", new { obj = item as Organization }, null)
</td>
</tr>
}
It is raising an exception: The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Edit(Int32)' in 'PartyWeb.Controllers.Internal.OrganizationController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
Can somebody advise how to pass object as parameter?

Can somebody advise how to pass object as parameter?
Why are you using an ActionLink? An ActionLink sends a GET request, not POST. So don't expect your [HttpPost] action to ever be invoked by using an ActionLink. You will have to use an HTML form and include all the properties you want to be sent as input fields.
So:
<tr>
<td colspan="4">
#using (Html.BeginForm("Edit", "Organization", FormMethod.Post))
{
<table>
<tr>
#foreach (var item in Model)
{
<td class="txt">
#Html.TextBox("Caption", item.GetValForProp<string>("Caption"), new { #class = "txt" })
</td>
<td class="txt">
#Html.TextBox("NameInUse", item.GetValForProp<string>("NameInUse"), new { #class = "txt" })
</td>
<td class="txt">
#Html.TextBox("Description", item.GetValForProp<string>("Description"), new { #class = "txt" })
</td>
<td>
<button type="submit">Edit</button>
</td>
}
</tr>
</table>
}
</td>
</tr>
Also notice that I used a nested <table> because you cannot have a <form> inside a <tr> and some browser such as IE won't like it.

Related

Cannot bind source type Umbraco.Web.Models.RenderModel to model type Repower.Cms.Umbraco.Models.Test

First of all I am new to Umbraco so if you see some basic mistakes don't judge me.
So I am creating currently a Login form which he goes to the database (check Username and Password) and reads the value which he returns and let's him Cannot bind source type Umbraco.Web.Models.RenderModel to model type Repower.Cms.Umbraco.Models.Test.
This Is my HTML:
#inherits Umbraco.Web.Mvc.UmbracoViewPage<Repower.Cms.Umbraco.Models.Test>
#{
Layout = "Master.cshtml";
}
<style type="text/css">
.btnStyle {
border: thin solid #000000;
line-height: normal;
width: 80px;
}
</style>
#using (Html.BeginForm("Test", "MembersProtectedPage", FormMethod.Post))
{
<div class="fontStyle">
<center>
<table style="margin-top: 100px;margin-left:150px">
<tr style="height:30px">
<td align="right">
#Html.LabelFor(m => m.User)
</td>
<td style="width:200px" align="right">
#Html.TextBoxFor(m => m.User)
</td>
<td style="width:250px;color:Red" align="left">
#Html.ValidationMessageFor(m => m.User)
</td>
</tr>
<tr style="height:30px">
<td align="right">
#Html.LabelFor(m => m.Password)
</td>
<td align="right">
#Html.PasswordFor(m => m.Password)
</td>
<td style="width:250px;color:Red" align="left">
#Html.ValidationMessageFor(m => m.Password)
</td>
</tr>
<tr style="height:30px">
<td colspan="2" align="center">
<input type="submit" value="Sign In" class="btnStyle" />
</td>
</tr>
</table>
</center>
</div>
}
This is my Model:
public class Test : RenderModel
{
public Test() : this(new UmbracoHelper(UmbracoContext.Current).TypedContent(UmbracoContext.Current.PageId)) { }
public Test(IPublishedContent content, CultureInfo culture) : base(content, culture) { }
public Test(IPublishedContent content) : base(content) { }
string connString = ConfigurationManager.ConnectionStrings["connectionStringName"].ConnectionString;
SqlConnection conn;
SqlCommand sqlcomm;
public string User { get; set; }
public string Password { get; set; }
public bool IsUserExist(string emailid, string password)
{
bool flag = false;
conn = new SqlConnection(connString);
conn.Open();
sqlcomm = new SqlCommand();
sqlcomm.Connection = conn;
sqlcomm.CommandType = System.Data.CommandType.StoredProcedure;
sqlcomm.CommandText = "dbo.uspLogin";
sqlcomm.Parameters.AddWithValue("#pLoginName", User);
sqlcomm.Parameters.AddWithValue("#pPassword", Password);
SqlParameter retval = sqlcomm.Parameters.Add("#RESULT", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.ExecuteNonQuery(); // MISSING
string retunvalue = (string)sqlcomm.Parameters["#RESULT"].Value;
switch (retunvalue)
{
case "0":
flag = true;
break;
case "1":
flag = false;
break;
case "2":
flag = false;
break;
default:
flag = false;
break;
}
return flag;
}
}
And this is my Controller:
public class TestController : Controller
{
public ViewResult Login()
{
return View();
}
[HttpPost, ValidateInput(false)]
public ActionResult Login(Test model)
{
if (ModelState.IsValid)
{
if (model.IsUserExist(model.User, model.Password))
{
ViewBag.UserName = model.User;
FormsAuthentication.RedirectFromLoginPage(model.User, false);
}
else
{
ModelState.AddModelError("", "Username or Password Incorrect.");
}
}
return View(model);
}
}
So I am inheriting a RenderModel because previously my error was "The model item passed into the dictionary is of type Repower.Cms.Umbraco.Models.Test', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'."
So I changed it (searched a lot in the Internet) and now I get this error.
Is also the rest of the code correct? The way that I access the Database and everything? I am expecting a Return value from the Database (don't know if that is correct)
Could Someone please help me?
I need to get this done today.
Thanks in Advance
There's a few issues with your implementation.
Use the Umbraco MemberService
You're reinventing the wheel by building a new table which holds member information (such as username and password).
Umbraco has built in membership which can handle members of your site. You can view the GUI in Umbraco at /umbraco/#/member. Using this GUI, you can manually create and edit members.
You can also programmatically create end edit members in this section using this MemberService.
For example, register a member:
var MemberService = ApplicationContext.Current.Services.MemberService
var member = MemberService.CreateMemberWithIdentity(newEmail, newEmail, newName, "Member");
MemberService.Save(member);
MemberService.SavePassword(member, newPassword);
FormsAuthentication.SetAuthCookie(newEmail, true);
Login:
var memberService = ApplicationContext.Current.Services.MemberService;
if (memberService.Exists(email))
{
if (Membership.ValidateUser(email, password))
{
FormsAuthentication.SetAuthCookie(email, true);
}
}
You can read about what other methods are available here.
You're mixing up your MVC
Your Test model isn't purely a model, is also has some controller in it as it is handling database stuff too!
Ideally, your model should just contain the data which has been sent forward, and your TestController should handle using that data.
As for fixing your binding issue
You're currently setting your page view model to Repower.Cms.Umbraco.Models.Test, where as I think it should be left as is.
Stop this model from inheriting from RenderModel.
Instead, render a partial with your code.
For your page view:
#inherits Umbraco.Web.Mvc.UmbracoViewPage
#{
Layout = "Master.cshtml";
}
#Html.Partial("Login", new Repower.Cms.Umbraco.Models.Test())
Partial called Login.cshtml:
#model Repower.Cms.Umbraco.Models.Test
#using (Html.BeginUmbracoForm<TestController>("Login"))
{
<div class="fontStyle">
<center>
<table style="margin-top: 100px;margin-left:150px">
<tr style="height:30px">
<td align="right">
#Html.LabelFor(m => m.User)
</td>
<td style="width:200px" align="right">
#Html.TextBoxFor(m => m.User)
</td>
<td style="width:250px;color:Red" align="left">
#Html.ValidationMessageFor(m => m.User)
</td>
</tr>
<tr style="height:30px">
<td align="right">
#Html.LabelFor(m => m.Password)
</td>
<td align="right">
#Html.PasswordFor(m => m.Password)
</td>
<td style="width:250px;color:Red" align="left">
#Html.ValidationMessageFor(m => m.Password)
</td>
</tr>
<tr style="height:30px">
<td colspan="2" align="center">
<input type="submit" value="Sign In" class="btnStyle" />
</td>
</tr>
</table>
</center>
</div>
}
Finally, update your controller to inherit from SurfaceController.
I hope this helps
This isn't the complete solution but should help you get off on the right track.

Insert Partial View to a another Partial View

This is the main view Dept_Manager_Approval.cshtml where I have put a modal to show data.
<td>
<i title="View Details">
#Ajax.ActionLink(" ", "ViewAccessStatus", new { id = item.request_access_id },
new AjaxOptions
{
HttpMethod = "Get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "edit-div",
}, new { #class = "fa fa-eye btn btn-success approveModal sample" })</i>
</td>
In this partial view which just a modal, ViewAccessStatus.cshtml , I have inserted in here another partial view.
<div>
<h2><span class ="label label-success">Request Creator</span> </h2>
#if (Model.carf_type == "BATCH CARF")
{
#Html.Partial("Batch_Requestor1", new {id= Model.carf_id })
}else{
<h4><span class ="label label-success">#Html.DisplayFor(model=>model.created_by)</span></h4>
}
</div>
COntroller:
public ActionResult Batch_Requestor1(int id = 0)
{
var data = db.Batch_CARF.Where(x => x.carf_id == id && x.active_flag == true).ToList();
return PartialView(data);
}
Batch_Requestor1.cshtml
#model IEnumerable<PETC_CARF.Models.Batch_CARF>
#{
ViewBag.Title = "All Requestors";
}
<br/><br/>
<table class="table table-hover">
<tr class="success">
<th>
#Html.DisplayName("Full Name")
</th>
<th>
#Html.DisplayName("Email Add")
</th>
<th>
#Html.DisplayName("User ID")
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.fname) - #Html.DisplayFor(modelItem => item.lname)
</td>
<td>
#Html.DisplayFor(modelItem => item.email_add)
</td>
<td>
#Html.DisplayFor(modelItem => item.user_id)
</td>
</tr>
}
</table>
When I run this, I've got this error
The model item passed into the dictionary is of type '<>f__AnonymousType01[System.Int32]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[PETC_CARF.Models.Batch_CARF]'.
Any ideas how will I insert another partial view?
#Html.Partial() renders a partial view. It does not call an action method that in turn renders the partial. In your case
#Html.Partial("Batch_Requestor1", new {id= Model.carf_id })
is rendering a partial view named Batch_Requestor1.cshtml and passing it a model defined by new {id= Model.carf_id } (and anonymous object) but that view expects a model which is IEnumerable<PETC_CARF.Models.Batch_CARF>.
Instead, you need to use
#Html.Action("Batch_Requestor1", new {id= Model.carf_id })
which calls the method public ActionResult Batch_Requestor1(int id = 0) and passes it the value of Model.carf_id, which will in turn render the partial view.

asp.net mvc dropdownlist with directory files list

I want to show all files from a folder "newsletter" with category list. but when I use following code, I got error, my code is :
public ActionResult templatelist()
{
var data = _session.emailForcampaigns.Select(m => m.category).Distinct().AsQueryable();
ViewBag.ddlCategory = new SelectList(data, "category", "category");
return View(Directory.EnumerateFiles(Server.MapPath("~/newsletter")));
}
and i have following lines in view
<table class="tables" >
<tr>
<td>Fiels</td>
<td>
<select id="template" name="template">
#foreach (var fullPath in Model)
{
var fileName = Path.GetFileName(fullPath);
<option value="#fileName"> #fileName</option>
}
</select>
</td>
</tr>
<tr>
<td>Category </td>
<td>
#Html.DropDownList("ddlCategory", null, new { #class = "dropdown " })
</td>
</tr>
</table>
but i am getting error
You forgot to specify a data source for the drop-down list:
#Html.DropDownList("ddlCategory", new SelectList(ViewBag.ddlCategory , "category", "category"))

MVC 4 - Return error message from Controller - Show in View

I am doing a C# project using Razor in VS2010 (MVC 4).
I need to return an error message from Controller to View and show it to the user.
What I have tried:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
ModelState.AddModelError("error", "adfdghdghgdhgdhdgda");
ViewBag.error = TempData["error"];
return RedirectToAction("Form_edit", "Form");
}
VIEW:
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary("error")
#Html.ValidationMessage("error")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Please help me to achieve this.
The Return View(model) returns you error because you don't fill the model with the values in your post method and the model data for the dropdown is empty. Please provide the Get method to explain further how to manage displaying the error. In order to the error to be shown you should use this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
if(ModelState.IsValid())
{
--- operations
return Redirect("OtherAction", "SomeController");
}
// here you can use a little trick
//fill the model property that holds the information for the dropdown with the data
// you haven't provided the get method but it should look something like this
model.Countries = ... some data goes here;
model.dd_value = ... some other data;
model.dd_text = ... other data;
ModelState.AddModelError("", "adfdghdghgdhgdhdgda");
return View(model);
}
and then in the view just use :
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary(true)
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
This should work okay.
If you just use RedirectToAction it will redirect you to the get method --> you will have no error but the view will be just reloaded and no error would be shown.
other way around is that you can pass the error not by ModelState.AddError, but with ViewData["error"] like this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
TempData["error"] = "someErrorMessage";
return RedirectToAction("form_Post", "Form");
}
[HttpGet]
public ActionResult form_edit()
{
do stuff here ----
ViewData["error"] = TempData["error"];
return View();
}
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
<div>#ViewData["error"]</div>
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Thanks for all the replies.
I was able to solve this by doing the following:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
return RedirectToAction("Form_edit", "Form", model);
}
public ActionResult form_edit(FormModels model, string searchString,string id)
{
string test = model.selectedvalue;
var bal = new FormModels();
bal.Countries = bal.get_contentdetails(searchString);
bal.selectedvalue = id;
bal.dd_text = "content_name";
bal.dd_value = "content_id";
test = model.error_msg;
ViewBag.head = "Heading";
if (model.error_msg != null)
{
ModelState.AddModelError("error_msg", test);
}
model.error_msg = "";
return View(bal);
}
VIEW:
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#ViewBag.error
#Html.ValidationMessage("error_msg")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
}
If you want to do a redirect, you can either:
ViewBag.Error = "error message";
or
TempData["Error"] = "error message";
You can add this to your _Layout.cshtml:
#using MyProj.ViewModels;
...
#if (TempData["UserMessage"] != null)
{
var message = (MessageViewModel)TempData["UserMessage"];
<div class="alert #message.CssClassName" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>#message.Title</strong>
#message.Message
</div>
}
Then if you want to throw an error message in your controller:
TempData["UserMessage"] = new MessageViewModel() { CssClassName = "alert-danger alert-dismissible", Title = "Error", Message = "This is an error message" };
MessageViewModel.cs:
public class MessageViewModel
{
public string CssClassName { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
Note: Using Bootstrap 4 classes.

Call post edit action from Razor view mvc4

This question might have been asked several times, however it is not working in my case, so please bear with me.
I have the below actions in my controller:
[HttpPost]
public ActionResult Edit(Organization obj)
{
if (ModelState.IsValid)
{
OrgRepo.Update(obj);
return RedirectToAction("Details");
}
else
return View();
}
public ActionResult Edit(int id)
{
return View();
}
I am trying to update the data into database by calling post edit action.
For this purpose, I am calling the edit action as below:
#foreach (var item in Model) {
var test = item.PartyId;
<tr id="#test">
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Caption)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.NameInUse)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Description )"/>
</td>
<td>
#using (Html.BeginForm())
{
#Html.ActionLink("Edit", "Edit", "Org", null, new { #obj = item })
}
</td>
</tr>
However when I click on edit I am getting exception:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Edit(Int32)' in 'Dwiza.Controllers.OrgController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
My questions:
How can I fix this?
Why it get edit action is getting invoked instead of post edit?
What are better ways to invoke edit action among invoke through jQuery, or ajax or any others, if you can suggest better ways of doing it?
The #Html.ActionLink produces an a tag which can only be used to call GET. Change to a submit button to get a POST.
Normally with an Edit, you are only editing a sinble model rather than a collection on a page but going with what you have, change the cshtml to:
#model ICollection<Organization>
<table>
#foreach (var item in Model)
{
using (Html.BeginForm())
{
var test = item.PartyId;
<tr id="#test">
<td class="txt">
<input type="text" name="Caption" class="txt" value="#item.Caption"/>
</td>
<td class="txt">
<input type="text" name="NameInUse" class="txt" value="#item.NameInUse"/>
</td>
<td class="txt">
<input type="text" name="Description" class="txt" value="#item.Description" />
</td>
<td>
<input type="hidden" name="PartyId" value="#item.PartyId"/>
<button type="submit">Edit</button>
</td>
</tr>
}
}
</table>
Now each table row is wrapped by a form meaning the submit button will post that data. The name attribute on the inputs will cause the MVC model binders to bind your posted values to your model correctly.
This hidden input at the end will ensure your PartyId value gets posted back. The fact that it is in int (and not nullable) was giving the exception with your initial code I think.
HTH
EDIT
Adding controller code (note - I still think this is a little/lot strange as you should be editing only the one Organisation...
public ActionResult Edit(int id)
{
// get your organisations from your orgRepo... I'm mocking that out.
var orgs = new List<Organization> { new Organization { PartyId = 1, Description = "Org 1", Caption = "Caption 1", NameInUse = "Name 1"},
new Organization { PartyId = 2, Description = "Org 2", Caption = "Caption 2", NameInUse = "Name 2"}};
return View(orgs);
}
lordy, that is a mess dude. your form only has a link in it, and that link is to the edit action, that will invoke a get, the form will never post back. are you trying to do a form inside a table row?
#foreach (var item in Model) {
var test = item.PartyId;
<tr>
<td colspan ="4>
#using (Html.BeginForm("Edit", "Org", FormMethod.Post))
{
#Html.HiddenFor(modelItem => item.PartyId)
<table>
<tr id="#test">
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Caption)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.NameInUse)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Description )"/>
</td>
<td>
<input type="submit" value="edit" />
</td>
</tr>
</table>
}
</td>
</tr>
}
That code will do an edit inside a row, but i am just guessing at the structure from the code you posted.