Passing Model Through POST - asp.net-mvc-4

I'm having some problems with one specific part of my ASP.NET site. This page is essentially where a user edits an invoice and then posts the changes.
At this point I'm doing a re-write of an existing page, which works. I cannot figure out what the difference is other than the other one is older MVC 3.
When I first go to the EditInvoice Action:
public ActionResult EditInvoice()
{
SalesDocument invoice = null;
try
{
invoice = SomeService.GetTheInvoice();
}
catch (Exception ex)
{
return HandleControllerException(ex);
}
return View("InvoiceEdit", invoice.LineItems);
}
The model of the "InvoiceEdit" view is the List
Now the view loads fine, and displays all the document line items in a form:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<MyApp.SalesDocumentLineItem>>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Line Items</legend>
<div id="lineItems" class="table">
<div class="row">
<div class="colHButton">X</div>
<div class="colH">Item</div>
<div class="colHPrice">Price</div>
<div class="colHQty">Qty</div>
</div>
<% foreach (var item in Model.Where(m => m.LineItemType.Id == NexTow.Client.Framework.UnitsService.LineItemTypes.SaleItem || m.LineItemType.Id == NexTow.Client.Framework.UnitsService.LineItemTypes.NonSaleItem))
{ %>
<div class="row">
<%=Html.Hidden("Model.LineItemNameId", item.LineItemNameId)%>
<div class="cellButton"><button onclick="DeleteContainer(event);">X</button></div>
<div class="cell"><span class="formData"><%=item.LineItemDescription %></span></div>
<div class="cellPrice">
<span class="formData">$</span>
<%= Html.TextBox("Model.Price", item.Price, new { style="width:75px;" })%>
</div>
<div class="cellQty">
<%= Html.TextBox("Model.Quantity", item.Quantity, new { style = "width:60px;" })%>
</div>
</div>
<%} %>
</div>
</fieldset>
<p>
<input type="submit" value="Update Invoice" onclick="SequenceFormElementsNames('salesItems');" />
</p>
<% } %>
</asp:Content>
This then provides the user the ability to edit the entries, and then the user clicks the "Update Invoice" submit button. This posts to the same view and action using a POST:
[HttpPost]
public ActionResult EditInvoice(List<SalesDocumentLineItem> salesItems)
{
if (salesItems == null || salesItems.Count == 0)
{
return View("ClientError", new ErrorModel() { Description = "Line items required." });
}
SalesDocument invoice = null;
try
{
invoice = SomeService.UpdateInvoice();
}
catch (Exception ex)
{
return HandleControllerException(ex);
}
InvoiceModel model = new InvoiceModel();
model.Document = invoice;
return View("InvoicePreview", model);
}
However, eventhough this worked in the old application. In the new one, this does not work. When we breakpoint at the final EditInvoice POST action method, the collection of salesItems is NULL. What is happening!?

When you use Html.TextBox(string, object), the first argument is used as the name of the form field. When you post this form back to the server, MVC looks at your action method's argument list and uses the names of the form fields to try and build those arguments. In your case, it tries to build a List<SalesDocumentLineItem>.
It looks like you're using "Model.Something" as the names of your form fields. This probably worked in the past, but I'm guessing something changed in MVC4 such that the framework doesn't know what you're talking about anymore. Fortunately, there's a better way.
Instead of setting the name of the form field using a string, use the strongly-typed HTML helpers like this:
<% for (var i = 0; i < Model.Count; i++) { %>
<div class="row">
<%= Html.HiddenFor(model => model[i].LineItemNameId) %>
<!-- etc... -->
</div>
<% } %>
These versions of the HTML helpers use a lambda expression to point to a property in your model and say "See that property? That one right there? Generate an appropriate form field name for it." (Bonus: Since the expression is pointing at the property anyway, you don't have to specify a value anymore -- MVC will just use the value of the property the expression represents)
Since lambda expressions are C# code, you will get a compile-time error if you make a typo, and tools like Visual Studio's "Rename" feature will work if you ever change the property's name.
(This answer goes into more detail on exactly how this works.)

Related

Cannot populate a repeater System.String' does not contain a property with the name

I am trying to populate a simple ASP repeater from the database. The query works fine and returns several varchar type characters.Here is my code, any insight would help out a lot, im stumped.
Front End
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<p> <%# Eval("FormData")%> </p>
</ItemTemplate>
</asp:Repeater>
Back End
public void Page_Load(object sender, EventArgs e)
{
Repeater1.DataSource = GetKudosList();
Repeater1.DataBind();
}
public List<string> GetKudosList()
{
using (IntranetEntities KudosContext = new IntranetEntities())
{
var jon = KudosContext.FormInstances.Where(u => u.WorkflowID == 1).Select(u => u.FormData).ToList();
return jon;
}
}
Even if i just create a list object in the back end without touching the DB i still get the error below
'System.String' does not contain a property with the name 'FormData'
Your problem is in your linq statement--> you are selecting: .Select(u => u.FormData).ToList();
You will get a List of FormData type, which is probably type of String... (which causes the eval to fail. You are trying to eval property "FormData" on a simple string)
Either use your item as is without eval, or remove the select :
Option 1:
You only need the FormData from your object:
Use the linq as is, change your repeater template:
<p> <%# Container.DataItem %> </p>
Option 2:
You need the whole object and you want to display properties from it,
Change your linq:
var jon = KudosContext.FormInstances.Where(u => u.WorkflowID == 1).ToList();
Change your linq query to select an anonymous type instead of string.
var jon = KudosContext.FormInstances
.Where(u => u.WorkflowID == 1)
.Select(u => new { FormData = u.FormData}).ToList();
Now you can use DataBinder to get the property name from anonymous type:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<p> <%# (string)DataBinder.Eval(e.Item.DataItem, "FormData"); %> </p>
</ItemTemplate>
</asp:Repeater>
Thanks for all the Support! I think your suggestions were correct altho they were not working for me. The string "FormData" was not binding to the object
<%# this.GetDataItem().ToString() %>

Sitecore MVC Controller and Forms - ActionResult not rendering Layout on postback

Sitecore 7.1v1, most recent Glass Mapper, MVC4. When we submit the form POST, we get no layout with the return View. I'd prefer not to have to redirect to another page since this is supposed to be a wizard-like experience. This is also lightweight enough not to require Ajax, although we could use it as a last resort. I can't find who to make sure that while returning the View that we get the layout as well. I'm new to Sitecore MVC and pretty new at MVC in general. The PageBase that's referenced is a Custom Model using Glass.
We have the following Controller Rendering:
public class RegistrationController : Controller
{
[HttpGet]
public ActionResult VerifyAccount()
{
return View("~/Views/Public/Wizards/Registration/VerifyAccount.cshtml",
new SitecoreContext().GetCurrentItem<PageBase>());
}
[HttpPost]
public ActionResult CreateProfile()
{
ViewBag.Name = Request["VerificationType"];
ViewBag.Step = 2;
return View("~/Views/Public/Wizards/Registration/CreateProfile.cshtml",
new SitecoreContext().GetCurrentItem<PageBase>());
}
}
The default action for this is VerifyAccount(). This renders as expected. The initial view is as follows:
#inherits Glass.Mapper.Sc.Web.Mvc.GlassView<Public.Model.GlassModel.Primary.PageBase>
<div>
<h3>#Editable(a => a.Title)</h3>
<p>
#Editable(a => a.Description)
</p>
<hr />
#using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post))
{
#Html.Sitecore().FormHandler("Registration", "CreateProfile")
#Html.ValidationSummary(true, "Verification Not Selected.")
<fieldset>
#Sitecore.Globalization.Translate.Text("$Registration.VerificationTitle")
#{ var validations = new SitecoreContext().GetItem<GlassFrameBase>(Guid.Parse("{3694FC43-3DB7-470A-A1E9-2649856AAF10}"));}
<select id="VerType" name="VerificationType">
#foreach (var validation in validations.GetChildren<VerificationMethod>())
{
<option value="#validation.MethodValue">#validation.MethodName</option>
}
</select>
<input type="submit" value="Next" />
</fieldset>
}
This posts back to the CreateProfile() Method. This part works great. The only issue is that when it returns the view this time, it returns just the view without the layout.
The final view is as follows:
#using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post))
{
#Html.Sitecore().FormHandler()
<p>
<b>Verification Type Was: </b>#ViewBag.Name
</p>
<p>#ViewBag.Step</p>
<input type="hidden" value="ThisIsATest" name="TestHidden" id="TestHidden"/>
<input type="submit" name="back" value="Back" /><br />
<input type="submit" name="next" value="Next" />
}
Everything else is working exactly as expected but I'm missing something important that loads the Layout on the return trip.
I have noticed this before as well and I think it relates to this line:
#Html.Sitecore().FormHandler("Registration", "CreateProfile")
It seems to bypass the standard rendering pipeline and just call the target action. I have written a blog post on how you can control calls to different action on multiple controllers. this might help:
http://www.experimentsincode.com/?p=425
Try changing the return type of CreateProfile from ActionResult to PartialViewResult, and then return View("... to return PartialView("...
Also, here's a post about what you can return for Sitecore Controller Renderings.
http://mhwelander.net/2014/04/09/sitecore-controller-rendering-action-results-what-can-i-return/
I haven't looked deeply into form posting with Controller Renderings, but if the above suggestion doesn't work then maybe consider the execution lifestyle used in Sitecore MVC (mentioned in the post).

Serve up images from network to View in ASP.NET MVC

I'm trying to load images from my server and then load them to a view:
var files = _directoryInfo.GetFiles()
.Where(x => x.Name.Contains(request.WarrantyReference))
.ToList();
model.Photos = files;
return View(model);
Then in the View im doing:
#if (Model.Photos.Count > 0)
{
#Html.Partial("_Photos", Model.Photos)
}
_Photos:
#model List<FileInfo>
<div class="ui items">
#foreach (var photo in Model)
{
<div class="item">
<div class="image">
<img src="#photo.FullName">
</div>
</div>
}
</div>
But nothing appears, I get the following error in the console:
Not allowed to load local resource:
file://my-server/dataphoto/WX0001 1.jpg
So I was wondering if it is possible to store the photos temporarily on my server to serve them to the View or is there another way around this?
Store the file names (or some identifier) in your Model.
Model
class Model
{
IList<string> Photos { get; set; }
Create an HtmlHelper to display your photo image.
In your example you could store "WX0001 1.jpg" as the photo identifier then when you read the file from the network server you look in a specific directory.
View
#model List<FileInfo>
<div class="ui items">
#foreach (var photo in Model)
{
<div class="item">
<div class="image">
#Html.PhotoImage(photo)
</div>
</div>
}
</div>
public static MvcHtmlString PhotoImage(this HtmlHelper html, string id)
{
var img = new TagBuilder("img");
var your_image_byte_array = read_image_from_server_using_id;
var base64 = Convert.ToBase64String(your_image_byte_array);
img.Attributes.Add("src", String.Format("data:image/gif;base64,{0}", base64));
return MvcHtmlString.Create(img.ToString());
}
Alternatively, have a look at using an Action method to display the image... Convert Byte Array to Image and Display in Razor View
you would need to copy the files somewhere where the ASP.NET user being used for the AppPool serving/hosting the website has access to. Alternatively, give the share the app pool account privileges to read. One other way would be to read the files and store them in a byte[] and finally render it on the screen/view without actually writing the files to disk.

Formatting a list item in jQuery Mobile for a POST link

I have a question about jQuery Mobile formatting, and how to get an <li> to format properly in a listview. The MVC 4 default template for a Mobile application in Visual Studio has a logoff link that uses a GET call to logoff. Here is the markup:
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>#Html.ActionLink("Log off", "LogOff")</li>
</ul>
And here is the call:
// GET: /Account/LogOff
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
With jQuery Mobile, it looks like this:
In contrast, the MVC 4 default template for an internet application uses a POST method for the same logoff:
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id =
"logoutForm" })) {#Html.AntiForgeryToken()
<a ref="javascript:document.getElementById('logoutForm').submit()">Log off</a>}
What I would like to do is to use a POST method in my Mobile application to log off instead of the default GET method (in keeping with best practice: Logout: GET or POST?). I can get the link to work properly, but what I am having trouble with is getting the button to look the way it did when it was using the GET method.
My markup looks like this:
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{#Html.AntiForgeryToken()
Log off}
</li>
</ul>
But the result looks like this:
I’ve tried putting the <li> tags around the <a> tag instead, but the results are not any better:
Could someone explain why this <li> tag is being formatted differently than the one above it, and how I might get it to look like the one at the top of this post?
Unless you feel like creating your own custom CSS in that list item don't put the actual <form> inside of it because forms have their own default CSS. Instead keep the form somewhere outside the list item and put the button itself inside the list item
<ul data-role="listview" data-inset="true">
<li>#Html.ActionLink("Change password", "ChangePassword")</li>
<li>Log off</li>
</ul>
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
}

need rails like form_for tag in Rhodes

I was wondering if I can have something like form_for tag instead of the html tag in the edit.erb page in Rhodes. Because I have a Counter model with two attribs which I want to update them seperately based on the button pressed which resides right beside the value. I was able to do it in rails using the <%= form.submit 'up_a' %> & check which button was pressed in update method, like:
def update
#counter = Counter.find(params[:id])
if params[:commit] == 'up_a'
update_attri1 # simple increment method for attrib 1
elsif params[:commit] == 'up_b'
update_attri2 # simple increment method for attrib 2
end
end
and call that method to update that value.
So I want to have more control on what attribs I want to update based
on the clicks in form. Is there anyway I can achieve this in Rhodes?
EDIT :
The general problem seems to be that you want two submit buttons in a single form, each of which should do two slightly different things.
In the case that you only have two different values and one submit button for each, the simplest solution would be simply to make two forms that both call the update def (through their action-attribute), but each with their specific value of the query-parameter (in this case "commit"). These calls would have the following form:
<form method="POST" class="myForm" action="<%=url_for :controller => :Counter, :action => :update, :query => {:commit => 'up_a'}%>">
However, if you only want a single form (possibly also with many other input-values) there are several different ways to do it. In the following you will see a detailed implementation of one way to do it.
In this solution your buttons should NOT be submit buttons, but regular buttons (exactly how they are made with jQuery Mobile).
In order to make this solution work, you will need to use some javascript. You should therefore add the following javascript functions to your application.js and include it in your layout.erb.
function submitForm(formClass){
var activeForm = 'div.ui-page-active '+formClass;
$(activeForm).submit();
}
function callCounterSetUpdateAction(c){
$.get('/app/Counter/setUpdateAction', { commit: c});
}
Now that we have the needed javascript functions in place, lets take a look at edit.erb.
In this example Counter have three different attributes: a, b and c. We will however, only pay attention to a and b to begin with.
The form in your edit.erb file should be similar the implementation below. Notice, that the form actually doesn't have a submit button (as we will see later, the submit is actually made through our javascript function submitForm(formClass)).
<form method="POST" class="myForm" action="<%= url_for :action => :update %>">
<input type="hidden" name="id" value="<%= #counter.object %>"/>
<div data-role="fieldcontain">
<label for="counter[a]" class="fieldLabel">A</label>
<input type="text" id="counter[a]" name="counter[a]" value="<%= #counter.a %>" <%= placeholder( "A" ) %> />
</div>
<div data-role="fieldcontain">
<label for="counter[b]" class="fieldLabel">B</label>
<input type="text" id="counter[b]" name="counter[b]" value="<%= #counter.b %>" <%= placeholder( "B" ) %> />
</div>
<div data-role="fieldcontain">
<label for="counter[c]" class="fieldLabel">C</label>
<input type="text" id="counter[c]" name="counter[c]" value="<%= #counter.c %>" <%= placeholder( "C" ) %> />
</div>
<a data-role="button" data-transition="none" href="javascript:callCounterSetUpdateAction('up_a');">Update A</a>
<a data-role="button" data-transition="none" href="javascript:callCounterSetUpdateAction('up_b');">Update B</a>
</form>
Now that we have defined our view (edit.erb) lets take a look at the definitions we need our controller.
Firsly, as it can be seen from the href attribute on the buttons, what actually happens once we press a button is that it calls a javascript function which in turn calls the following def in the controller:
def setUpdateAction
$pressedButton = #params['commit']
WebView.execute_js("submitForm('.myForm');")
end
The purpose of this def is to store the parameter we sent from our button and then submit the form on the active page. Notice here that we added a class called myForm to the form shown above. You should also notice that we ensure that only the form on the active page is selected by adding 'div.ui-page-active ' to our formClass in the jQuery selection.
Finally, lets take a look at how your update definition should look like:
def update
#counter = Counter.find(#params['id'])
c = #params['counter']
if #counter
if $pressedButton == 'up_a'
# Update value A.
#counter.update_attributes(
{"a" => c['a']}
)
elsif $pressedButton == 'up_b'
# Update value B.
#counter.update_attributes(
{"b" => c['b']}
)
end
end
redirect :action => :index
end
It should be noticed here that we select which attributes to update based upon the $pressedButton variable we assigned through setUpdateAction. As a final comment we could also update multiple attributes as seen below (where we also update the 'c' attribute).
#counter.update_attributes(
{"b" => c['b'],"c" => c['c']}
)