Real-World Angular Series

Real-World #AngularJS Series - Part 3: Fetching and Displaying API Data ➤  #webdev

  • Part 3 of the tutorial series covers fetching data from MongoDB with a Node API and displaying and filtering it with Angular.
  • The third installment in the series covers fetching data from MongoDB with a Node API and displaying and filtering the data with Angular.
  • Now that we have API routes for fetching events, we need to access these routes in our Angular app so we can display events data.
  • The method attaches the necessary header using the access token stored in local storage from the authentication service we created in Angular: Basic Authentication in Part 2.
  • We want our API service to be available throughout our app, so let’s provide it in our :

    We can now import the service in any of our components to use its methods.

Build and deploy a real-world app with MongoDB, Express, Angular, and Node (MEAN): fetching, displaying, and filtering data.

@WaltSpence: Real-World #AngularJS Series – Part 3: Fetching and Displaying API Data ➤ #webdev

TL;DR: This 8-part tutorial series covers building and deploying a full-stack JavaScript application from the ground up with hosted MongoDB, Express, Angular (v2+), and Node.js (MEAN stack). The completed code is available in the mean-rsvp-auth0 GitHub repo and a deployed sample app is available at https://rsvp.kmaida.net. Part 3 of the tutorial series covers fetching data from MongoDB with a Node API and displaying and filtering it with Angular.

Part 3: Fetching and Displaying API Data

The second part of this tutorial covered authentication, authorization, feature planning, and data modeling.

The third installment in the series covers fetching data from MongoDB with a Node API and displaying and filtering the data with Angular.

API: Fetching Events

Let’s pick up right where we left off last time. We have data in our database, so it’s time to retrieve it with the API. We’ll start by writing four endpoints that will get data from MongoDB:

file and let’s begin.

collection.

mongoose schema in Part 2: Data Modeling. We can now use those schema to execute MongoDB collection methods with mongoose.

file:

because we only want public events with a starting datetime greater than or equal to now.

We also want to pass a projection (see first example). Projections state which fields we want returned in the documents that match our query. If no projection is specified, all fields are returned. In our case, we don’t need descriptions or locations in main event listings, so our projection will contain only the properties we do want returned.

In the callback, we’ll handle errors and iterate over any results, pushing them to an array that will be returned. We want an empty array if there are no events, since a lack of event documents simply means none have been created yet. Pretty straightforward!

. This time, we want authentication and admin privileges before we’ll send any data. We can implement this like so:

to leave out locations and descriptions.

endpoint:

method. If no event is found matching the ID we passed, we’ll send a bad request error. Otherwise, we’ll return the event.

. RSVPs in our app are transparent to all authenticated users; many people want to know if their friends are attending the same events they are.

, which will be passed with the request as a parameter. We want to return an array whether or not there are RSVPs, since a lack of RSVPs does not indicate an error.

Angular: Fetching Events

Now that we have API routes for fetching events, we need to access these routes in our Angular app so we can display events data.

To do this, we’ll create an API service. Let’s generate the service now:

folder. Open the service and add the following code:

) we created earlier.

when retrieving all RSVPs for an event.

header using the access token stored in local storage from the authentication service we created in Angular: Basic Authentication in Part 2.

methods, we’ll specify the arguments when calling the endpoint from our components.

Finally, we’ll handle successes and errors. A successful API call returns the response as JSON while a failed call checks the error message and prompts a fresh login if necessary, canceling the observable and producing an error if something else went wrong.

We can now import the service in any of our components to use its methods.

Since we’ll be making asynchronous API calls, it’s ideal to also have a loading state. Alternatively, we could use route resolve to prevent routes from loading until the necessary API data has been returned, but this can give an app the appearance of sluggishness while navigating. Instead, we’ll show a loading icon with a very simple component.

Generate the loading component like so:

We want this to be a single-file component, so we’ll set a few options with the Angular CLI:

Now let’s grab a suitable loading image. You easily can make your own at loading.io, or you can download this one:

folder and place the loading icon there.

and add the markup and a few simple styles:

in this case.)

file and replace its contents with the following:

Now we’ll see the spinner after the login redirect instead of plain text. We’ll also use the loading component when making API calls across other components.

Angular: Create a Utility Service

Before we start building out our components, let’s make a utility service that we can build on throughout development.

Run the following command to generate the boilerplate:

utility. Then we’ll create methods to manage the display of event dates. Each event has a start datetime and an end datetime. Start and end dates for a single event may be different days or the same day. We want a way to collapse same-day events into one date when displaying them in the UI. We also don’t need to show times on the main listings, only on detail pages. Finally, we’ll want a way to determine if an event already happened and is now in the past.

to help us craft some helper methods:

. We’ll add it to the constructor function’s parameters.

otherwise. This helps ensure that we don’t reveal the wrong UI state in our templates.

method accepts start and end dates, then uses the date pipe to transform the dates into user-friendly strings. If the start and end dates are the same, only one date is returned. If they’re different, the dates are returned as a range.

method does something very similar, but with times as well.

parameter and compares it to the current datetime, outputting a boolean that informs us if the event has already ended.

file and make the following updates:

We’re now ready to use our new utilities in components. We’ll add more methods to this handy service as we need them throughout development.

. Angular (v2+) uses pipes to transform data, but no longer provides out-of-the-box pipes for filtering or sorting for reasons cited here.

Due to the performance and minification impacts of this choice, we will not create custom pipes to implement filtering or sorting functionality. This would simply re-introduce the same problems the Angular team was attempting to solve by removing these filters. Instead, the appropriate approach is to use services.

Run the following command to generate a service with the Angular CLI:

file and add:

// src/app/core/filter-sort.service.ts import { Injectable } from ‘@angular/core’; import { DatePipe } from ‘@angular/common’; @Injectable() export class FilterSortService { constructor(private datePipe: DatePipe) { } private _objArrayCheck(array: any[]): boolean { // Checks if the first item in the array is an object // (assumes same-shape for all array items) // Necessary because some arrays passed in may have // models that don’t match {[key: string]: any}[] // This check prevents uncaught reference errors const item0 = array[0]; const check = !!(array.length && item0 !== null && Object.prototype.toString.call(item0) === ‘[object Object]’); return check; } search(array: any[], query: string, excludeProps?: string|string[], dateFormat?: string) { // Match query to strings and Date objects / ISO UTC strings // Optionally exclude properties from being searched // If matching dates, can optionally pass in date format string if (!query || !this._objArrayCheck(array)) { return array; } const lQuery = query.toLowerCase(); const isoDateRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; // ISO UTC const dateF = dateFormat ? dateFormat : ‘medium’; const filteredArray = array.filter(item => { for (const key in item) { if (item.hasOwnProperty(key)) { if (!excludeProps || excludeProps.indexOf(key) === -1) { const thisVal = item[key]; if ( // Value is a string and NOT a UTC date typeof thisVal === ‘string’ && !thisVal.match(isoDateRegex) && thisVal.toLowerCase().indexOf(lQuery) !== -1 ) { return true; } else if ( // Value is a Date object or UTC string (thisVal instanceof Date || thisVal.toString().match(isoDateRegex)) && // https://angular.io/api/common/DatePipe // Matching date format string passed in as param (or default to ‘medium’) this.datePipe.transform(thisVal, dateF).toLowerCase().indexOf(lQuery) !== -1 ) { return true; } } } } }); return filteredArray; } noSearchResults(arr: any[], query: string): boolean { // Check if array searched by query returned any results return !!(!arr.length && query); } orderByDate(array: any[], prop: string, reverse?: boolean) { // Order an array of objects by a date property // Default: ascending (1992->2017 | Jan->Dec) if (!prop || !this._objArrayCheck(array)) { return array; } const sortedArray = array.sort((a, b) => { const dateA = new Date(a[prop]).getTime(); const dateB = new Date(b[prop]).getTime(); return !reverse ? dateA – dateB : dateB – dateA; }); return sortedArray; } }

. We’ll add it to the constructor function’s parameters.

method to ensure that the array we’re trying to search or sort contains objects. If it doesn’t, uncaught reference errors will be produced, so we’d like a way to prevent this.

to search for, any optional properties we want to exclude from searching (either a single property string or an array of properties), and optionally, a date format string.

in the data.

for US).

, we’ll check the value for matches to the query.

when calling the method in our components.

if the array is empty and a query is present.

argument to change the sort order from ascending to descending.

If no property is passed, the array is returned unsorted.

array method to re-order the array by date timestamp.

file and make the following updates:

We can now search as well as sort our event arrays by date in our components.

Our components should get and display lists of events. We’ve already created the API endpoints to return this data and implemented an API service to fetch it. Now we need to subscribe to and display it in our components.

method.

and handle errors.

method.

acquired in the API call.

lifecycle method.

template:

method.

. (We’ll create this detail page a little later.)

If event data was retrieved but no events were returned in the array from the API, we’ll show a message saying that there are no upcoming, public events available.

Last, if there was an error retrieving data from the API, we’ll show a message. The console should also log the error message.

Our public events homepage should look something like this now:

If we type in a search query that returns no matches, we’ll see the following:

If there are no events available in the database, the homepage should show this message:

When an error occurs fetching events data, the homepage should look like this:

In Part 3 of our Real-World Angular Series, we’ve covered fetching data from the database with a Node API and manipulating and displaying the data in Angular. In the next part of the tutorial series, we’ll tackle access management, displaying the admin events list, and developing an event details page with tabbed child components.

Real-World Angular Series

You might also like More from author

Comments are closed, but trackbacks and pingbacks are open.