MVC4 C# - How to submit list of object that are being displayed to the user? - asp.net-mvc-4

I'm working on an MVC4 C# project in VS2010.
I would like to allow the user to upload the contents of a .csv file to a database but there is a requirement to first echo the contents of the file to screen (as a final visual check) before submitting. What would be the best approach of submitting to the database as I am struggling to find a way of persisting the complex object in the view?
Here is the view where I am using a form to allow the user to upload the csv file:
#model IEnumerable<MyNamespace.Models.MyModel>
#{
ViewBag.Title = "Upload";
WebGrid grid = new WebGrid(Model, rowsPerPage: 5);
}
<h2>Upload</h2>
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
<h2>Grid</h2>
#grid.GetHtml(
//Displaying Grid here)
<p>
#Html.ActionLink("Submit", "Insert")
</p>
Here is the action in the controller that processes the csv file:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(path);
//Stream reader will read test.csv file in current folder
StreamReader sr = new StreamReader(path);
//Csv reader reads the stream
CsvReader csvread = new CsvReader(sr);
List<MyModel> listMyModele = new List<MyModel>(); // creating list of model.
csvread.Configuration.RegisterClassMap<MyModelMap>(); // use mapping specified.
listMyModel = csvread.GetRecords<MyModel>().ToList();
sr.Close();
//return View();
return View(listMyModel);
}
Up until this point everything is simple, I can upload the csv to the controller, read using CsvHelper, produce a list of MyModel objects and display in the view within a grid. To reiterate my initial question, is it now possible to submit the complex object (the list of MyModel) from the view as I can't figure out a way of making it available to an action within the controller.
Thank you.

Yes it's possible, It's "easier" if you have a Model with the IEnumerable in it so you can use the naming convention like this:
Property[index].ItemProperty
for every Html input/select field.
If you want to keep the IEnumerable as Model I think the naming convention is something like this:
ItemProperty[index]
So translated in code:
#Html.TextBoxFor(t => t.Property, new { name = "Property[" + i + "]" })
where i comes from a for loop to render all items or something like that.
I have already done it but I can't find the code at the moment. KendoUI uses this scheme for its multirows edit in the grid component.
You can check their POST AJAX requests for the right naming convention.
EDIT 1:
Otherwise you can think about store the model somewhere temporarily and retrieve it every time and updating with user inputs. It's a little more expensive but probably easier to write. Something like an updated csv file or a temporary db table.

Related

How to integrate my code into a sitefinity project

We have a sitefinity Customer Portal. Now we need to add MVC pages to it. I understand how to add a page, and how to drag e.g. a list to the page's content. But I don't understand how I can create a controller and other c# code to populate the list and do other custom things. We cannot open the project in Visual Studio, and we have no access to the existing code.
First of all, you must sure your project run success on your local. You can check it by login to back end page.
Then you can create the MVC component like this: (you should create all of this in root/MVC folder)
Create controller first:
[ControllerToolboxItem(Name = "ImportCSV", Title = "ImportCSV", SectionName = "ImportCSV")]
public class ImportCSVController : Controller
{
// GET: ImportCSV
public ActionResult Index()
{
return View();
}
}
SectionName is title of content group for you custom
Title is the title of component
Name is used for code behind
Then you can create the views to show in page: (you have to create the views in MVC/Views/ImportCSV, sitefinity will recognize folder name to map in BE)
<h2>Upload File</h2>
<div class="form-group">
<input type="file" id="dataFile" name="upload" />
</div>
<div class="form-group">
<a onclick="upload()" class="button" id="btnupload">Upload</a>
</div>
You need to get access to the code then, controllers\models need to be compiled. You can get away with a lot directly in a cshtml file though which DOESN'T need to be compiled.
Could you download a new blank SF project that's on your version and start from scratch pointed at your DB? Copy over /App_Data and /ResourcePackages to the new project and just run it. Should work fine, but any page that has a custom widget on it that uses custom code would tank. Sorry I'm just not sure why you don't have the code. Could use JustDecompile to retrieve the actual code for custom widgets too I suppose.

asp mvc 4 passing a long string

I currently have a view containing a form. In the form there is a link which allows the user to view one of the textboxes in a separate tab/window.
My question is how do I retrieve the text from that textbox and pass only that text to a controller?
Currently I have the below code
(which returns:
The request filtering module is configured to deny a request where the query string is too long
)
View
#using (Html.BeginForm("PostValuation", "Property", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
....Some data (part of a view model)...
View as letter
....Some data (part of a view model)...
<input type="submit" value="Pass ...Some data... using a View Model" class="btn btn-default" />
}
Controller
public ActionResult ShowLetter(string letter)
{
ViewBag.Letter = letter;
return View();
}
I understand there is a limit on the length of the string I can pass and after reading suggestions I see I can change the web.config to resolve the max length. But this can potentially be thousands of characters long, so I feel that altering the web.config is not the right solution. Can someone offer a better suggestion please.

MVC Partial view with controller, ajax - how do I ge the partial controller to get data?

I'm learning MVC and am stumped by this. I'm trying to factor some common code that gets data and displays it in a WebGrid into a partial view that i can use on multiple pages.
My home controller Index method just does a return View(). The Home view looks like this:
#using (Ajax.BeginForm("SearchAction", "Search",
new AjaxOptions { UpdateTargetId = "data-grid", HttpMethod = "Post" }))
{
#Html.TextBoxFor(model => model.name)
<input type="submit" value="Search" />
}
#{
<div id="data-grid">
#Html.Partial("SearchResults", Model)
</div>
}
I'm trying to use Ajax to avoid losing my search form data when clicking a WebGrid pager link, which are rendered as normal links.
My SearchController looks like this:
public ActionResult SearchAction(string name)
{
return RedirectToAction("SearchResults", new { name = name });
}
public ActionResult SearchResults(string name)
{
//does database query and sticks results in the viewbag
//filter on optional name parameter
VieweBag.Members = MyQueryResults;
return PartialView();
}
My SearchResults shared view, data is passed in via ViewBag.Members:
#{
var grid = new WebGrid(null, rowsPerPage: ViewBag.Pagesize);
grid.Bind(ViewBag.Members);
#grid.GetHtml(// etc. etc.)
}
The results I'm getting is that the ViewBag.Pagesize and ViewBag.Members binding fails since there is no data in the viewbag. Obviously, my partial controller is not being called to do the initial query and put stuff in the ViewBag when the home page is first loaded. How do I make that happen?
The other weird thing is that if I just copy the database query code into my home controller (where it originally was) to force the original query, then if I put some text into the search field and do a search, the partial view renders by itself on a new page. Why is that happening, I thought it would only render as part of my home page.
I've cobbled this partial view together from various answers/places and have no doubt gotten something horribly wrong :\
The partial page won't pass through a controller, but simply render the view directly. If you want to pass view data to the partial view, there is an overloaded function that takes a viewdata dictionary. I'm sorry I can't be more detailed, but I'm on my mobile (waiting for my son to fall asleep in the other room) :)
Update:
If you want to trigger a GET action for your partial view, you can use Html.Action. Here are some useful links:
MSDN RenderAction
Difference between RenderPartial and RenderAction
Further, it would probably make sense for you to move your form tags into your partial view, but those are details for when you clean up the code.
Jonass is right, the ViewBag only propagates between the Controller and the View.
One thing you can do is make the model of the partial view be the same as the type of data you're putting into the ViewBag.
So if for example your MyQueryResults is of type:
IEnumerable<Result>
In your partial view you'd add
#Model IEnumerable<Result>
And then in the main view, you'd pass it through the Render method:
#Html.Partial("SearchResults", ViewBag.Members);
You'll need to tweak this a bit to make sure it's the right type, but this should do the trick.
Good luck!

Passing a GET parameter to ActionLink in ASP.NET

Sorry but I am new to C# and ASP.NET and I saw alot of posts about this problem but I quite didn't get it. I am trying to understand how to pass a GET parameter to an action thru HTML.ActionLink:
here is the the URL:
http://localhost:36896/Movies/SearchIndex?searchString=the
and my CSHTML page should look like this:
<input type="Text" id="searchString" name="searchString" />
#Html.ActionLink("Search Existing", "SearchIndex", new { searchString = "the"})
this hard coded parameter "the" is actually working, but how can I select the input element with id=searchString, with something like document.getElementById("searchString").value
Thanks,
If the value you want to send as GET parameter is not known on the server you cannot use the Html.ActionLink helper to add it. You need to use javascript to manipulate the existing link and append the parameter.
It looks like you have an input field that contains a search string and you want to send the value entered in this field to the server. A better way to handle this scenario is to use an HTML form with method="GET" instead of an ActionLink. This way you don't need to use any javascript - it's part of the HTML specification:
#using (Html.BeginForm("SearchIndex", "Movies", FormMethod.Get))
{
#Html.EditorFor(x => x.SearchString)
<button type="submit">Search</button>
}
Now when you click on the Search button the value entered in the SearchString field will automatically be sent to the SearchIndex action:
http://localhost:36896/Movies/SearchIndex?searchString=the
But if you absolutely insist on using an ActionLink you will have to write javascript to manipulate the href of the existing link when this link is clicked in order to append the value to the url. It's an approach I wouldn't recommend though because the HTML specification already provides you this functionality throughout HTML forms.
This makes the #Html.EditorFor refer to the Title field of the object, kinda in a random way but it works!
#using (Html.BeginForm ("SearchIndex", "Movies", FormMethod.Get))
{
#Html.EditorFor( x => x.ElementAt(0).Title)
<button type="submit">Search</button>
}
Still couldn't pass input parameter to the URL in the GET.
EDIT:
FINAL SOLUTION:
#Html.TextBox("SearchString")
<button type="submit">Filter</button>
and on the controller side, switch the input parameter. Basically it will automatically recognize the passed parameter.
public ActionResult SearchIndex(string searchString)
{
...
}

I need help in uploading a file to a folder on my server and then adding the file details to a database

I am trying to make a page for my site where users can upload a game, give it a name then the file gets uploaded to a folder on my site and the file name and game name get added to my sql database but it gives an error saying: "System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index" when i try to run it.
My code for this page is:
#{
var db= Database.Open("Games");
var sqlQ = "SELECT * FROM Games";
var data = db.Query(sqlQ);
}
#{
var fileName = "";
if (IsPost) {
var fileSavePath = "";
var uploadedFile = Request.Files[0];
fileName = Path.GetFileName(uploadedFile.FileName);
fileSavePath = Server.MapPath("~/App_Data/UploadedFiles/" +
fileName);
uploadedFile.SaveAs(fileSavePath);
}
var GameName="";
var GameGenre="";
var GameYear="";
if(IsPost){
GameName=Request["formName"];
var SQLINSERT = "INSERT INTO Games (Name, file_path) VALUES (#0, #1)";
db.Execute(SQLINSERT, GameName, fileName);
Response.Redirect("default.cshtml");
}
}
<h1 >Add a new game to the database</h1>
<form action="" method="post">
<p>Name:<input type="text" name="formName" /></p>
#FileUpload.GetHtml(
initialNumberOfFiles:1,
allowMoreFilesToBeAdded:false,
includeFormTag:true,
uploadText:"Add")
The error page says that the problem is with line 11 but i don't know what to change.
As Hightechrider has correctly said, your code should reside in Controllers, not in views. The exception you are getting is because Request.Files is an empty array, so when you're trying to access [0] element, you get an IndexWasOutOfRange error.
I'd recommend you reading the following article for uploading files in ASP.NET MVC. It presents an easier, more consistent and flexible model by putting the code in the controller action. Basically, all code boils down to this:
You create an upload form with submit button and file input.
You create an action that accepts HttpPostedFileBase variable.
You must set the proper enctype to your form if you want to upload files:
<form action="" method="post" enctype="multipart/form-data">