locked
How to pass Json response data on to view page after the view page is already loaded RRS feed

  • Question

  • User1242168447 posted

    I have an asynchronous action method named "GetStudentsDetails" that retrieves data thru webAPI... which is associated with its View.

    What I want to achieve is when user click a button from another page to call "GetStudentsDetails" action method, it must directly show the view page of "GetStudentsDetails" with a bootstrap "Spinner" and a text saying "Please wait while page is retrieving students data"; and at the same time the action method is still retrieving data in background... and when the webapi is finished retrieving data, then it directly passes the data on to the view. (possibly without loading again the view page). Because the below method is waiting until the webapi finishes to retrieve data and display it... and the waiting time might vary... sometimes it takes long...

    The code is working properly, the only problem is that it's holding me on the current page while it is calling the webapis.

        [HttpGet]
        public async Task<ActionResult> GetStudentsDetails()
        {
             HttpClient client = new HttpClient();
    
                string APIdatas = null;
    
                HttpResponseMessage response = await client.GetAsync("https://www.clientWebSite.com/api/endpoint/9F507DA1785E77E2EA79335A3930E59E3F3B7CD/deals");
    
                if (response.IsSuccessStatusCode)
                {
                    APIdatas = await response.Content.ReadAsStringAsync();
                }
    
                var stringJson = JsonConvert.DeserializeObject<List>(APIdatas);
    
                RootObject stringJsonConv = null;
    
                List stringJsonConvToList = new List();
    
                foreach (var item in stringJson.ToList())
                {
    
    
                    HttpClient client2 = new HttpClient();
    
                    string APIdatas2 = null;
    
                    HttpResponseMessage response2 = await client2.GetAsync("https://www.clientWebSite.com/api/endpoint/9F507DA1785E77E2EA79335A3930E59E3F3B7CD/deals/" + item.Id);
    
    
                    if (response2.IsSuccessStatusCode)
                    {
                        APIdatas2 = await response2.Content.ReadAsStringAsync();
    						
                            //var stringJson2 = JsonConvert.DeserializeObject(APIdatas2);
    
                            RootObject stringJson2 = JsonConvert.DeserializeObject(APIdatas2);
    
                            stringJsonConv = stringJson2;
    						stringJsonConvToList.Add(stringJsonConv);
    
                    }
                    else
                    {
                        Debug.WriteLine("Error occurred, the status code is : {0}", response2.StatusCode);
                    }
    
                }
    
                return View(stringJsonConvToList);
    
    
           
    
        }

    Monday, October 21, 2019 8:32 AM

Answers

  • User61956409 posted

    Hi LetMeCode,

    As mgebhard suggested, you can display spinner/loader before calling GetStudentsDetails and return view.

     For example, you can create and use a overlay to display spinner/loader in a view page, and when user click the link/button to get student details, you can make spinner/loader display using js code, like below.

    <a href="/home/GetStudentsDetails" id="btn_getstudentdetails">StudentsDetails</a>
    
    <div id="loading">
        <div class="loader" id="loader-1">
        </div>
    </div>
    <script>
        $(function () {
            $("#btn_getstudentdetails").click(function () {
                $("#loading").fadeIn();
            })
        })
    </script>

    With Regards,

    Fei Han

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 22, 2019 6:51 AM
  • User475983607 posted

    LetMeCode

    Now I understand. But many other web applications out there are implementing that mechanism to show a loader while an operation is going on in background.

    for instance, Office 365, Google Apps,...

    Can you please help me out implement that using my above code? please

    Fei Han provided the source code above.  Now that you understand the issue, the solution is very simple.   

    Add the spinner HTML to your View but hide the spinner.  Below is untested code but the Internet is rich with full examples as this is a very common scenario.

    <div id="mySpinner" style="display:none;">
      <img src="theSpinner.gif" />
    </div>

    Write a jQuery/JavaScript form.submit handler that shows the spinner.  

    $('form')[0].submit(function(){
      $('#mySpinner').show();
    });

    The browser refreshes the page once the response returns which overwrites the spinner HTML with new content.

    LetMeCode

    I'm though more familiar with WinForms app in VB.Net and C#... I usually implement heavy workload using System.Threading.Thread which I can start/stop/cancel/resume the current thread.  Also there's today something called Backgroundworker that almost does the same thing.

    Threading works the same in a web application.  The difference is a web application is shared by many users while a WinForms application has a single user.  Keep in mind, you have not fixed the C# code which still has the same issues.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, October 23, 2019 12:21 PM

All replies

  • User475983607 posted

    What I want to achieve is when user click a button from another page to call "GetStudentsDetails" action method, it must directly show the view page of "GetStudentsDetails" with a bootstrap "Spinner" and a text saying "Please wait while page is retrieving students data"; and at the same time the action method is still retrieving data in background...

    GetStudentsDetails contains the actual results from the Web API.  You must show the spinner before calling GetStudentsDetails.   Write JavaScript in the page that has the button to show the spinner followed by doing a Post to GetStudentsDetails.  GetStudentDetails will refresh the page with new HTML.

    Monday, October 21, 2019 11:02 AM
  • User61956409 posted

    Hi LetMeCode,

    As mgebhard suggested, you can display spinner/loader before calling GetStudentsDetails and return view.

     For example, you can create and use a overlay to display spinner/loader in a view page, and when user click the link/button to get student details, you can make spinner/loader display using js code, like below.

    <a href="/home/GetStudentsDetails" id="btn_getstudentdetails">StudentsDetails</a>
    
    <div id="loading">
        <div class="loader" id="loader-1">
        </div>
    </div>
    <script>
        $(function () {
            $("#btn_getstudentdetails").click(function () {
                $("#loading").fadeIn();
            })
        })
    </script>

    With Regards,

    Fei Han

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, October 22, 2019 6:51 AM
  • User1242168447 posted

    Hi guys thanks for your replies but it's not working for me.

    I edited the code and added foreach loop. this is how it is in my computer.

    I put a break point to spot each step. I mean when I click on a button to call the action method "GetStudentsDetails", it goes through it and the page just stuck until the loop is finished...

    so while is looping, it's just stuck on the page that has GetStudentsDetails button (Not the GetStudentsDetails view) and it only shows the GetStudentsDetails view page after the entire loop is complete !!!

    Also, return View(studentsDataJson) is out of the foreach loop !!! so why is it holding it (the page) until the loop is complete???  Not sure why !!!  Seems like I need a real Thread, not a async Task !!!

    https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/

    Is there any way of doing that please?? cause I really want to finish with that small app very soon?

    Tuesday, October 22, 2019 6:58 PM
  • User475983607 posted

    LetMeCode

    so while is looping, it's just stuck on the page that has GetStudentsDetails button (Not the GetStudentsDetails view) and it only shows the GetStudentsDetails view page after the entire loop is complete !!!

    Correct, you are explaining how HTTP fundamentally works.  The browser makes an HTTP request and waits for the server to send a response.  In MVC, the response is sent at the end of the Action usually by returning a View.

    return View(SomeModel);

    LetMeCode

    Is there any way of doing that please?? cause I really want to finish with that small app very soon?

    A side from not understanding HTTP, your original code has a lot of errors.

    • The foreach is looping over an unknown collection; resultJson.
    • The final result of studentsDataJson is the result of the very last foreach loop but it seems every request is the same so it is not clear why you have a for each loop.
    • HttpClient should be a static and reused.  As written the code will open a new connection for each loop which can consume all available connections.  The code will wait until TcpTimedWaitDelay expires once all connections are used up.

    It's not easy to understand the intent given the code and therefore not easy to provide a solution.  Can you explain how the Action is supposed to work?   

    Tuesday, October 22, 2019 7:43 PM
  • User1242168447 posted

    Hi,

    I edited the above code and pasted the whole code for the action method.

    I read async and await and Task<> methods now, and the documentation says that inside asynchronous method, wherever you see await it's mean that the control flow will stop if it reaches "await" until the line with await keyword will finish its work... in the meantime, the UI is not blocking, then after the work is finished, then the control flow continue in the next line after await !!!

    For me, I think I need to use Thread instead of async/await/Task<>...

    According to MSDN official doc, as from my understanding, async/await/Task<> execute a task asyncronously without blocking the UI thread but by stopping the control flow to jump on the next line-statement...

    instead Thread let you execute a task away from the normal control flow, right after the execution start, it jumps in the next line-statement.

    Wednesday, October 23, 2019 9:06 AM
  • User475983607 posted

    The new code has the same issues as the first.

    I read async and await and Task<> methods now, and the documentation says that inside asynchronous method, wherever you see await it's mean that the control flow will stop if it reaches "await" until the line with await keyword will finish its work... in the meantime, the UI is not blocking, then after the work is finished, then the control flow continue in the next line after await !!!

    The GetStudentsDetails() Action method is functioning as expected.  The problem is you misunderstand how web applications work at a fundamental level.  In web applications there is one request stream and once response stream.  The browser sends a request and waits for the response regardless of implementing an async/await pattern.  Basically, the GetStudentsDetails() Action method runs to completion and sends one response not several.

    Wednesday, October 23, 2019 11:22 AM
  • User1242168447 posted

    Thanks mgebhard.

    Now I understand. But many other web applications out there are implementing that mechanism to show a loader while an operation is going on in background.

    for instance, Office 365, Google Apps,...

    Can you please help me out implement that using my above code? please

    I'm though more familiar with WinForms app in VB.Net and C#... I usually implement heavy workload using System.Threading.Thread which I can start/stop/cancel/resume the current thread.  Also there's today something called Backgroundworker that almost does the same thing.

    Please I really want to implement it. Can you please assist me?

    Wednesday, October 23, 2019 11:55 AM
  • User475983607 posted

    LetMeCode

    Now I understand. But many other web applications out there are implementing that mechanism to show a loader while an operation is going on in background.

    for instance, Office 365, Google Apps,...

    Can you please help me out implement that using my above code? please

    Fei Han provided the source code above.  Now that you understand the issue, the solution is very simple.   

    Add the spinner HTML to your View but hide the spinner.  Below is untested code but the Internet is rich with full examples as this is a very common scenario.

    <div id="mySpinner" style="display:none;">
      <img src="theSpinner.gif" />
    </div>

    Write a jQuery/JavaScript form.submit handler that shows the spinner.  

    $('form')[0].submit(function(){
      $('#mySpinner').show();
    });

    The browser refreshes the page once the response returns which overwrites the spinner HTML with new content.

    LetMeCode

    I'm though more familiar with WinForms app in VB.Net and C#... I usually implement heavy workload using System.Threading.Thread which I can start/stop/cancel/resume the current thread.  Also there's today something called Backgroundworker that almost does the same thing.

    Threading works the same in a web application.  The difference is a web application is shared by many users while a WinForms application has a single user.  Keep in mind, you have not fixed the C# code which still has the same issues.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, October 23, 2019 12:21 PM
  • User753101303 posted

    As pointed already it's unrelated to your server side code. Your client side code should show a "progress ui" right before triggering the Ajax call so that the server can do its job and then the client side will update the UI when the response is returned by the server.

    Have a a look for example at https://makitweb.com/display-loading-image-when-ajax-call-is-in-progress/ 

    Forget about WinForms. Keep in mind in particular that the UI is shown inside a browser while the web api code runs on a server that has absolutely no knowledge at all about the UI.

    Basically a web server just process incoming text and renders text (most often HTML markup) as a response but doesn't have any direct knownledge about the UI side (and even less about a UI thread).

    Wednesday, October 23, 2019 12:32 PM
  • User1242168447 posted

    Thanks guys for your explanation.

    Good, it's now working. But it's showing on the current page...  I want it to display on the next page (return View) page. Do I need to create a fake page with same view (without data) and show that fake page with the loading progress, and then after the webapi call will terminate then pass the value (return view) in to the real page and show it???  Not sure if it's right to do it?

    I also need to add some text below/above the loading gif/picture saying you posted "Please wait while we're fetching data". How Do I do it?

    Also, if I use the above code your provided, how do i know that the api is finished and clear the loading gif ??

    I now fixed the above code as it is on my visual studio.

    Want it to look like that with default view, but without data, and after the webapi will terminate and pass the data in the view !!!

    https://cms.jotform.com/uploads/answers/answer/StreetLink/1625623_Stripe%20Payment%20Processing%20Time.png

    https://blog.malwarebytes.org/wp-content/uploads/2015/09/amazon-phish-spinny.png

    Wednesday, October 23, 2019 4:57 PM
  • User475983607 posted

    LetMeCode

    I also need to add some text below/above the loading gif/picture saying you posted "Please wait while we're fetching data". How Do I do it?

    I'm not sure how to make this any clearer than what's been discussed above.

    LetMeCode

    Also, if I use the above code your provided, how do i know that the api is finished and clear the loading gif ??

    If you are doing a full page refreshes the new content simply overwrites the gif within the browser window.  There's nothing else to do you have to do.   If you are doing a partial request from JavaScript code, there's an even handler that fire when the the client (code) receives the response.   Write code in the handler to do whatever UI updates you like.

    Wednesday, October 23, 2019 5:43 PM
  • User1242168447 posted

    What If I use TempData to pass value when the api call is finished from controller to view, will it work?

    Wednesday, October 23, 2019 6:43 PM
  • User475983607 posted

    LetMeCode

    What If I use TempData to pass value when the api call is finished from controller to view, will it work?

    Web API is stateless by definition and does not use TempData.  Even if you switched to an MVC controller to populate TempData, the logic makes no logical sense because you can return whatever data you like in any format you prefer.

    Wednesday, October 23, 2019 7:06 PM
  • User1242168447 posted

    Thanks.

    I read on this thread that it's possible to do so and also tested it, it's working nicely. Read here

    But I'm wondering about... How to pass the result data (after the task is done) on to the view to be displayed??? Can you please assist me?

    public ViewResult Index() { 
    
        Task.Factory.StartNew(() => { 
            //Do an advanced looging here which takes a while
        });
    
        return View();
    }

    Thursday, October 24, 2019 3:26 PM
  • User2041008840 posted

    DO it using Jquery 

    call the get method using jquery put spinner before loading all data.

    Thursday, October 24, 2019 4:06 PM
  • User475983607 posted

    But I'm wondering about... How to pass the result data (after the task is done) on to the view to be displayed??? Can you please assist me?

    The problem you are facing has not changed.  HTTP still works the same way there is one request stream and one response stream.   The new code starts a background task and returns to the browser.  Your browser application must make separate HTTP requests to query the state of the running process.  This is code that you must design and write.

    Thursday, October 24, 2019 5:31 PM