Retrieve and display a file list

You are almost done. The application supports uploading a new file to the storage, retrieving it, and removing it. The problem occurs when a user navigates back to the application.

You can simulate that behavior. If you still have http://localhost:8080 opened in your browser, hit the refresh button. The list of files is gone! But they are still on the server in the user_upload directory.

The next step in this project is to implement a responsive list of files for the user_upload directory. The list shown in the browser window will be updated dynamically to reflect the contents of the directory and it will reflect the list of files in the directory when the application starts.

It’s possible to do that by adding another REST endpoint to our server that returns a list of files. This would be good solution when the back-end server code is running on a different machine from the code that does the server-side rendering.

But as long as the back-end code is running on the same server as the code that serves the front end, it doesn’t make sense to execute the Angular Universal (server-side rendering) code and execute REST calls to the same machine. Instead, you can use the fs module to list all files in a given path.

The previous post, Build Faster JavaScript Web Apps with Angular Universal, a TransferState Service and an API Watchdog, demonstrates how to implement isPlatformServer() and isPlatformBrowser() methods to determine which platform is executing the code. This project uses those functions as well.

The previous post also shows how to share data between the server and the client with the TransferState object by injecting it into the AuthService class. These methods help make fs module functionality accessible to the client-side code, even though the module itself can’t be loaded in the browser. This project also utilizes that technique.

The following diagram shows the sequence of events:

The user performs a GET / request to the server. Node.js receives the request. Node.js launches Angular and renders the view on the server. Data is stored in the TransferState registry. The server-side rendered view, including the browser-side JavaScript and TransferState registry, is passed to the browser and the Angular application is re-rendered in the browser.

There is one more thing to consider here. You know that browsers will not allow JavaScript code to manipulate the file system for security reasons. The webpack JavaScript module bundling system used by the Angular CLI won’t allow you to use the fs module for code built for the browser.

Since this project has a single codebase for both platforms, webpack interprets it as being built for the browser—which it is, in part. But it needs fs to read the directory contents and manipulate files, so it needs a solution that will get around the prohibition on running fs in the browser.

At this point you might think you need to create a separate codebase just for the server-side code, giving you two projects to maintain. But there is a technique which can enable you to maintain the single codebase and still manipulate files from the Angular executed on the server.

Angular has the ability to inject values and references outside the “Angular sandbox”. You can pass a reference to the Node.js function to the angular-side code and execute it from there.

Take a look at the following diagram:

The browser sends a GET / request to the server. Server fires Angular to render the view and calls the constructor() of the FileService . The constructor uses the isPlatformServer() method to determine if it is being executed in Node.js on the server. If so, the constructor calls the listFiles() method injected into FileService as a callback. The listFiles() method provides the current list of the contents of the user_upload directory, which is then stored in the fileList local variable. The list of files is stored in the TransferState object. The rendered view is send back to the browser and the browser displays the view and bootstraps Angular on the client. The client calls the constructor() again and uses isPlatformServer() to determine that the code is being executed on the client. The constructor() retrieves list of files from the TransferState object.

Implement server-side file manipulation

With the API endpoints in place you can complete the implementation of file manipulation operations from the client.

Open the server.ts file and locate the following line of code: