Home > Software design >  Why isn't my list maintaining state on the PageModel between posts?
Why isn't my list maintaining state on the PageModel between posts?

Time:11-16

I'm maintaining a list on my PageModel as a BindProperty.

When the user submits a form on the page, this list should be added to and the input from which it got its value should be reset.

<form class="ratingQuestionForm" method="post" asp-page-handler="AddQuestion">
    <input type="hidden" name="questionType" id="questionType" value="Rating" />
    <textarea name="questionText" id="questionText" class="form-control mb-1"
        required></textarea>
</form>
<a class="btn btn-outline-dark add-rating-question">Add Rating
    Question</a>

<script>
    $(".add-rating-question").on("click", function () {
        $(".ratingQuestionForm").submit();
        $(this).closest("textarea").val("");
    });
</script>

On the backend, I'm using TempData["Questions"] as short term storage for the list of questions submitted this way.

[BindProperties]
public class CreateModel : PageModel
{
    public List<Question> Questions { get; set; }

    // The usual constructor and DI stuff has been removed for brevity.
    public async Task OnGetAsync()
    {
        // If there are already questions in ViewData, load them.
        if (ViewData["Questions"] is not null)
        {
            this.Questions = ViewData["Questions"] as List<Question>;
        }

        // Basically everything not related to the question removed for brevity.
    }

    public void OnPostAddQuestion(string type, string text)
    {
        var _type = type switch
        {
            "Rating" => QuestionType.Rating,
            _ => QuestionType.OpenEnded
        };

        // Load existing questions if there are any.
        if (ViewData["Questions"] is not null)
        {
            this.Questions = ViewData["Questions"] as List<Question>;
        }

        this.Questions.Add(new Question
        {
            Type = _type,
            QuestionText = text
        }
    }
}

When I wasn't seeing the behavior I wanted on the UI, I had the page build a list of questions for me. This indicated that only the last question added was persisting to the model.

@if (Model.Questions is not null && Model.Questions.Any())
{
    <ul class="list-group list-group-flush">
        @foreach (var q in Model.Questions)
        {
            <li class="list-group-item">@q.QuestionText</li>
        }
    </ul>
}

How can I persist my questions through posts?

CodePudding user response:

In your question I see that you say "TempData", but in your code I see you using "ViewData" instead. I believe ViewData only lasts for the current request. What you'll want to use is TempData. Please note, however, that the TempData dictionary cannot hold complex objects. To get around this, you can serialize and de-serialize whatever you need storing. I'd recommend using JavaScript Object Notation (JSON). You can use the System.Text.Json namespace/library discussed in this article for serializing:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-overview?pivots=dotnet-core-3-1

Here's what I did with my own code to try and solve your issue:

using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Text.Json;

namespace StackOverflowQA.Pages
{
    public class CreateModelModel : PageModel
    {
        public class Question
        {
            public string Type { get; set; }
            public string Text { get; set; }
        }

        private const string questionsKey = "Questions";

        public List<Question> Questions { get; set; }

        public void OnGet()
        {
            Questions = GetQuestions();
        }

        public void OnPostAddQuestion(string type, string text)
        {
            Questions = GetQuestions();
            Questions.Add(new Question { Type = type, Text = text });
            TempData[questionsKey] = JsonSerializer.Serialize(Questions);
        }

        private List<Question> GetQuestions()
        {
            TempData.TryGetValue(questionsKey, out object o);
            if (o is null)
                return new List<Question>();
            return JsonSerializer.Deserialize<List<Question>>((string)o);
        }
    }
}

Here's another article found that explains a bit about how TempData works: https://www.learnrazorpages.com/razor-pages/tempdata

  • Related