When creating UI/UX components before the API or DB is in place, there are a number of tricks that can be used to simulate an API call.
For the following examples, we have a model class called Student
. This class represents the shape of the data we're expecting. A huge benefit of working on the UI first is it tends to be much easier to make adjustments based on new information or something unrealized from a first examination. It is easy to change the model on the front end and change how the screen works without needing to update the DB and API.
export class Student {
id: number;
name: string;
age: number;
gpa: number;
}
Using RxJS "of"
Let's say we are developing a screen that needs to display a table of information. Since the endpoint is not available, or the exact structure of the data is unknown, we can create our service (or method) that can emulate the process of retrieving data.
In our data service we can create a new method call getStudents
. It will look something like this:
//import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class StudentService {
getStudents(): Observable<Student[]> {
return of([
{
id: 1,
name: 'Bob',
age: 42,
gpa: 3.14
},
{
id: 2,
name: 'Alice',
age: 21,
gpa: 3.14
}
]);
}
}
Using of
allows us to return a data set wrapped in an observable. When the API is ready, using the HTTP client will also return an observable of the same model. From here, we can call this method, subscribe to it, and make use of the data inside a component just like if an actual API call was made.
Create
Stubbing the create process is similar. Let's make some modifications to the previous code. We're going to move that array out of the class so that we can modify it.
@Injectable({
providedIn: 'root',
})
export class StudentService {
nextId = 2;
getStudents(): Observable<Student[]> {
return of([...this.students]);
}
createStudent(student: Student): Observable<Student> {
this.nextId++;
student.id = this.nextId;
this.students.push(student);
return of(student);
}
private students: Student[] = [
{
id: 1,
name: 'Bob',
age: 42,
gpa: 3.14,
},
{
id: 2,
name: 'Alice',
age: 21,
gpa: 3.14,
},
];
}
We're going to store a temporary variable to increment the ID of our student during creation. In order for us to get a clean copy of the array, we will take advantage of ES6's spread operation [...this.students]
.
createStudent
takes, of course, a student object and returns an observable of student. To simulate a post event to our API, we will set the ID of the student object to the next increment of nextId
, push it to the array, and return the student object with the ID set.
Edit
Edit is pretty simple because we're just going to replace the existing item in the array.
editStudent(student: Student): Observable<void> {
let studentIndex = this.students.findIndex(
(student) => student.id === student.id
);
this.students[studentIndex] = student;
return of(null);
}
Note that Edit does not return anything. In typical REST patterns for an API, unless a resource has changed, there is no need to return a resource. An "OK" status is enough.
Delete
Like edit, delete doesn't involve too much work. We're simply going to splice the array based on the ID.
deleteStudent(id: number): Observable<void> {
const studentIndex = this.students.findIndex(
(student) => student.id === id
);
this.students.splice(studentIndex, 1);
return of(null);
}
Delete also returns an empty result because the API should only provide an "OK" status.
That's it!
As you can see, it's pretty simple to mock out an API service in Angular. Except the RxJs parts, much of this could be done in normal TS/JS.
The best part of how simple this is, is that you can create the frontend outcome, write the logic for the screen flow and verify that what you have is the desire result.
When you start from the database, you might have in mind what needs to change. You can create the API and data layer to fulfill the backend requirements, but when you get to creating the UI, you might realize that you missed something, or there needs to be changes now that the UI can be seen. This might result in a new migration and code changes all the way to the database, costing time and effort.
Starting from the UI (or better yet, a wireframe) allows for a much faster reaction time to change.
I hope that helps you in your web development journey.
You can find a work demo of this concept at this Stackblitz .
Comments
You can also comment directly on GitHub.