Accessing the database from PersonalDetails

We can now add database support to our PersonalDetails class. The first thing we are going to do is update the member variables and constructor to bring in the database support and store the list of people we want to display:

  1. First, we add the members:
private readonly dataLayer: Database<PersonRecord>;
private people: IPersonState[];
  1. Next, we update the constructor to hook up to the database and create TableBuilder using PersonalDetailsTableBuilder:
const tableBuilder : PersonalDetailsTableBuilder = new PersonalDetailsTableBuilder();
this.dataLayer = new Database(tableBuilder.Build());
  1. One thing that we still have to do is add the ability to show people into our render method. In a similar way to displaying the validation failures using map, we are going to apply map to the people array:
let people = null;
if (this.people) {
const copyThis = this;
people = this.people.map(function it(p) {
return (<Row key={p.PersonId}><Col lg="6"><label >{p.FirstName} {p.LastName}</label></Col>
<Col lg="3">
<Button value={p.PersonId} color="link" onClick={copyThis.setActive}>Edit</Button>
</Col>
<Col lg="3">
<Button value={p.PersonId} color="link" onClick={copyThis.delete}>Delete</Button>
</Col></Row>)
}, this);
}
  1. This is then rendered out with the following:
<Col>
<Col>
<Row>
<Col>{people}</Col>
</Row>
<Row>
<Col lg="6"><Button size="lg" color="success" onClick={this.loadPeople}>Load</Button></Col>
<Col lg="6"><Button size="lg" color="info" onClick={this.clear}>New Person</Button></Col>
</Row>
</Col>
</Col>

The Load button is one of a number of places that the loadPeople method is called from in this class. We will see it in use when we update and then delete the records.

When working with database code, it is common to encounter situations where the deletion of a record should not physically delete the record from the database. We might not want to physically delete it because another record points to that one, and so deleting it will break the other record. Alternatively, we might have to keep it in place for auditing purposes. In those cases, it is common to do something called a soft delete (a hard delete being the one where the record is deleted from the database). With a soft delete, there is a flag on a record that indicates whether the record is active. While IPersonState does not provide this flag, the PersonRecord type does because it is an intersection of IPersonState and RecordState. Our delete method is going to change IsActive to false and update the database with that value. The code that loads the people already understands that it is retrieving records where IsActive is true, so these deleted records will disappear as soon as the list is reloaded. This means that, while we wrote a Delete method in our database code, we aren't actually going to be using it. It's there as a handy reference and you might want to change the code to do a hard delete but this isn't necessary for our purpose.

The Delete button is going to trigger the delete operation. As there can be a number of items in this list, and we cannot assume that the user is going to select a person before deleting them, we need to find that person from the list of people before we attempt to delete them. Looking back at the code to render out the people, we can see that the ID of the person is passed across to the event handler. Before we write our event handler, we are going to write the method that asynchronously deletes the person from the database. The first thing we are going to do in this method is find the person using the find array method:

private async DeletePerson(person : string) {
const foundPerson = this.people.find((element : IPersonState) => {
return element.PersonId === person;
});
if (!foundPerson) {
return;
}
}

Assuming that we find the person from the array, we need to get the person into a state where we can set IsActive to false. We start off by creating a new instance of RecordState, as follows:

  const personState : IRecordState = new RecordState();
personState.IsActive = false;

We have an intersection type, PersonRecord, made up of the intersection of the person and record states. We are going to spread foundPerson and personState to give us our PersonRecord type. With this in place, we are going to call our Update database method. What we want to do, when our update has completed, is reload the list of people and clear the item currently in the editorjust in case it's the one that we have just deleted; we don't want the user to be able to reinstate the record simply because they save it again with IsActive set to true. We are going to use the fact that we can use await on code written as a promise to wait until the record has been updated before we carry on with the processing:

  const state : PersonRecord = {...foundPerson, ...personState};
await this.dataLayer.Update(state);
this.loadPeople();
this.clear();

The clear method simply changes the state back to our default state. That's the whole reason we passed it into this component, so that we can easily clear the values back to their default state:

private clear = () => {
this.setState(this.defaultState);
}

Using our delete event handler, the full code for this is as follows:

private delete = (event : any) => {
const person : string = event.target.value;
this.DeletePerson(person);
}

private async DeletePerson(person : string) {
const foundPerson = this.people.find((element : IPersonState) => {
return element.PersonId === person;
});
if (!foundPerson) {
return;
}
const personState : IRecordState = new RecordState();
personState.IsActive = false;
const state : PersonRecord = {...foundPerson, ...personState};
await this.dataLayer.Update(state);
this.loadPeople();
this.clear();
}

The last database operations we need to hook up to are triggered from the Save button. What happens with the save depends on whether we have previously saved the record, which can be identified by whether PersonId is empty. Before we attempt to save the record, we have to determine whether it can be saved. This comes down to checking whether the validation says we can save or not. If there are outstanding validation failures, we are going to alert the users that they cannot save the record:

private savePerson = () => {
if (!this.canSave) {
alert(`Cannot save this record with missing or incorrect items`);
return;
}
}

Similarly to how we used the deletion technique, we are going to create our PersonRecord type by bringing the state together with RecordState. This time, we set IsActive to true so that it is picked up as a live record:

const personState : IRecordState = new RecordState();
personState.IsActive = true;
const state : PersonRecord = {...this.state, ...personState};

When we insert our record, we need to assign it a unique value for PersonId. For simplicity, we are just going to use it with the current date and time. When we add the person to the database, we reload the list of people and clear the current record from the editor so that the user cannot insert a duplicate just by clicking on Save again:

  if (state.PersonId === "") {
state.PersonId = Date.now().toString();
this.dataLayer.Create(state);
this.loadPeople();
this.clear();
}

The code to update the person leverages the features of a promise so that the list of people is updated immediately after it has finished saving. We do not need to clear the current record in this case because if the user clicks on Save again, there is no possibility that we are going to create a new recordbut we will simply update the current one:

  else {
this.dataLayer.Update(state).then(rsn => this.loadPeople());
}

The completed method for saving is as follows:

private savePerson = () => {
if (!this.canSave) {
alert(`Cannot save this record with missing or incorrect items`);
return;
}
if (state.PersonId === "") {
state.PersonId = Date.now().toString();
this.dataLayer.Create(state);
this.loadPeople();
this.clear();
}
else {
this.dataLayer.Update(state).then(rsn => this.loadPeople());
}
}

There is one last method that we need to cover. What you may have noticed is that we have no way of selecting and displaying a user in the textboxes when we click on the Edit button. Logic dictates that pressing the button should trigger an event that passes PersonId to an event handler, which we can use to find the relevant person from the list; we have already seen this type of behavior when using the Delete button, so we have a good idea of what the selection portion of the code will look like. Once we have the person, we call setState to update the state, which will update the display through the power of binding:

private setActive = (event : any) => {
const person : string = event.target.value;
const state = this.people.find((element : IPersonState) => {
return element.PersonId === person;
});
if (state) {
this.setState(state);
}
}

We now have all the code we need to build our contact manager with React. We have satisfied the requirements that we set out at the start of the chapter and our display looks close enough to our mock layout.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.84.155