Advanced Ajapa Yoga Kriyas and Meditations for Software Developers : Tap the power of breath, mantra, mudra, and dhyana for improved focus, peace of mind, and blissful inner connection.


CRUD using fetch() and Razor Pages in ASP.NET Core -- Part 2

In the previous article we created various page handlers that are supposed to be called from the client side script using fetch() method. Now it's time to build the UI by adding the fetch() calls and client side event handlers.

In all there are five client side event to be dealt with -- page DOM load, change event of the dropdown list, and click event of Insert, Update, and Delete buttons.

Open Index.cshtml file and add the following markup that renders the UI as discussed earlier.

@page "{id?}"
@model FetchApiDemo.Pages.IndexModel
@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers

<h1>CRUD using fetch() in Razor Pages </h1>

<form>
    
@Html.AntiForgeryToken()

    
<table border="1" cellpadding="10">
 
<tr>
            
<td>Customer ID :</td>
            
<td>
                
<select id="customerid"></select>
                
OR

<input id="newcustomerid" type="text" />

</td>
        
</tr>

<tr>
            
<td>Company Name :</td>
        
<td><input id="companyname" type="text" /></td>

</tr>
        <tr>
            
<td>Contact Name :</td>
            
<td><input id="contactname" type="text" /></td>
        
</tr>
        
<tr>
            
<td>Country :</td>
            
<td><input id="country" type="text" /></td>
        
</tr>
        <tr>
            
<td colspan="2">
                
<input type="button" id="insert" value="Insert" />
                
<input type="button" id="update" value="Update" />
                
<input type="button" id="delete" value="Delete" />
            
</td>
        </tr>
    </table>
    
<br />
    <div id="message"></div>

</form>

As you can see, there is a dropdown list with ID customerid and four textboxes with IDs newcustomerid,companyname, contactname, and country respectively. There are also three push buttons at the bottom with IDs insert, update, and delete. Below the table there is a div element with ID message. We have discussed the overall working of this form in the previous article. So, I won't go into those details again.

When the page loads in the browser we need to populate the dropdown list with all the CustoemrIDs. This is done in the DOMContentLoaded event as shown below:

document.addEventListener("DOMContentLoaded", 
async function () {

    var customerid = document.getElementById
    ("customerid");
    var newcustomerid = document.getElementById
    ("newcustomerid");
    var companyname = document.getElementById
    ("companyname");
    var contactname = document.getElementById
    ("contactname");
    var country = document.getElementById
    ("country");

    var insert = document.getElementById("insert");
    var update = document.getElementById("update");
    var del = document.getElementById("delete");

    var message = document.getElementById("message");

    message.innerHTML = "";

    
    const request = new Request("/Index?handler=SelectAll");

    const options = {
        method:"GET"
    };

    const response = await fetch(request,options);

    if (!response.ok) {
        message.innerHTML = `<h2>${response.status} 
            - ${response.statusText}</h2>`;
        return;
    }
    const json = await response.json();

    json.forEach(function(customer){
        const option = document.createElement('option');
        option.value = customer.customerID;
        option.innerHTML = customer.customerID;
        customerid.appendChild(option);
    });

    message.innerHTML = "Customers fetched successfully.";
    
});

The async event handler code starts by grabbing various DOM elements needed by the later part of the code. Mainly we need dropdown list, textboxes, buttons, and the div element. This is done using a series of getElementById() calls.

Notice the code shown in bold letters. This code calls the SelectAll page handler we added in the page model class.

To accomplish this task we first create a Request object and an options object. The Request object wraps the URL of the server side page handler. Notice how handler query string parameter is set to SelectAll -- the name of the page handler we want to invoke. The options object specifies the request method to be GET.

We then call the fetch() method by passing the Request object and the request options.

If the Response is anything other than ok, we display an error message in the message div element. Otherwise, we get the response data using json() method. In our example response data is an array of Customer objects as returned by the SelectAll page handler.

Then we iterate through the list of Customer objects using forEach() method. And append option elements to the customerid dropdown list. This is done using createElements() and appendChild() methods. Note that Customer properties such as CustomerID and CompanyName are automatically converted to their camelCasing equivalents (CustomerID becomes customerID). A success message is displayed in the message div element once all the data is loaded in the dropdown list.

When a user selects a CustomerID from the dropdown list we need to fetch() that customer's data from the SelectByID page handler and show it in the textboxes. This is done in the change event handler of the custoemrid dropdown list.

customerid.addEventListener('change', async function(){
    const request = new Request
    ("/Index/"+ customerid.value + 
    "?handler=SelectByID");

    const options = {
        method: "GET"
    };

    const response = await fetch(request, options);

    if (!response.ok) {
        message.innerHTML = `<h2>${response.status} 
            - ${response.statusText}</h2>`;
        return;
    }
    const json = await response.json();
    companyname.value = json.companyName;
    contactname.value = json.contactName;
    country.value = json.country;
});

This time we make a Request to SelectByID page handler using the fetch() method. Notice that we also embed the CustomerID selected in the dropdown list in the URL. This way the page handler knows which CustomerID is to be returned. The Response returned from the server will be a single Customer object. We read that data using json() method and load the values of CompanyName, ContactName, and Country in the respective textboxes.

In order to add a new customer, a user needs to enter CustomerID, CompanyName, ContactName, and Country values in the newcustomerid, companyname, contactname, and country textboxes and click on the Insert button. The click event handler of Insert button makes a POST request to the Insert page handler. This event handler is shown below:

insert.addEventListener('click', async function(){

    const request = new Request("/Index?handler=Insert");

    let customer = {};
    customer.customerID = newcustomerid.value;
    customer.companyName = companyname.value;
    customer.contactName = contactname.value;
    customer.country = country.value;

    
    const token = document.getElementsByName
    ("__RequestVerificationToken")[0];

    const options = {
        method: "POST",
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'MY-XSRF-TOKEN': token.value
        },
        body: JSON.stringify(customer)
    };
    
    const response = await fetch(request, options);

    if (!response.ok) {
        message.innerHTML = `<h2>${response.status} 
            - ${response.statusText}</h2>`;
        return;
    }
    const msg = await response.json();
    message.innerHTML = msg;
});

The click event handler of the Insert button forms a Request object pointing to the Insert page handler. We need to wrap the customer details in an object and pass that object along with the POST request. So, we create a JavaScript object with four properties -- customerID, companyName, contactName, and country. And we set these properties to the values from the newcustomerid, companyname, contactname, and country textboxes.

Notice the code shown in the bold letters. The Razor Page's anti-forgery token is stored in a hidden form field named __RequestVerificationToken. We pick that token using getElementsByName() method.

This time the options object contains method, headers, and body properties. The method property is set to POST. The headers property sets three request headers namely Accept, Content-Type, and MY-XSRF-TOKEN. Recollect from the previous part of this article that MY-XSRF-TOKEN has been configured to be an HTTP header for carrying the XSRF token. The body property contains the JSON stringified version of the customer object. This JSON data will go as the request body.

We then initiate the fetch() call by passing the Request and options objects as before. This t ime the Response will be a success string message. We display the message in the div element.

In order to update a customer, a user needs to select a CustoemrID from the dropdown list. Then modify the existing CompanyName, ContactName, and Country values from the respective textboxes and hit the Update button. The click event handler of the Update button is similar to that of the Insert button but with a few differences. Take a look below:

update.addEventListener('click', async function(){
    
    
    const request = new Request("/Index/" + 
    customerid.value + "?handler=Update");
    

    let customer = {};
    customer.customerID = customerid.value;
    customer.companyName = companyname.value;
    customer.contactName = contactname.value;
    customer.country = country.value;

    const token = document.getElementsByName
    ("__RequestVerificationToken")[0];

    
    const options = {
        method: "PUT",
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'MY-XSRF-TOKEN': token.value
        },
        body: JSON.stringify(customer)
    };
    

    const response = await fetch(request, options);

    if (!response.ok) {
        message.innerHTML = `<h2>${response.status} 
            - ${response.statusText}</h2>`;
        return;
    }
    const msg = await response.json();
    message.innerHTML = msg;
});

Notice the code shown in bold letters. This time the request URL contains a CustomerID to be updated and points to the Update page handler.

The options object sets the method property to PUT since it's an update operation. Other HTTP headers and body are configured as before.

In order to delete a customer, user needs to pick its CustomerID from the dropdown list and click on the Delete button. The click event handler of the Delete button is shown below:

del.addEventListener('click', async function () {
    
    const request = new Request("/Index/" + 
    customerid.value + "?handler=Delete");
    const token = document.getElementsByName
    ("__RequestVerificationToken")[0];
    const options = {
        method: "DELETE",
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'MY-XSRF-TOKEN': token.value
        }
    };
    
    const response = await fetch(request, options);
    if (!response.ok) {
        message.innerHTML = `<h2>${response.status} 
            - ${response.statusText}</h2>`;
        return;
    }
    const msg = await response.json();
    message.innerHTML = msg;

    // refill the dropdown list

    const request2 = new Request
    ("/Index?handler=SelectAll");
    const options2 = {
        method: "GET"
    };
    const response2 = await fetch(request2, options2);
    if (!response2.ok) {
        message.innerHTML = `<h2>${response2.status} 
            - ${response2.statusText}</h2>`;
        return;
    }
    const json2 = await response2.json();
    customerid.innerHTML = "";
    json2.forEach(function (customer) {
        const option = document.createElement('option');
        option.value = customer.customerID;
        option.innerHTML = customer.customerID;
        customerid.appendChild(option);
    });
});

This code sends a DELETE request to the Delete page handler. Since this is a DELETE request we don't need to pass anything in the request body. Simply embedding the CustomerID and page handler in the URL is sufficient.

Once a customer is deleted, we need to remove it's CustomerID from the dropdown list. We could have done that easily from client side script itself. However, just to confirm that the customer has been successfully deleted, we refill the customerid dropdown list again. The code after the comment is similar to the DOMContentLoaded handler and hence not discussed again.

This completes all the event handlers that are responsible for making fetch() calls to the Razor Page handlers. Run the application and check the CRUD operations. The following figure shows a sample Update operation.

And this is how modified data is received in the Update page handler:

In the next part of this fetch() series, we will learn to invoke MVC actions from the client side script.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant and trainer by profession specializing in Microsoft web development technologies. Having embraced the Yoga way of life he is also a meditation teacher and spiritual guide to his students. He is a prolific author and writes regularly about software development and yoga on his websites. He is programming, meditating, writing, and teaching for over 27 years. To know more about his ASP.NET online courses go here. More details about his Ajapa Japa and Shambhavi Mudra online course are available here.

Posted On : 20 February 2023







Advanced Ajapa Yoga Kriyas and Meditations for Software Developers : Tap the power of breath, mantra, mudra, and dhyana for improved focus, peace of mind, and blissful inner connection.