Keep partial form data when switching localization? - c#

I used this great guide to localize my ASP.NET MVC 2 application, which I followed almost to the letter. The app is mainly form based.
I was wondering if there was an easy way to be able to switch between languages in the middle of filling out a form without clearing the whole form, and having to start over? If not, could you suggest a way of localizing an application that would support this?
Maybe that's not a thing...

The approach used in the article is not the best one to keep the localisation I guess.
But what you can do is the following:
Handle event when user clicks on the language link.
Change language via ajax preventing browser to go to the actual link.
Submit the form that user is editing adding parameter saying "make sure you don't save".
The server would re-render the form as normally with the data posted, but in the new language.
JavaScript pseudocode:
var submitCurrentForm = function() {
$("form:last").submit({
data { dontSave: "True"} // this is just meta, you can use QueryString or hidden input
});
}
var switchLanguage = function(href, done) {
$.post(href).success(done); // using jQuery deferred
}
$("a.lang").click(function(e) {
e.preventDefault();
switchLanguage(this.href, submitCurrentForm);
});
Controller pseudocode:
public ActionResult Create(YourStuff stuff, bool dontSave = false) {
if (!dontSave)
ProcessTheStuff();
return View(stuff);
}
Not the best solution, but the easiest one you can go with ATM.

Related

Keeping state of component after refresh

Hello i am wondering how can you keep state of a component variable in Blazor without using different pages.
Lets say i have a Parent page that has a Login component :
Login
#functions()
{
public Action<string>onlogin{get;set;} //some logic that triggers this event to the parent , ---not important
}
Main
#if(this.id==string.Empty)
{
<Login onlogin="#((value)=>OnLoginEvent(value))"></Login>
}
#functions()
{
public string id{get;set;}=string.Empty;
public void OnLoginEvent(string value)
{
this.id=value;
}
Now as you can see ,the first time i render the Main component and i log in , the id will become be different from string.Empty, and so the Login component will then disappear.
However if i refresh the page , i am still back with the login component rendered ,so the id is resetted.
Can i somehow keep the state of a Component variable even after refresh ? In this case i want to keep id from being string.Empty even after refresh?
P.S I do not want to use different pages , and i was thinking as a solution to store the value of id in a service that is injected.Is there any other way?
A service would be a good way to solve your problem, as you've concluded. But another option would be to use the browsers local storage. This would persist your data in a very robust way. You can use a library such as this one BlazoredLocalStorage (Full disclosure, I'm the author).
This method would persist the data outside of a users session though, so, for example, a user could close the browser and come back a day later and the data would still be present. So you would have to manage that. But this is how many SPA applications handle this problem.

ASP.NET MVC form wizard prevent user skipping wizard step (Controller Action) using URL bar

I’m currently working on a very large form. I thought it would be a good idea to section the form by implementing a form wizard (multiple views/action results) to improve the user experience.
One requirement is that the user must complete a small eligibility test that ensures they meet the minimum requirements prior to starting the application wizard itself.
Having done nothing like this before I can only see one approach to this problem and that is using a flag e.g IsEligible in a session that determines if the user can access the form wizard view/s. Let’s say my controller has two ActionResults (Eligibility and WizardStepOne) that server separate views. For example:
Controller - Untested Code
public ActionResult Eligibility()
{
Return View();
}
[HttpPost]
public ActionResult Eligibility(EligibilityViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
Session["IsEligible"] = true;
return("Success");
}
public ActionResult WizardStepOne()
{
bool stuff = (bool)Session["stuff"];
if(IsEligible == null)
{
return("Eligibility");
}
return RedirectToAction("Eligibility");
}
In short if the user attempts access the first step of the wizard and the IsEligible flag hasn't been set by the Eligibility post action then the user get redirected back to the eligibility form.
I've also looked into action filters but couldn't make much sense of it. I'll also have to implement this functionality at a later stage to prevent users skipping between wizard steps using the url e.g skipping WizardStepOne and starting at WizardStepTwo.
Is there a better approach then one described above in this situation? I'd rather avoid using a session as restarting the form becomes problematic as it would require a restart button to abandon the session especially between wizard steps.
What you are mentioning sounds like you might make use of some simple state machine to define valid steps through your form. So, when user is at stage 3. for example, you will check if he came from step 2 or not. For each step for particular user and session > you can save some hash value in his session. That way they could make no use typing in url directly.

How do I check to make sure the customer has done a task before they visit any page

I am using the Membership provider on a MVC Razor website.
After the customer creates an account and then logs in, I need to make sure they add a system account(s) to their login otherwise the following pages will have issues.
Here is what my page looks like:
As you can see, they can click on any of the tabs at the top and circumvent this screen.
What is the best way to handle this?
Should I disable the tabs? If so, where would I disable them? Or should I do a check on each page and redirect them back to this page?
Thanks for the help!
You should check whether that information has been entered before loading any other page. (probably in an action filter)
If you just disable the links, malicious users can navigate directly to the URLs.
Use RedirectToAction() if they have not created one e.g.
public ActionResult OrderGas(){
// do check here to see if they have system account
if (!hasSystemAccount()){
// this will re-display the add another account page each time
return RedirectToAction("AddAnotherAccountAction");
}
//other wise continue;
return view("OrderGas");
}
Alternatively you can use javascript to hide the buttons depending on a model
public ActionResult OrderGas(){
// do check here to see if they have system account
Boolean hasAccount = this.hasSystemAccount();
// apply to the model
model.hasSystemAccount = hasAccount;
return view("OrderGas", model);
}
Then your jquery can check this value and hide the links accordingly
if($('#hasSystemAccount').val().toLower() = "false"){
$('.myLinkClass').hide();
}
Have you considered using an additional Membership ROLE which indicates a customer has added the 'system account' information, such as CustomerWithSystemAccountInfo.
Then you can protect the controller actions with [Authorize(Roles = "CustomerWithSystemAccountInfo")]
As far as hiding or disabling the menu options in _layout based on ROLE the following article presents a way of doing it that I quite like:
http://techbrij.com/981/role-based-menu-asp-net-mvc

How can I access attributes of Html elements in ASP.NET MVC

With ASP.NET Webforms, I could drag and drop a control on a form and be able to access its properties:
if (number == 0)
AddButton.Enabled = false;
Although I cannot drag and drop a control on a ASP.NET MVC View template, can I change its attributes?
For instance:
disable a button in some conditions and be able to enable it if conditions change.
Be able to change the text of a button from "Next->" to "Finish"
etc.
There are (at least) two Methods to do this:
Method 1: The Simple Way
You would do this by adding logic in your view:
<input type="button" disabled=<%= Model.number <= 0 %> />
Where Model.number is the count of items passed to your view by your controller. If it's less than or equal to zero, disabled will be true.
The syntax may not be exact, I haven't tried this, but this is the path I would go down to do what you want.
This will work for the initial setting of the value; changing it without refreshing the page is a matter of using JavaScript, as other answers have pointed out.
Method 2: The overly complex but more 'MVC' way
If you want the logic in the controller rather than the view, you should set up a specific ViewModel object that you can add the logic to:
ViewModel
public class MyObjectViewModel
{
MyObject MyObject {get; private set; }
bool Enabled; {get; set; }
public MyObjectViewModel(MyObject obj)
{
MyObject = obj;
}
string IsEnabled
{
get
{
if (Enabled)
{
return "";
}
else return "disabled=disabled";
}
}
Controller
public ActionResult Show(int id)
{
MyObject myObject = repository.GetMyObjectById(id)
MyObjectViewModel movm = myObject;
movm.Enabled = myObject.number > 0;
return View(movm);
}
View
<input type="button" <%= Model.IsEnabled %> />
Again, the syntax and usage may be a little off, I'm prototyping this off the top of my head, and am not in a location where I can test this for you.
If you're interested in ViewModels, here are some good resources:
View Model Best Practices
ASP.NET MVC Tip# 50: Create View Models
I've updated it to return disabled=disabled using the string if it is actually disabled.
All client side behaviour is scripted through javascript. MVC default ships with jQuery for this (www.jquery.com).
I've outlined how you could go about your examples:
<input id="nextFinishBtn" type="button" value="Next ->"/>
Assume you want to change this to "Finish" if the user unchecks a checkbox named "Configure Advanced settings". which was true by default
<%= Html.CheckBox("DoAdvancedSettings", "true", new { onclick='changeNextButton()' }); %>
<script langauge="javascript">
function changeNextButton() {
$('#nextFinishBtn').val('Finish');
}
</script>
In general you can access any attribute of any element in jQuery using the .attr construct:
$('#nextFinishBtn').attr('disabled','disabled');
You call it with two parameters to set a value, and with just one to fetch the value. So to see if the button is disabled, you'd do:
if ($('#nextFinishBtn').attr('disabled')=='disabled') { alert('button is disabled'); }
ASP.NET MVC is a lightweight programming model. It does not create a control object model for you at the server the way plain ASP.NET does. Typically, you would use client-side javascript (possibly with help from JQuery) to manipulate the properties of controls that are put on the page by the markup in your view.
If you want to get a quick start with ASP.NET MVC, check out Sharp Architecture (open source). They have guidance and all sorts of goodies to help you get productive with ASP.NET MVC quickly.
Generally, you rarely want to do this. The MVC way is:
Let your controller populate the model with the objects needed to generate the HTML
Pass the model to the appropriate view
Let the view output the HTML based on the values of the objects in the model.
If you often find yourself in need of modifying HTML attributes after the HTML has been generated, you're probably not applying this pattern correctly.
I would check out the HTML helpers.
<%= Html.Button("AddButton","Button Text",HtmlButtonType.Submit,"SomeJavaScriptFunction()",new {disabled="disabled"} ) %>
The only real gotcha is the anonymous class at the end gets funny when you add attributes that are keywords. For example, to add a Css class you need an anonymous class that looks like this new {#class="myCssClassName"}.
As I replied to George Stocker, I've noticed that the disabled attribute can get only 1 value (disabled = "disabled"). Also, anything else disable the input control as well. For instance, disabled = true and disable = false will still disable the control.
It looks like (I'm not sure) having disabled attribute disables the control and not having it enables the control. So I decided to write a extension method to the HtmlHelper class.
public static class MyHelperClass
{
public static string InputDisable(this HtmlHelper html, string name, string myValue, bool isEnabled)
{
string show = "";
if(!isEnable)
show = "disabled = \"disabled\"";
return "<input type = \"submit\" value = \"" + myValue + "\"" + show + " />";
}
}
Now I can access the method this way
<% = Html.InputDisable("myInput", "My Button", false)%>
So the last param determines weather the input control is visible.
Now using the Goerge Stocker logical, I can define the value of isEnabled .
Thanks for all your answers

MVC, Jquery, and Ajax to display object based on dropdown

I'm trying to design a view to accept donations, sell memberships, or sell tickets to events.
I have a dropdown list that displays "Make a donation", "Purchase membership", and the subsequent options are populated from the IList<Event> Model passed from the controller. I use javascript to determine which panel (membership, donation, or event) should be displayed based on the selection.
The problem I'm having is that once an event is selected, I need to be able to dynamically populate the Event panel with the properties of the selected event (without, of course, having to put the user through a browser refresh). I was told by someone that I should be able to use Ajax to accomplish this. Supposedly I could go to my server/home/GetEventById action to do this. However, I haven't been able to find any examples or any tutorials that would help me accomplish this.
Could anybody shed some light on this for me by means of how to go about this, or provide examples or tutorials that would help me?
Here is a code example of fetching some content by calling a controller method through ajax, and then populating a jQuery dialog with it. Hopefully this helps point you in the right direction.
The controller method:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetItemsForJson()
{
var items = Repository.GetItems();
var result = Json(items);
return result;
}
And the jQuery to make it happen:
$('#dialog_link').click(function () {
$.getJSON("/Items/GetItemsForJson/", getItems);
});
function getItems(items) {
$("#itemlist").text("");
$.each(items, function (i, item) {
$("#itemlist").append("<li>" + item.Id + item.Name + "</li>");
});
}
Your question is a bit too broad. I assume you already implemented your Action in controller so we concentrate only on client side scripting.
Following should within $.ready:
$("#ddlSelectEvent").change(function() { // this will fire when drop down list is changed
var selection = $(this).attr("selected"); // text representation of selected value
$(".panels").hide();
$("#panel_" + selection).show(); // Assume the panel naming will be panel_MakeDonation and those...
// Now is time for ajax - load html directly
$.get("server/home/geteventbyId",
{id: "12345"},
function (data) { // callback when data is loaded
$("#panel_" + selection).html(data);
}
);
});
Above codes assume you populate content of panel with html. You might use JSON or other types depending on how you implement it.
http://docs.jquery.com/Ajax/jQuery.get#urldatacallbacktype
I'm not sure how MVC changes this, but here is how I do a callback with Ajax:
In the onchange event of the dropdownlist box you would call a java function that uses Ajax's PageMethod, something like this:
PageMethods.getVersions(LoadVersionsCallback);
The method you are calling in your .aspx.cs file has to be static, it can take parameters and looks something like:
[System.Web.Services.WebMethod]
public static string getVersions() {
StringBuilder sb = new StringBuilder();
... etc.
return sb.ToString();
}
The javascript function that you specified when you called the method will run when the method completes. It will be passed the results.
function LoadVersionsCallback(result) {
// do something with the results - I load a dropdown list box.
...etc.
}

Categories