MVC Razor Hidden input and passing values - c#

I am pretty sure I am doing something wrong here. I have been developing a web app using MVC and Razor and I never thought of using the form element. Now so much has already been done with master pages and sub pages that it means restructuring most of our code in order to use form element and the would result in multiple form elements on a page.
That aside, in Asp.Net if I wanted to access any control in the C# code behind I could just give it an ID="SomeID" and a RUNAT="SERVER". Then in my code behind I could set its value and properties.
When I do this in Razor, I use lines like:
<input id="hiddenPostBack" runat="server" type="hidden" />
Why can't I access this in the controller? I want to detect a postback and set the value to false if it is the first time the page loads, and if not, then set the value to true. Then based on this, I will read it either server side or client side and do something.
My real question is, how do I "do something" both server side and client side given that I don't have a form element. I was under the impression that if I wanted to pass values from client to server and back, the easiest way to do this is with a hidden input. But I am just not getting how to accomplish this with MVC3 and razor.

A move from WebForms to MVC requires a complete sea-change in logic and brain processes. You're no longer interacting with the 'form' both server-side and client-side (and in fact even with WebForms you weren't interacting client-side). You've probably just mixed up a bit of thinking there, in that with WebForms and RUNAT="SERVER" you were merely interacting with the building of the Web page.
MVC is somewhat similar in that you have server-side code in constructing the model (the data you need to build what your user will see), but once you have built the HTML you need to appreciate that the link between the server and the user no longer exists. They have a page of HTML, that's it.
So the HTML you are building is read-only. You pass the model through to the Razor page, which will build HTML appropriate to that model.
If you want to have a hidden element which sets true or false depending on whether this is the first view or not you need a bool in your model, and set it to True in the Action if it's in response to a follow up. This could be done by having different actions depending on whether the request is [HttpGet] or [HttpPost] (if that's appropriate for how you set up your form: a GET request for the first visit and a POST request if submitting a form).
Alternatively the model could be set to True when it's created (which will be the first time you visit the page), but after you check the value as being True or False (since a bool defaults to False when it's instantiated). Then using:
#Html.HiddenFor(x => x.HiddenPostBack)
in your form, which will put a hidden True. When the form is posted back to your server the model will now have that value set to True.
It's hard to give much more advice than that as your question isn't specific as to why you want to do this. It's perhaps vital that you read a good book on moving to MVC from WebForms, such as Steve Sanderson's Pro ASP.NET MVC.

If you are using Razor, you cannot access the field directly, but you can manage its value.
The idea is that the first Microsoft approach drive the developers away from Web Development and make it easy for Desktop programmers (for example) to make web applications.
Meanwhile, the web developers, did not understand this tricky strange way of ASP.NET.
Actually this hidden input is rendered on client-side, and the ASP has no access to it (it never had). However, in time you will see its a piratical way and you may rely on it, when you get use with it. The web development differs from the Desktop or Mobile.
The model is your logical unit, and the hidden field (and the whole view page) is just a representative view of the data. So you can dedicate your work on the application or domain logic and the view simply just serves it to the consumer - which means you need no detailed access and "brainstorming" functionality in the view.
The controller actually does work you need for manage the hidden or general setup. The model serves specific logical unit properties and functionality and the view just renders it to the end user, simply said. Read more about MVC.
Model
public class MyClassModel
{
public int Id { get; set; }
public string Name { get; set; }
public string MyPropertyForHidden { get; set; }
}
This is the controller aciton
public ActionResult MyPageView()
{
MyClassModel model = new MyClassModel(); // Single entity, strongly-typed
// IList model = new List<MyClassModel>(); // or List, strongly-typed
// ViewBag.MyHiddenInputValue = "Something to pass"; // ...or using ViewBag
return View(model);
}
The view is below
//This will make a Model property of the View to be of MyClassModel
#model MyNamespace.Models.MyClassModel // strongly-typed view
// #model IList<MyNamespace.Models.MyClassModel> // list, strongly-typed view
// ... Some Other Code ...
#using(Html.BeginForm()) // Creates <form>
{
// Renders hidden field for your model property (strongly-typed)
// The field rendered to server your model property (Address, Phone, etc.)
Html.HiddenFor(model => Model.MyPropertyForHidden);
// For list you may use foreach on Model
// foreach(var item in Model) or foreach(MyClassModel item in Model)
}
// ... Some Other Code ...
The view with ViewBag:
// ... Some Other Code ...
#using(Html.BeginForm()) // Creates <form>
{
Html.Hidden(
"HiddenName",
ViewBag.MyHiddenInputValue,
new { #class = "hiddencss", maxlength = 255 /*, etc... */ }
);
}
// ... Some Other Code ...
We are using Html Helper to render the Hidden field or we could write it by hand - <input name=".." id=".." value="ViewBag.MyHiddenInputValue"> also.
The ViewBag is some sort of data carrier to the view. It does not restrict you with model - you can place whatever you like.

As you may have already figured, Asp.Net MVC is a different paradigm than Asp.Net (webforms). Accessing form elements between the server and client take a different approach in Asp.Net MVC.
You can google more reading material on this on the web. For now, I would suggest using Ajax to get or post data to the server. You can still employ input type="hidden", but initialize it with a value from the ViewData or for Razor, ViewBag.
For example, your controller may look like this:
public ActionResult Index()
{
ViewBag.MyInitialValue = true;
return View();
}
In your view, you can have an input elemet that is initialized by the value in your ViewBag:
<input type="hidden" name="myHiddenInput" id="myHiddenInput" value="#ViewBag.MyInitialValue" />
Then you can pass data between the client and server via ajax. For example, using jQuery:
$.get('GetMyNewValue?oldValue=' + $('#myHiddenInput').val(), function (e) {
// blah
});
You can alternatively use $.ajax, $.getJSON, $.post depending on your requirement.

First of all ASP.NET MVC does not work the same way WebForms does. You don't have the whole runat="server" thing. MVC does not offer the abstraction layer that WebForms offered. Probabaly you should try to understand what controllers and actions are and then you should look at model binding. Any beginner level tutorial about MVC shows how you can pass data between the client and the server.

You are doing it wrong since you try to map WebForms in the MVC application.
There are no server side controlls in MVC. Only the View and the
Controller on the back-end. You send the data from server to the client by
means of initialization of the View with your model.
This is happening on the HTTP GET request to your resource.
[HttpGet]
public ActionResult Home()
{
var model = new HomeModel { Greeatings = "Hi" };
return View(model);
}
You send data from client to server by means of posting data to
server. To make that happen, you create a form inside your view and
[HttpPost] handler in your controller.
// View
#using (Html.BeginForm()) {
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.Password)
}
// Controller
[HttpPost]
public ActionResult Home(LoginModel model)
{
// do auth.. and stuff
return Redirect();
}

Related

Singleton Service properties not updated .net core 2 razor pages

When my page loads i fire 2 unobtrusive Jquery requests using the asp.net helpers as seen below
<div id="stdReplace">
<form method="get" data-ajax="true" data-ajax-url="/Stats/Index/QuickChart"
data-ajax-failure="PulseTs.HttpUtilsTs.httpRequestFailed"
data-ajax-success="stdOnComplete"
data-ajax-method="get"
data-ajax-begin="PulseTs.VisualHelpersTs.blockElement(stdReplace)"
data-ajax-update="#stdReplace"
data-ajax-mode="REPLACE-WITH"
class="quickChartForm">
<input type="hidden" name="id" value="std"/>
</form>
</div>
</div>
Which both reach the correct handler and return a partial view to update the parent element they rest in
The handler:
public async Task<IActionResult> OnGetQuickChart(string id)
{
await _serviceStats.GetGeneralStats(routeName, startDate, endDate);
string key = "ssts" + id.First().ToString().ToUpper() + id.Substring(1);
if (string.Equals(id, "avg"))
{
TempData["ChartTitle"] = "avg";
TempData["ChartId"] = "avgChart";
}
else
{
TempData["ChartTitle"] = "std";
TempData["ChartId"] = "stdChart";
}
return new PartialViewResult
{
StatusCode = 200,
ViewName = "Stats/Partial/_QuickChartView",
ViewData = ViewData,
TempData = TempData
};
Now get GetGeneralTtats function should internally update one of the services properties, which when i debug it does. But i also inject this service into my Razor Page View and when i attempt to retrieve that property in javascript it is empty
i inject the service in the razor page as such
#inject IServiceStats ServiceStats
and attempt to retreive the property in js as below
data = #Json.Serialize(ServiceStats.StatsVm.AvgData);
When debugging i see this
But when i console.log(data)
i get this
Why is the property of the singleton service not updated shouldn't it be the same throughout the lifetime of the application?
You've got parts of this happening on the server and parts happening on the client. When your model is provided to the view, and then later set as the value of data in your JS, that's all happening server-side. The data variable doesn't actually even exist, it's just text for all the server is concerned. The serialized value of your model is simply being dumped to the page in that spot. Then, after the response is returned, and the browser (the client) runs the JS on the page, then and only then is data set to something, which is just a JS object at that point.
When you make a request via AJAX, you're able to affect the model on the server, but this has zero impact on what's already been done client-side. If you need to the updated data, you would need to make a request specifically for that to the server, return that data as the response from the server (JSON), and then set the data JS variable with that JSON response.

How can I prevent form resubmission in MVC

My current situation is as follows.
I have a form that when it gets submitted, passes the model into the controller and it does what it needs to do. At the end it redirects to a confirmation page that also gets passed the same model. All of that works fine, except when I am on the confirmation page, whenever I reload the page it resubmits the form.
I've tried using TempData but when I use that it requires for my model to be serializable and my model uses other models inside it that were made by other people that all need to be serializable which would result in like 15-20 different classes all needing to become serializable which just doesnt seem reasonable.
Here is some of what I am working with:
[HttpPost]
public async Task<ActionResult> SubmitClaim(WarrantyClaim model)
{
... code ...
return BeddingWarrantyConfirmation(model);
}
public ActionResult BeddingWarrantyConfirmation(WarrantyClaim model)
{
return View("BeddingWarrantyConfirmation",model);
}
You could make use of the Post Redirect Get Pattern. You could return the following in SubmitClaim:
return RedirectToAction("BeddingWarrantyConfirmation", "CONTROLLER", model);
For more information, please see https://en.wikipedia.org/wiki/Post/Redirect/Get
This is a common problem in MVC development, you can follow the Post/Redirect/Get strategy to avoid the request.
https://en.wikipedia.org/wiki/Post/Redirect/Get

MVC Component with postback

I am trying to build a set of MVC components which can be easily reused with various settings. They are built and used as child actions, because I need them to be able to make partial postback to themselves without the knowledge of the other content of the view on which they are hosted.
I am facing an issue with where to store their parameters without passing them through client (I don't want to build something like view state, and also for security reasons), and make them available to the partial postback.
Here is a simplified example (not that code may not be compilable as I cut my syntax sugar to simplify it to plain MVC):
View code (component usage):
#Html.Action(
"Default",
"FacebookFeed",
new {
// I don't want this data to pass through client
settings = new FacebookFeedSettings {
AppKey = "XYZ",
AppSecret = "123",
PageSize = 10
}
.ItemTemplate(
#<div class="feed-item">#item.Title</div>
)
}
)
Controller code:
public class FacebookFeedController {
public ActionResult Default(FacebookFeedSettings settings)
{
// Action code using settings
return PartialView(model);
}
}
Feed view code:
#using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
// Form code
<input type="submit" value="Refresh" />
}
So when the Refresh button is hit, it is supposed to render fresh data, but settings is missing in that request.
So far I came up only with solution to make some sort of settings register indexed by string key where they would register their settings set.
Modified view code (component usage):
#Html.Action(
"Default",
"FacebookFeed",
new {
settingsKey = "HomePageFBFeed"
}
)
Extra code from user:
[ComponentSettings]
public class HomePageFBFeed : FacebookFeedSettings
{
public HomePageFBFeed()
{
AppKey = "XYZ";
AppSecret = "123";
PageSize = 10;
}
}
Modified controller code:
public ActionResult Default(string settingsKey)
{
FacebookFeedSettings settings = ComponentSettings.GetSettings(settingsKey);
// Action code using settings
return PartialView(model);
}
Modified view code:
#using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }, new { settingsKey = Model.SettingsKey }))
{
...
}
So it this case I pass over the client only some unique ID of that configuration, which is fine, but it has lousy user experience compared to first as it needs to be managed outside of view where the component is placed.
I am also not able to use inline template in this case as shown in the first code part, because in this case settings is built outside the scope of a view.
Note that I also need this to work with application restarts, and across process boundaries (in cloud), so I can't rely on storing the configuration on server side at the first load of the view.
Is there some better way / best practice how to do that in ASP.NET 4.6 / MVC 5?
If not, will it be possible in ASP.NET 5 / MVC 6?
Martin, I know you said you don't want to create a view state, but considering the disconnected mode of MVC (you don't want to use server-side Session, also because it doesn't scale), would you be open to transfer an encrypted string of the settings?
Something like this in the view:
#using (Ajax.BeginForm("Default", "FacebookFeed", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.ReplaceWith }))
{
#Html.AntiForgeryToken()
<input type="hidden" name="Settings" value="#Model.ToEncryptedString()" />
<input type="submit" value="Refresh" />
}
The ToEncryptedString() would be an extension on your model, that:
Serialize your settings (the FacebookFeedSettings object)
Encrypt it
Convert to Base 64 to make it HTTP friendly
When going back to the controller, all you need to do is to read the Settings parameter in the Default action:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Default(string settings)
{
FacebookFeedSettings facebookSettings = FacebookFeedSettings.FromEncryptedString(settings);
// do something with the settings
// build the model
return PartialView(model);
}
The FromEncryptedString() does exactly the opposite direction:
Convert from Base 64 to byte[]
Decrypt the byte array
Deserialize to a FacebookFeedSettings object instance
Basically, this encrypted string works more or less like the anti-forgery token.
To make it even more elegant, I guess you can move the settings validation also at attribute level, and mark your action with a custom attribute:
[HttpPost, ValidateAntiForgeryToken, FacebookFeedSettings]
public ActionResult Default(/* No need to capture settings here */)
The FacebookFeedSettingsAttribute would obtain the Settings parameter from the Request and the build and validate the FacebookFeedSettings object instance.
I haven't tried going this far in my try, I leave it to you to practice :-)
What do you think?
I understand and I agree, the problem is that you'd like to keep some form of state anyway, in a stateless technology.
Good news is that MVC 6 seems to have the answer to your problem. First of all, child actions do not exist any longer, replaced by View Components.
View Components are made of a C# class and a Razor view, and do not have a dependency on a Controller, which eases reusability.
I do not have a direct experience yet, but from what I am reading about MVC 6 and specifically View Components, they are self-contained, so basically you can manage state within the components itself. Parameters are not passed over HTTP from the view, because you actually invoke the component server-side (no trip back from the client).
Also, View components don’t take part in the controller lifecycle but you still have access to ViewBag and ViewData (shared with the Controller). Lasty, like controllers, View Components also take part in dependency injection so any other information you need can simply be injected to the view component.
This should work for you, but you need to wait for MVC 6 :-)

Should I use WebApi for this? ASP.net MVC

I have a question about MVC asp.net
I have a link that provides a data using HTTP in this XML format
[...]
<Item>
<Name>Money</Name>
<Unit>1000</Unit>
</Item>
[...]
If I want this to display data on view in my application - what should I use? WebAPI?
The data ( < unit > ) change every few minutes, so always view have to display the current data.
Data are also possible to get in JSON format.
To clear up the confusion from all existing answers and comments: your actual problem statement is this:
I have a third-party URL that when requested, gives you some JSON which I wish to display in a table on an MVC view.
This is very trivial. See Deserializing JSON into an object to generate classes to deserialize the JSON. This provides you with a statically typed class that you can use from code.
Then you define a view model to hold a list of items:
public class JsonViewModel
{
public List<JsonItem> Items { get; set; }
}
public class JsonItem
{
public string Name { get; set; }
public string Unit { get; set; }
}
And in the controller you perform an HTTP GET request to retrieve the JSON (HTTP GET request and XML answer), parse it, map it to your view model and return it to your view:
public class FooController : Controller
{
public ActionResult Index()
{
// 1. Perform HTTP request to retrieve the JSON.
var webClient = new WebClient();
string rawJson = webClient.DownloadString("json-url");
// 2. Parse the JSON.
var jsonRootObject = JsonConvert.DeserializeObject<JsonRootObject>(rawJson);
// 3. Map to your viewmodel
var viewModel = new JsonViewModel
{
Items = jsonRootObject.Items.Select(i => new JsonItem
{
Name = i.Name,
Unit = i.Unit
}).ToList()
};
// 4. Return the model to your view
return View(viewModel);
}
}
Then finally you render the model in your view:
#model JsonViewModel
<table>
<tr>
<th>Name</th><th>Unit</th></tr>
</tr>
#foreach (var item in Model.Items)
{
<tr>
<td>#item.Name</td><td>#item.Unit</td>
</tr>
}
</table>
In your controller, call the external service to get the data. Can be XML, but JSON is more lightweight; I'd go for that. Parse the data into a view model that you then pass to the view. The view model contains the parsed information in the format that is best suited for your view; this will make sure your view can be kept as simple as possible (focusing on view logic).
To keep your controllers light, you might want to decide to move the retrieval and parsing logic in dedicated components, and use those from within your controller.
Basically, what to use - JSON or View - is fully up to you and depends on your knowledges.
You can reach what you need here using JSON(or even XML) and js code in the page, or partial view and less js code.
I'd suggest you to return partial view from your MVC controller and update whole block.
You can simply use a periodic ajax call using setInterval to the 3rd party endpoint and then update your view accordingly.
A better approach would be to use MVVM so you just need to update the view model and the framework will do the rest.
If you want to decouple your site from the 3rd party or you just want to hide that 3rd party address, You can use Web Api to call the 3rd party endpoint and call the web api endpoint from the client.
You will need to handle CORS this way, if the web api is in a different domain.
You can also use mvc action instead of the web api but if you're planning on building large scale application. It's better that you start using SOA Architecture.
I prefer Json instead of Xml because it is lightweight, so... you can go through three different ways:
Create a method that returns a JsonResult in your Controller (that is what i think you are doing) inside your asp.net mvc application;
Create a WebApiController inside your asp.net mvc application just to have the web api stuff without creating a new application;
Create a Web Api application and return your data (if you need only one service, i don't think you should create a new app for that, it is too much work)
If you choose the third option and you are not familliar with Web Api, i will recommend you to read this.

How to implement custom controllers in Umbraco 6

I am having trouble getting my custom controllers to behave properly in an Umbraco MVC site. I can't work out how to pass custom models and use custom ActionResults. When I try to send a form the method is not being called. So a method like this
public ActionResult Home(string username, string password)
{
Debug.WriteLine("form");
var company = new Company();
return CurrentTemplate(company);
}
that should be called from a form like this but nothing happens.
#using (Html.BeginForm("Home","Login", FormMethod.Post, new {name = "logon"}))
{
<label>User name </label><input type="text" id="username"/><br/>
<label>Password </label><input type="password" id="password"/><br/>
<input type="submit" value="Here"/>
}
I know it is possible to override the default controller but is there any point in doing so?
Old post, but I thought I'd add my findings from a current project I'm working in.
For posts, as you're referring to in your form, you'd need a SurfaceController for most instances as #Digbyswift said.
HOWEVER - this is where I had the most trouble. I have a form that posts to another page, which displays results, and then displays that data on a view. I wanted to get away from using query strings and putting data in session, etc, since we're using MVC. This also allowed me to return a custom view that also used CurrentPage, which you need to have RenderModel as the model. We're going to need to set up a few things:
1) Create your controller, view, and document type. In this instance, I was dealing w/ "members", so I created a Member doc type, MemberController, etc.
2) On the view that's posting to the form:
<form method="post" action="member">
<div class="form-group">
<input type="text" id="query" name="query" class="form-control" placeholder="Member Company or State" />
</div>
<input type="submit" value="Search" class="button button-green" />
</form>
You could also define a custom route in a class that inherits from the ApplicationEventHandler class and register the route in the ApplicationStarted event, but this way we'll just override the Index action.
3) On the controller, MemberController
public class MemberController : RenderMvcController
{
[EnsurePublishedContentRequest(2303)] // 2303 is your node ID
public ActionResult Index(string query)
{
// your code - assign to ViewBag
return View("~/Views/Member.cshtml", this.CreateRenderModel(Umbraco.TypedContent(2303)));
}
}
private RenderModel CreateRenderModel(IPublishedContent content)
{
var model = new RenderModel(content, CultureInfo.CurrentUICulture);
//add an umbraco data token so the umbraco view engine executes
RouteData.DataTokens["umbraco-doc-request"] = model;
return model;
}
You DO NOT have to do this if you do not have any macros that need to rendered. In my case I had this view that inherited a _layout.
Custom MVC Routes in Umbraco
We have to do (2) things here. One is to make sure that we get the PublishedContentRequest.
The second part is to get the RenderModel object. I read a lot of articles in which your base model should inherit from RenderModel, and add in some default Umbraco constructors, but I didn't have very much luck with this.
Hope this helps somebody.
In Umbraco, every request is routed through the Umbraco.Web.Mvc.RenderMvcController but you can override this and the documentation is here.
However, I would suggest that if you feel you need to do this then you are possibly over-complicating your implementation. You can still use your approach to render ChildActions which can be given a model independent of the Umbraco page model. See here. This is great for things like rendering paged search results, document listings and content you want to be able to control in a controller. I use this approach a lot but always try and pass back a model centered around the IPublishedContent interface (e.g. IEnumerable<IPublishedContent> for a page listing), that way in the View, you can still have access to the Umbraco content and API from the View instead of having to implement too many of your own properties in a model.
When it comes to posting forms it's a little more tricky because you have two options:
As Dan Diplo says, you can use the Html.BeginUmbracoForm() approach; or
You can post back to a [HttpPost] action in the standard MVC way, i.e. Html.BeginForm().
The challenge with (2) is that because all requests are passed through Umbraco.Web.Mvc.RenderMvcController, you cannot tell a form which page to post to. You can only post to itself or a non-Umbraco-controlled action. You could for example let the form post back to the same page and have a second ChildAction specifically for the [HttpPost]. The issue with this is that it would catch all posts regardless of the form being posted from.
Personally, I use approach (1) in most standard forms where I need to interact with Umbraco directly, e.g. enquiries, uploads etc. and I use approach (2) when I need more control. But this generally needs a lot more thought.
Do you really mean you want a custom controller, or do you actually just want to create a form in Umbraco using MVC? If it's the latter then you need to use a surface controller ie. ensure your controller inherits from Umbraco.Web.Mvc.SurfaceController and has the suffix 'SurfaceController'.
public class MySurfaceController : Umbraco.Web.Mvc.SurfaceController
{
public ActionResult Index()
{
return Content("hello world");
}
}
You then need to use the custom Umbraco Html helper to create your form tags:
#using(Html.BeginUmbracoForm("CreateComment", "BlogPostSurface"))
{
//
}
See http://our.umbraco.org/documentation/Reference/Mvc/forms

Categories

Resources