Controller does not receive value from view (implementing search function) on MVC - asp.net

I'm trying to make a page with a search function. When you first go to the page, all you have is a search bar. When you type something into the search bar and click the "submit" or "search" button, I want the value in the search box to be submitted to the controller as a string. Then, this value can be used in returning the a model back to the page. Here's what I have so far:
Search.cshtml
#{
ViewBag.Title = "Search";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#ViewBag.PageTitle
<h1>Search All Issues</h1>
<form asp-controller="Report" asp-action="Search" method="get">
<input name="searchstr" id="Search" />
<input type="submit" value="Submit" />
</form>
ReportController.cs
[HttpGet]
public ActionResult Search(string test)
{
ViewBag.PageTitle = test;
var report = _context.Reports.ToList();
return View(report);
}
What am I doing wrong here? For now, it would be nice if I can just get that ViewBag.PageTitle to appear on the page. If I can do that, then I can return the report model just the way I want.
Also, a few additional questions. Some of the stuff I've seen on stack overflow has a recommendation to do a Post in the controller. If I do that, the page errors out. Why is a get needed for this? Intuitively, it makes more sense to me to use a Post...

Turns out I was extremely close; Stephen Muecke had the correct answer. I simply needed to change my input name or parameter name.
I made this mistake as I was considering this from a C++/C standpoint where the parameter names can be irrelevant. I see now, though, that with ASP.NET MVC, you need to have the input name match the parameter name.
Thanks for the help!

Related

How to make a search for item and show the result in seperated page [ASP MVC]

I just want to make a search for my site , and before I start work on that I just want to sort my work steps .
My site type is books site . and here is what I want to know :
I have a search box in my navbar .
when someone entered a book name to search I want to show the result of this
search in this page :
Localhost/Search?string=C++
How I can call the method that will process the search from the view
?
How should the controller method looks like , also the view page.
How to make a paging to the search page ?
I don't need a code , I just want from someone to help me with a list of steps I should do , to make the search as I requested and thanks a lot guys ....
You can keep your input box for search inside a form and keep the form's method value to be GET.
#using (Html.BeginForm("Search", "Students", FormMethod.Get, null))
{
<input type="text" name="searchTerm" />
<input type="submit" />
}
Now you should have an action method called Search inside your StudentsController which accepts a string value in a parameter named searchTerm.
public ActionResult Search(string searchTerm)
{
// use searchTerm variable to get data and pass to view
return View();
}
Now in your view (~/Views/Students/Search.cshtml), you can use the passed data to display the search results.
For paging, you may add another parameter to your action method which you will use to get a specific sub set of the data.
public ActionResult Search(string searchTerm,int page=1,size=10)
{
// use searchTerm variable to get data and pass to view
// page number is in page variable
// size is in size variable
return View();
}
Now you need to adjust your view to show all the page numbers and link to the same action method and pass the page number in url like
/Students/Search?searchTerm=Java&page=2&size=25

TextBoxFor producing a blank field

I have a weird problem in my MVC app.
When the user selects a date from a drop down, it clears the StartDate and EndDate fields.
I have the following fragment of code:
<label>Start date: #Model.StartDate</label>
#Html.TextBoxFor(s => s.StartDate)
The weird thing is that you can wee where I'm outputting it in the label, the date comes out there. The textbox is unpopulated.
I've checked the produced markup and the textbox is not being populated.
<label>Start date: 19/05/2013</label>
<input id="StartDate" name="StartDate" type="text" value="" /> <br />
What am I missing here?
To add a little bit more information, when the page is initially populated the default start and end date are output. There is a bit of jQuery that empties those fields when a <select> is changed. If I comment that bit out then the fields retain their previous values as opposed to blank. Essentially, whatever is submitted to the server is output rather than the value in the model.
Essentially, whatever is submitted to the server is output rather than the value in the model.
This behaviour is actually by design. The idea being that generally the user would expect to see in the text box what they submitted to the server.
See here for a detailed explanation, and a work around.
Instead of doing this
<label>Start date: #Model.StartDate</label>
#Html.TextBoxFor(s => s.StartDate)
You should do this
<label id="someId"></label>
#Html.TextBoxFor(s => s.StartDate,new{#id="startdate"})
and using jquery on change event on your textbox you can set lablel
$("#startdate").change(function(){
var date="Start Date:"+$(this).val();
$("#someid").html(date);
});
Thinks that your model is a class named What like this:
public class What
{
public string StartDate { get; set; }
}
Then, think that your application is "MyApplication", you need to add to the view as if the view is stronglytyped:
#using MyApplication.Models;
#inherits System.Web.Mvc.WebViewPage<What>
Then all should we run as you expect

Orchard and master detail editing

I was reading http://www.orchardproject.net/docs/Creating-1-n-and-n-n-relations.ashx and could not get the idea, if it is possible to easily make master detail editing, to give you concrete example i've attached screenshot from wordpress:
So there is post and post contains set of custom fields, simple 1:N relationship, everything edited in one page - you can add/edit custom field without leaving post page.
May be someone saw similar example for Orchard on internet, or could shortly describe path to achieve this by code, would be really helpful (I hope not only for me, because this is quite common case I think).
This should be possible, although not in the most 'Orchardy' way.
I've not tested any of the below so it is probably full of mistakes - but maybe Bertrand or Pszmyd will be along later today to correct me :-)
As you have probably seen you can pass a view model to a view when creating a content shape in your editor driver:
protected override DriverResult Editor(CatPart part, dynamic shapeHelper)
{
// Driver for our cat editor
var viewModel = new CatViewModel
{
Cats = _catService.GetCats() // Cats is IEnumerable<Cat>
};
return ContentShape("Parts_CatPart_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/CatPart",
Model: viewModel,
Prefix: Prefix
));
}
So we can pass in a list of items, and render it in our view like so:
#foreach(var cat in Model.Cats)
{
<span class="cat">
<p>#cat.Name</p>
<a href="...">Delete Cat</p>
</span>
}
The problem here would be posting back changes to update the model. Orchard provides an override of the Editor method to handle the postback when a part is edited, and we can revive the viewmodel we passed in the previous method:
protected override DriverResult Editor(CatPart part, IUpdateModel updater, dynamic shapeHelper)
{
var viewModel = new CatViewModel();
if (updater.TryUpdateModel(viewModel, Prefix, null, null))
{
// Access stuff altered in the Cat view model, we can then update the CatPart with this info if needed.
}
}
This works really well for basic information like strings or integers. But I've never been able to get it working with (and not been sure if it is possible to do this with) dynamic lists which are edited on the client side.
One way around this would be to set up the buttons for the items on the N-end of the 1:N relationship such that they post back to an MVC controller. This controller can then update the model and redirect the client back to the editor they came from, showing the updated version of the record. This would require you to consistently set the HTML ID/Name property of elements you add on the client side so that they can be read when the POST request is made to your controller, or create seperate nested forms that submit directly to the contoller.
So your view might become:
#foreach(var cat in Model.Cats)
{
<form action="/My.Module/MyController/MyAction" method="POST">
<input type="hidden" name="cat-id" value="#cat.Id" />
<span class="cat">
<p>#cat.Name</p>
<input type="submit" name="delete" value="Delete Cat" />
</span>
</form>
}
<form action="/My.Module/MyController/AddItem" method="POST">
<input type="hidden" name="part-id" value="<relevant identifier>" />
<input type="submit" name="add" value="Add Cat" />
</form>
Another possibility would be to create a controller that can return the relevant data as XML/JSON and implement this all on the client side with Javascript.
You may need to do some hacking to get this to work on the editor for new records (think creating a content item vs. creating one) as the content item (and all it's parts) don't exist yet.
I hope this all makes sense, let me know if you have any questions :-)

Rendering a spark view to a string

I'm trying to render a partial view as a string so it can be returned as HTML to a jquery ajax call. After a lot of searching I found this code.
public string RenderAsString(string viewName, string modelName, object model)
{
// Set up your spark engine goodness.
var settings = new SparkSettings().SetPageBaseType(typeof(SparkView));
var templates = new FileSystemViewFolder(Server.MapPath("~/Views"));
var engine = new SparkViewEngine(settings) { ViewFolder = templates };
// "Describe" the view (the template, it is a template after all), and its details.
var descriptor = new SparkViewDescriptor().AddTemplate(#"Shared\" + viewName + ".spark");
// Create a spark view engine instance
var view = (SparkView)engine.CreateInstance(descriptor);
// Add the model to the view data for the view to use.
view.ViewData[modelName] = model;
// Render the view to a text writer.
var writer = new StringWriter(); view.RenderView(writer);
// Convert to string
return writer.ToString();
}
But when the following line executes:
var view = (SparkView)engine.CreateInstance(descriptor);
I get the following error:
Dynamic view compilation failed. An
object reference is required for the
non-static field, method, or property
'DomainModel.Entities.Region.Id.get.
This is my partial view:
<ViewData Model="Region" />
<div id="${ Region.Id }" class="active-translation-region-widget" >
<label>${Region.RegionName}</label>
${ Html.CheckBox("Active") }
</div>
It doesn't seem to recognise the model.
P.S. When I call the view from a parent view like so
<for each="var region in Model">
<ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>
It renders perfectly. What am I doing wrong?
Just from looking at it, I think the following line is the problem:
<ViewData Model="Region" />
Instead it should read:
<viewata model="Region" />
Note the lower case "model". This is because model is a special case since behind the scenes it performs a cast to a strongly typed viewmodel. The top one will define a variable called Model in the generated view class and assign the value Region to it. Using the lowercase option below will actually create a Model variable, but also cast it to strongly typed instance of Region that comes from the ViewData dictionary.
Note When using Model in the code though, like you did in the for each loop, it needs to be upper case which is correct in your case. Once again, this is the only special case because it's pulling a strongly typed model from the ViewData dictionary.
One other thing - <viewata model="Region" /> must be declared in the parent view, and it can only be defined once per page, so you cannot redefine it in a partial view. If it's a partial view, you should rather use it by passing in a part of the model like you have done in your second example above.
The reason for your exception above is because it's trying to get the Id property as a static item off the Region Type, rather than querying the Id property on your instance of Region as part of your viewmodel.
As a side note, the code to get where you want is a little mangled. You can find neater ways of doing what you want by checking out some of the Direct Usage Samples, but I understand this was probably just a spike to see it working... :)
Update in response to your follow up question/answer
I'm fairly sure that the problem is with passing the Region into the following call:
<ActiveTranslationRegion Region="region" if="region.Active==true">
... is again down to naming. Yes, you can only have one model per view as I said before, so what you need to do is remove the following from the top of your partial:
<viewdata model="Region" />
That's what's causing an issue. I would then rename the item going into your partial like so:
<ActiveTranslationRegion ActiveRegion="region" if="region.Active==true">
and then your partial would look like this:
<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
<input type="hidden" name="Id" value="${ActiveRegion.Id}" />
<label class="region-name">${ ActiveRegion.RegionName }</label>
<input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>
Note I'm using ActiveRegion because in the Spark parser, ActiveRegion gets declared as a variable and assigned the value of region in the current scope as you loop through the for loop. No need to stick religiously to the model - because you've gone and passed in a piece of the model now that you've declared as ActiveRegion. Oh, and you could stick with the name Region if you really want, I just changed it to make a point, and because you've got a Type called Region in your code and I'm not a big fan of the quirky issues using the same name for a variable as a type can bring about. Plus it makes it a little clearer.
The disadvantage of calling the Html.RenderPartial method is not immediately obvious. One thing you lose is the 3-pass rendering that Spark provides. If you use the tag syntax (which is preferable) you'll be able to stack partials within partials to multiple levels down passing variables that feed each partial what they need down the stack. It gets really powerful - start thinking data grid type structures where rows and cells are individual partials that are fed the variables they need from the model, all kept nice and clean in separate manageable view files. Don't stop there though, start thinking about targeting header and footer content base on variables or three column layouts that create a dashboard that renders all sorts on individually stacked partials many levels deep.
You lose all of that flexibility when you use the bog standard ASP.NET MVC Helper method Html.RenderPartial() - careful of doing that, there's more than likely a solution like the one above.
Let me know if that works...
All the best
Rob G
I refactored the code and views quite a bit. In the end all I'm really trying to acheive is have a parent view (not shown) iterate over an IEnumerable and for each iteration render a partial view (ActiveTranslationRegion) which renders some Html to represent a region model.
I also want to be a able to call an action method via an ajax call to render an indivual ActiveTranslationRegion partial view by passing it an individual region model. I've refactored the code accordingly.
Partial view (_ActiveTranslationRegion.spark)
<viewdata model="Region" />
<form action="/Translation/DeactivateRegion" class="ui-widget-content active-translation-region-widget">
<input type="hidden" name="Id" value="${Model.Id}" />
<label class="region-name">${ Model.RegionName }</label>
<input class="deactivate-region-button" type="image" src=${Url.Content("~/Content/Images/Deactivate.png")} alt="Deactivate" />
</form>
Notice by using I can refer to Model within the view as RobertTheGrey suggested (see above) .
I removed all the code to return the view as a string and simply created an action method method that returned a partialViewResult:
[UnitOfWork]
public PartialViewResult ActivateRegion(int id)
{
var region = _repos.Get(id);
if (region != null)
{
region.Active = true;
_repos.SaveOrUpdate(region);
}
return PartialView("_ActiveTranslationRegion", region);
}
One thing I had to do was amend my parent view to look like so:
<div id="inactive-translation-regions-panel">
<h3 class="ui-widget-header">Inactive Regions</h3>
<for each="var region in Model">
<span if="region.Active==false">
# Html.RenderPartial("_InActiveTranslationRegion", region);
</span>
</for>
</div>
Where previously I had the following:
<div id="inactive-translation-regions-panel">
<for each="var region in Model">
<ActiveTranslationRegion Region="region" if="region.Active==true"></ActiveTranslationRegion>
</for>
</div>
Notice I have to call the Html.RenderPartial rather than use the element. If I try and use the element (which I would prefer to do) I get the following error:
Only one viewdata model can be declared. IEnumerable<Region> != Region
Is there a way round that problem?
Update:
I tried your recommendation but with no luck. To recap the problem, I want to use the partial in 2 different situations. In the first instance I have a parent view that uses a model of IEnumerable<Region>, the partial simply uses Region as its model. So in the parent I iterate over the IEnumerable and pass Region to the partial. In the second instance I want to call PartialView("_ActiveTranslationRegion", region) from an action method. If I remove the <viewdata model="Region" /> from the partial I get an error complaining about the model. The best way round the problem I have found is to add a binding to the bindings.xml file:
<element name="partial"># Html.RenderPartial("#name", #model);</element>
(Note: It seems very important to keep this entry in the bindings file all on te same line)
This way I can still call the partial from the action method as described above and pass it a Region as the model, but I can also replace my call to Html.RenderPartial in the parent view with a more 'html' like tag:
<partial name="_ActiveTranslationRegion" model="region" />
So my parent view now looks more like this:
<div id="inactive-translation-regions-panel">
<h3 class="ui-widget-header">Inactive Regions</h3>
<for each="var region in Model">
<span if="region.Active==false">
<partial name="_ActiveTranslationRegion" model="region" />
</span>
</for>
</div>
Of course under the hood its still making a call to
# Html.RenderPartial("_ActiveTranslationRegion", region);
But its the best solution we could come up with.
Regards,
Simon

Passing hidden field from one page to another in querystring

I want to pass a query in a hidden filed from 1 page to another by querystring.
Can anyone help me out with the logic?
It's worth taking the time to learn jQuery. It's not very complicated, and it makes writing javascript much easier. There are also many jQuery plugins, such as jquery.url.
Also, as other posters have suggested, you may not wish to put the hidden field's value in the query string if you care about it being displayed to the user. However, if the data is present in a hidden field it will always be possible for a user to find it if they care to look.
If you really do want to put the hidden field in the query string and then extract it via non-jQuery javascript:
hiddenFieldPage.aspx
This form will take the user to processingPage.aspx?datum=someValue when it is submitted. You could probably also just use an ordinary link if nothing else needs to be submitted at the same time.
<form method="GET" action="processingPage.aspx">
<input type="hidden" name="datum" value="someValue">
<input type="submit">
</form>
or, inserting the value from code-behind:
RegisterHiddenField("datum", "someValue");
processingPage.aspx
This script will pop-up an alert box with the value of "datum" from the URL - assuming the form's method is set to "GET":
<script type="text/javascript">
function getUrlParam( key ) {
// Get the query and split it into its constituent params
var query = window.location.search.substring(1);
var params = query.split('&');
// Loop through the params till we find the one we want
for( var i in params ) {
var keyValue = params[i].split('=');
if( key == keyValue[0] ) {
return keyValue[1];
}
}
// Didn't find it, so return null
return null;
}
alert( getUrlParam("datum") );
</script>
If the form's method was set to "POST" (as it usually would be in ASP.NET), then "datum" won't be in the query string and you'll have to place it on the page again:
RegisterHiddenField( "datum", Request.Form["datum"] );
To retrieve the hidden value on the second page:
var datum = document.Form1.item("datum").value;
alert( datum );
You can easily submit a form on one page that points to another page using the action parameter. For instance, inside of page1.aspx put the following:
<form action="page2.aspx" method="GET">
<input type="hidden" name="username" value="joenobody" />
<input type="submit" />
</form>
Since you're using "GET" as the method instead of "POST", you could potentially use Javascript to parse the URL and get the value that was passed. Alternatively, you could use ASPX to store the value of the "username" field somewhere else on the page. I don't know ASPX (or ASP, or anything Microsoft really), but if you can find a way to output something like the following (and are using jQuery), it may do what you require. Honestly though, it sounds like you are going about something all wrong. Can you modify your question to be a bit more specific about what the general object is that you are attempting to accomplish?
<div id="some_div"><%= Request.form("username") %></div>
<script type='text/javascript'>
var value_needed = $('#some_div').html();
</script>
<form method="get">
Assuming you mean hidden in the HTML form sense, your field will be submitted along with all the other fields when the form is submitted. If you are submitting via GET, then your "hidden" field will show up in plain text in the URL. If you don't want the data in the hidden field to be accessible to users, don't put an understandable value in that field.
If you are using aspx, you do not need to parse the query string using JavaScript, or even use <form method="GET" ...>. You can POST the form to the second aspx page, extract the value in C# or VB then write it to a client-side JavaScript variable. Something like this:
page1.aspx:
<form method="POST" action="page2.aspx">
<input type="hidden" name="myHiddenServerField" value="myHiddenServerValue">
<input type="submit">
</form>
page2.aspx:
<script type="text/javascript">
var myHiddenClientValue = '<%= Request.Form['myHiddenServerField']; %>';
</script>
The above would set the client-side JavaScript variable called myHiddenClientValue to a value of 'myHiddenServerValue' after the POST.
This can be a bad idea because if myHiddenServerField contains single quotes or a newline character, then setting it on the client in page2.aspx can fail. Embedding ASP.NET Server Variables in Client JavaScript and Embedding ASP.NET Server Variables in Client JavaScript, Part 2 deals with specifically these issues, and solves them with a server-side class that ensures values being written to the client are escaped correctly.
If you use method="get" on an HTML form then any hidden inputs in that form will be converted to query parameters.
See also Jeremy Stein's answer.

Resources