aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.62k stars 2.14k forks source link

Razor Pages - whole app logic on server side, without JS/Ajax #8760

Closed troncomputers closed 5 years ago

troncomputers commented 5 years ago

Sorry if this is wrong when I put everything in one issue, but I thought it would be nice when all of my feature requests are in one place.

  1. Temp objects without JS - for my case I need a list of products that users selected. EXAMPLE:

    public class NewModel : PageModel
    {
    public List<string> SelectedProducts {get;set;}
    
    public async Task<IActionResult> OnGetAsync()
    {
        SelectedProducts = new List<string>();
        return Page();
    }
    
    public async Task<IActionResult> OnPostAddProduct(string product)
    {
       SelectedProducts.Add(product);
       return RedirectToPage();
    }
    }
    <form method='post' asp-page-handler='AddProduct'>
    <input name="product" />
    </form>

    SelectedProducts is null and every AddProduct is empty because OnGetAsync() is fired. In this case only HttpContext.Session with converting object to JSON is working but... If I navigate from menu somewhere else and go back to New page, the selected products are still there which brings us to next feature I need.

  2. Page model OnDestroy event - is not firing on self redirection or post redirection. Should fire only if I navigate to or from somewhere else.

  3. Url change detection - I didn't find any info about how to manage url changes. It would help with OnDestroy event, because then I am able to do my own logic for session object.

I don't care about page refreshing. It's not that big project. I want whole logic on server side without JS/ajax etc.

mkArtakMSFT commented 5 years ago

Thanks for contacting us, @troncomputers. Looks like what you're looking for is achievable using TempData. It can be used to save state between requests and that's what you can use to store your SelectedProducts. You can read more about TempData at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.1#tempdata Here is another good article on the use cases of TempData, which may help: https://www.learnrazorpages.com/razor-pages/tempdata

troncomputers commented 5 years ago

Hi TempData is not a solution in my case. When I post my TempData["SelectedItems"] is null. If I use the .Keep() method, TempData is acting like HttpContext.Session but... like I said before

If I navigate from menu somewhere else and go back to New page, the selected products are still there

And there is no property or method to determine if I do a postback or redirection or just page refresh. EXAMPLE 1:

        public async Task<IActionResult> OnGetAsync()
        {
            if(TempData["SelectedItems"] == null)
            {
                SelectedItems = new List<string>();
                string json = JsonConvert.SerializeObject(SelectedItems);
                TempData["SelectedItems"] = SelectedListJson;
            }
            else
            {
                string json = TempData["SelectedItems"].ToString();
                SelectedItems = JsonConvert.DeserializeObject<List<string>>(json);
            }

            return Page();
        }

        [HttpPost]
        public async Task<IActionResult> OnPostAddItem(string item)
        {
            string json = TempData["SelectedItems"].ToString(); // <-- TempData["SelectedItems"] is null here
            SelectedItems = JsonConvert.DeserializeObject<List<string>>(json);
            if (SelectedItems == null)
                SelectedItems = new List<string>();

            SelectedItems.Add(item);
            json = JsonConvert.SerializeObject(SelectedItems);

            TempData["SelectedItems"] = json;

            return RedirectToPage();
        }

If I do check that TempData["SelectedItems"] == null and I will do object creation in post method, I end up with only one element in List<string>

        [HttpPost]
        public async Task<IActionResult> OnPostAddItem(string item)
        {
            // ...because the TempData["SelectedItems"] is always null here
            if(TempData["SelectedItems"] == null)
            {
                SelectedItems = new List<string>();
                TempData["SelectedItems"] = JsonConvert.SerializeObject(SelectedItems);
            }
            else
            {
                SelectedItems = JsonConvert.DeserializeObject<List<string>>(TempData["SelectedItems"].ToString());
            }

            SelectedItems.Add(item);

            TempData["SelectedItems"] = JsonConvert.SerializeObject(SelectedItems);

            return RedirectToPage();
        }

My post method is creating a List<string> over and over again with only one last posted item. Same behavior when I use [TempData] attribute on a model property. Instead of TempData["SelectedItems"] I used:

public class IndexModel : PageModel
    {
        public List<string> SelectedItems { get; set; }

        [TempData]
        public string SelectedListJson { get; set; }
   }
mkArtakMSFT commented 5 years ago

Thanks for the details, @troncomputers. @NTaylorMullen, can you please look into this? Thanks!

mkArtakMSFT commented 5 years ago

@troncomputers you should read the documentation I've shared. accessing a value stored in TempData removes it. So if you intend to read it multiple times, or just want to check whether it's there or not you should use an appropriate approach. In this case, you could use the Peek() method. See more details in the documentation I've referenced.

troncomputers commented 5 years ago

@mkArtakMSFT .Peek() has same behavior as .Keep() and HttpContext.Session. I don't know when I can clear the list.

Imagine that you're doing an app, realy simple and small, that can store products orders. You have order Index page with list of all your orders and one button to create a new one. In the New order page you can select products that you want to order. Here is the tricky part that I can't manage to solve. You did select couple of products but you changed your mind and decide to not order anything now and from main menu you navigate back to the Index page. So far so good. Couple minutes later someone else is trying to do an order but there are your selected products, btw shouldn't be there. It doesn't have to be other person, even you can go back to the computer and try to do new, different, order but before that you have to manualy clear old one...

I am aware that sometimes you want to keep selected products, go back to index, open saved order to compare that you are not going to order something again and continue with previous, not finished order. Right now I want to keep the object on post, self redirect or even a page refresh in New page.

mkArtakMSFT commented 5 years ago

@troncomputers, it seems this is more of an usage type of question. While we do our best to look through all the issues filed here, to get a faster response we suggest posting your questions to StackOverflow using the asp.net-core-mvc tag.