Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Implement CRUD forms with React and ASP.Net Core - Part 3

0.00/5 (No votes)
8 Nov 2017 1  
In this part of our tutorial we'll be adding CRUD (Create, Read, Update, Delete) actions to our high performance ASP.Net Core React.js components without using Redux or JQuery.

Introduction

This is part two of our three part series on building CRUD forms using React.js on ASP.NET Core 2.0.  If you haven't completed Parts 1 and 2 of this tutuorial, you'll want to do that first and then return to this page.

For Part 2 of this tutorial click here:

Part 2: How to Implement CRUD forms with React and ASP.Net Core

Action Links

Let's start by adding our action links to the Index view of our Actor component. Open that file (ClientApp/components/Actor/Index.tsx) and add an html button as shown below to the render() method (fig 1). The button will be used to open our view for creating new records.

Index.tsx

fig 1

We also need buttons for reading details, editing (updating), and deleting records in order to implement all of our CRUD actions. Scroll down to the renderTable() method and insert the three buttons shown below between the first <td> </td> tags (fig 2).

Index.tsx

fig 2

We need Javascript handlers to handle the actions that our buttons will trigger. Before we add those, though, we need to add flags to our state to indicate whether one of these views should be shown. We also need to track the id of the active item, so we've added a variable for that. Scroll up to the ActorState interface and add the three boolean flags, as well as the activeId variable, as shown below (fig 3). Also, initialize these variables in the constructor by setting the the flags to false and activeId to 0 (fig 3).

Index.tsx

fig 3

We can now add our Javascript handlers, starting with Create, Edit, and Details. Go ahead and copy these three handlers below the constructor as shown below (fig 4).

handleCreate() {
    this.setState({ showCreate: true, showDetails: false, showEdit: false })
}

handleEdit(id: number) {
	this.setState({ showEdit: true, showDetails: false, showCreate: false, activeId: id })
}

handleDetails(id: number) {
	this.setState({ showDetails: true, showCreate: false, showEdit: false, activeId: id})
}

fig 4

Last, but not least, is our Delete handler. Type it as shown below (fig 5).

handleDelete(id: number) {
    if (!confirm('Are you sure you want to delete this?'))
        return
	fetch('api/actor/delete/' + id, { method: 'delete' })
        .then(data => {
            this.setState(
                {
                    actor: this.state.actor.filter((rec) => {
						return (rec.Id != id);
                    })
                });
        });
}

fig 5

We need to add import statements for our new views as well as one for react-modal, a popup component that we'll be using to display the other views (fig 5.1).

import * as Modal from 'react-modal'
import { CreateEdit } from './CreateEdit'
import { Details } from './Details'

fig 5.1

We need to modify our render() method a bit to render our new views, which will be rendered in a popup. The first method, renderPopup(), simply checks our showXXXXXX flags to determine whether to show the popup for a given view. The renderPopupContent() method, as its name implies, renders the actual content. Let's type out the two methods as shown (fig 5.2).

renderPopup()

fig 5.2

You may have noticed a few new Javascript handlers in the previous code. There's one for closing the popup (closeModal()) and another for saving the data in the popup (handlePopupSave()). Guess what's next? You guessed it, we get to copy those (fig 5.3)! Why so much typing, you ask? A wise man once told me.."Experience isn't the best way to learn..it's the only way.".

closeModal() {
    this.setState({ showDetails: false, showCreate: false, showEdit: false });
}

handlePopupSave(success: boolean) {
    if (success)
        this.setState({ showCreate: false, showEdit: false });
}

fig 5.3

React-modal

React-modal is the popup component that we are using for displaying our other views. We need to install this component in order to use it (that's a no brainer). Let's install it using NPM:

  1. Open Windows Explorer, or your file explorer program if you're not using Windows.
  2. Navigate to your project's root foler (the one with the bin\, Controllers\, and Views\ folders).
  3. Type the following command (fig 5.4):
    npm install react-modal@2.3.2

npm

fig 5.4

The Other Views

If it seems like a long road, it has been. But we are nearly done. We need a couple of other views to handle the rest of our CRUD operations. Specifically we need a view for handling Creates/Edits and another one for Details. Let's start with the Details view since it's simpler.

The Details View

Right click on the ClientApp/components/Actor folder and select Add, then New Item... From the left side of the Add New Item popup, select ASP.Net Core, then Web, then Scripts. Then select TypeScript JSX File from the middle box, just like when we created the Index view in Part 2. Name the new file: Details.tsx.

Just like before type out the same import statements and create an interface for the Details state as shown below (fig 6).

import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import * as models from '../../models';

interface DetailsState {
    actor: models.Actor;
    loading: boolean;
}

interface DetailsProps {
	id: number
}    

fig 6

Unlike the Index component, we will be passing in a record id, so we need to create an interface for the Component Properties. Component Properties are values that you pass in to a React component (here it's the record ID). If you haven't already done so, also type out the DetailsProps interface as shown (fig 6).

Our Details component class is very similar to the one we create for the Index component. Go ahead and type out the code for it as shown (fig 7).

Details.tsx

fig 7

The last part of our component is simply the reanderDetails() method, which is similar to the one in the Index component but quite a bit simpler (fig 8).

renderDetails()

fig 8

Whew! If you're feeling the same way I here you. Luckily can see the light at the end of the tunnel from here. Let's test our Details view. We'll have to comment three lines of code to do that, since we haven't created the CreateEdit component yet. Temporarily comment the 'import {CreateEdit}..' line as well as the code in the renderPopupContent() method as shown below (figs 9 and 10).

commented code

fig 9

renderPopupContent()

fig 10

We can now run the application and view our Details view. Just click on any Details link to trigger the popup. You may notice the positioning is not quite right for larger screens. We'll take care of that with some CSS next.

Actor Details

fig 11

Note: If you were'nt able to run the application, don't fret and definitely don't give up. You can download the code for this tutorial below.

The CreateEdit View

Our final React view is the CreateEdit view. It will allow us to add new records, as well as edit existing ones. Before we dive in, go back and un-comment the three lines we commented above in order to test the Details component. Moving on...

Before we build our final component lets fix the positioning issue with our Details view. All we need to do is add some CSS... Expand the css/ folder under ClientApp/, then open the site.css file. Just before the '@media (max-width: 767px)' copy the following CSS (fig 11.1), which will offset the popup by 25% (to the right of the nav menu) for computer screens, and for screens 837px or smaller fill most of the screen.

div.ReactModal__Content {
    left: 25% !important;
}

@media only screen and (max-width: 837px) {
    div.ReactModal__Content {
        top: 60px !important;
        left: 4% !important;
        right: 4% !important;
        bottom: 4% !important;
    }
}

fig 11.1

Let's build our final view. By now you should be getting familiar with the routine. Create a new TypeScript JSX file in the ClientApp/components/Actor folder and call it CreateEdit.tsx. Then add the following import statements (fig 12).

import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import './actor.css';
import {Actor} from '../../models'
import * as models from '../../models'

fig 12

Next, create interfaces for the component's State and Properties (fig 13).

interface CreateEditState {
    actor: Actor;
    loading: boolean;
	save: boolean
}
interface CreateEditProps {
	id: number
    dbaction: string
    onSave?: any /* event*/
}

fig 13

Our component class looks similar to the two prior components we created (fig 14). We have a bit more to add to the class so make sure to leave it open, i.e. don't add the final brace }.

export class CreateEdit extends React.Component<createeditprops, createeditstate=""><CreateEditProps, CreateEditState> {
    constructor(props) {
        super(props);
        if (this.props.dbaction == "edit") {
			this.state = { actor: null, loading: true, save: false }
			fetch('api/actors/' + this.props.id, { method: 'get' })
                .then(response => response.json() as Promise<actor>)
                .then(data => {
                    this.setState({ actor: data, loading: false });
                });
        } else
			this.state = { actor: null, loading: false, save: false}
    }    
</actor></createeditprops,>

fig 14

When a new record is added, or an existing record modified, we need to save it to the database. Here's how we will handle that (fig 15). You may notice that we pass the method in the URL. That allows us to use one form to handle both Create and Edit operations.

    handleSave(e) {
        e.preventDefault()
        let meth: string = (this.props.dbaction == "edit" ? "put" : "post")
        let form: Element = document.querySelector('#frmCreateEdit')
        let id = document.getElementById('Id') as HtmlInputElement
		fetch('api/actors/' + meth,
            {
                method: meth,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(formToJson(form))
            })
<models.jsonresponse>            .then(data => {
				this.setState({ save: false, jsonResponse: data });
                this.props.onSave(true);
            });
    }
</models.jsonresponse>

fig 15

Our render() method should also look familiar (fig 16). The only difference being that we toggle the <h1> text between 'Edit Actor' and 'Create Actor'.

render()

fig 16

Our renderForm() method renders the actual Html markup (fig 17). Note that we use the defaultValue property on the inputs here to allow users to modify the value. If you set the value property React will control the form and not allow the values to be modified.

renderForm()

fig 17

Last but not least, we need to add a utility for serilizing our form as JSON when we submit it. Let's type out the following code, making sure to add the final brace } on the component class (fig 18).

/* utils*/
isValidElement = element => {
    return element.name && element.value;
};
isValidValue = element => {
    return (['checkbox', 'radio'].indexOf(element.type) == -1 || element.checked);
};
formToJson = elements => [].reduce.call(elements, (data, element) => {
    console.log('formToJson()', element)
    if (this.isValidElement(element) && this.isValidValue(element)) {
        data[element.name] = element.value;
    }
    return data;
}, {});

fig 18

And that completes our component. If the planets are in alignment and everything compiles correctly, you can run your React Single Page Application and perform all of your CRUD operations as seamlessly as a native app!

Create Actor

fig 18

Actor Details

fig 19

Edit Actor

fig 20

Delete Actor

fig 21

And there you have it! Our other two components: Movie and MovieActor can be built using the same approach, so we'll leave that to the reader to pursue. You can download all of the code for this tutorial here.

Final Thoughts

It's been quite a journey, wouldn't you say? I must confess one thing, though, and that's that ALL of the code from this tutorial was auto-generated with a single button click in less than a minute using React Turbo Scaffolding - that's right every last line even the server models we started with. By simply pointing to our Sql Server database schema we were able to generate the entire application - every line of code for every table (Actor, Movie, and MovieActor) with one button click. In fact React Turbo Scaffolding also generated: Automatic Paging, Sorting, Form Validation, and Error Handling for all views - features we removed for this tutorial. And if, as you were working through this tutorial, you noticed something you thought might have been better implemented a slightly different way, React Turbo Scaffolding features modifiable templates, allowing you to tweak the output it generates.  In short, it's a "power tool" that allows you and your development team to hit the ground running and produce complete results today - not three months from now.

Learn more ยป

We love React. We believe it's better than its competitors. We believe it has a promising future and its popularity will only increase, given that 10s of thousands of developers have already embraced it. Best of luck in your React development!

Contact Author

History

  • 11/8/2017 Initial Article

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here