Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET core persisting values between Get and Post error validation

I'm new to web development so I don't know a good way on how to persist data between requests.

This is my site so far:

enter image description here

The elephant title is being fetched from an API on the GET request, according to the titleId query parameter. When I press login, model validations are being run, for example that email and password must have been entered. However, when error page is returned, elephant text is empty since that value was not persisted. What are the best approaches to persist that value so that is still visible when POST error is returned? Does it has to be included in the POST data? I don't want to request the API again.

Code behind:

public class IndexModel : PageModel
{
    private string apiTitle;
    public string ApiTitle { get { return apiTitle; } set { apiTitle = value;  } }

    // Bind form values
    [BindProperty]
    public User user { get; set; }

    public Task<IActionResult> OnGetAsync(string titleId)
    {
        if (!string.IsNullOrEmpty(titleId))
        {
            ApiTitle = await GetTitleFromApiAsync(titleId);
        }
        return Page();
    }

    public async Task<IActionResult> OnPostLoginAsync()
    {
        if (!IsLoginFormValid())
        {
            // When this is returned, for example if no password was entered,
            // elephant title goes missing since apiTitle is null?
            return Page();
        }

        var user = await LoginEmailAsync();
        return RedirectToPage("/Profile");
    }
}

Html:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<link rel="stylesheet" href="css/index.css">

<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>

<div class="login-page">

    <span id="label_api_title">@Model.ApiTitle</span>

    <div class="form">
        <form class="login-form" method="post" asp-page-handler="Login">
            <input type="text" placeholder="Email" asp-for="User.Email"/>
            <span asp-validation-for="User.Email" class="text-danger"></span>
            <input type="password" placeholder="Password" asp-for="User.Password1" />
            <span asp-validation-for="User.Password1" class="text-danger"></span>
            <button>login</button>
            <p class="message">Not registered? <a href="#">Create an account</a></p>
        </form>
    </div>
</div>

<script src="js/index.js"></script>
like image 754
Lucas Arrefelt Avatar asked Nov 20 '17 18:11

Lucas Arrefelt


1 Answers

Yes. What you see is the expected behavior. Remember, Http is stateless. You are making 2 separate http calls, one for the GET and one for POST. The second call has no idea what the first call did ( or even there was first call at all!)

If you want to have a way to read the ApiTitle property value in the Post call and return that to the view, you need to persist it somewhere so that it is available between http calls. But in your case, all you need is to include that in the form post and have the framework bind it for you.

In your case, you can simply use a public property (Which is settable and gettable) for this. No need to keep a private variable. Decorate your property with BindProperty attribute so the model binder will bind the data on this property.

public class CreateModel : PageModel
{
    [BindProperty]
    public string ApiTitle { get; set; }

    //Your existing code goes here
}

Now inside your form tag, have an input hidden element for the ApiTitle. This way, when the form is submitted, the value of ApiTitle property will be send in the request data.

<form class="login-form" method="post" asp-page-handler="Login">
     <input type="hidden" asp-for="ApiTitle"/>
     <input type="text" placeholder="Email" asp-for="User.Username"/>
     <!--Your other existing form elements -->
     <button>login</button>
</form>

Now in your OnPostLoginAsync method, you can read the ApiTitle value if needed. When you return the Page (when validation fails), the UI will display the ApiTitle property value in your span element.

public async Task<IActionResult> OnPostLoginAsync()
{
    var title  = this.ApiTitle;  // If you want to read this value

    if (!ModelState.IsValid)
    {
        return Page();
    }
    return RedirectToPage("/Profile");
}
like image 200
Shyju Avatar answered Oct 12 '22 18:10

Shyju