Advanced 11ty: Using Objects in your UI
Tags: 11ty, await, javascript, tutorial
Published: 2022-02-05 17:34:55

In this tutorial, we will be writing an async / await call and then returning its response (an object) to the UI for further use.

Specifically, we'll be reading EXIF data from a photo in our images folder, but you'll be able to swap out our asynchronous call for any other asynchronous call you have, such as a fetch request, or another NPM package that you're using.

To begin, we're going to start with an 11ty filter. This is because filters can be chained together, we we'll need to do that in order to extract all of the object data on our front-end. I'll be using the Nunjucks syntax, but you'll be able to switch between Liquid or Handlebars fairly easily.

Inside our `eleventy.js` file, lets create our new filter:

```javascript
eleventyConfig.addNunjucksAsyncFilter("getExifData", async function(image, callback) {
   // Coming soon
}
```

Starting from the top line, the built in 11ty method `addNunjucksAsyncFilter` will create a promise for us that won't be resolved until we say so, using the `callback` parameter at the end. I'll get into more detail on that later.

After the method, we give our filter a name. In this case, "getExifData". This is how we'll call the filter on the UI. After that, we create our async function and in our example, we want to pass an `image` path to our filter for the processing. The last thing you include in the parameters is the `callback`. This is required or the promise will never complete, and sometimes 11ty can crash if its not there.

Now what we want to do is import our NPM package. I'll be using [ExifReader](https://www.npmjs.com/package/exifreader) for this.

*P.S. this is where you would swap out for your own asynchronous package / function.*

```bash
npm i exifreader
```

At the top of your `eleventy.js` file, you can call it inside your app like so:

```javascript
const ExifReader = require('exifreader');
```

Now that its added to our project, we can use it inside our new filter.

```javascript
eleventyConfig.addNunjucksAsyncFilter("getExifData", async function(image, callback) {
   const exifData = await ExifReader.load(image);
}
```

What this will do is take an image path thats supplied to the filter, run it through the NPM package, and return a gigantic object of all the EXIF data contained within the image. This particular package is asynchronous so we need to add the `await` parameter so that 11ty doesn't finish compiling before the promise is fulfilled. 

In my case, I dont need *all* of the data returned from this package, so I'm going to create my own `Object` of just the things I want.

```javascript
eleventyConfig.addNunjucksAsyncFilter("getExifData", async function(image, callback) {
   const exifData = await ExifReader.load(image);

    const extractedValues = {
        camera: exifData.Model.value,
        shutterSpeed: exifData.ExposureTime.value,
        fStop: exifData.FNumber.value,
    }
}
```

Now I've simplified what data gets returned, and this will make it much easier to call on the UI side. Otherwise, I would have to drill down each of the objects and that might look messy if we're using this filter a lot. This way, it is standardized before it hits the front-end.

Now we need to resolve our `promise`. We do that by using the last function parameter, `callback`, passing null as the first value, and then our data that we want returned as the second value.

```javascript
eleventyConfig.addNunjucksAsyncFilter("getExifData", async function(image, callback) {
   const exifData = await ExifReader.load(image);

    const extractedValues = {
        camera: exifData.Model.value,
        shutterSpeed: exifData.ExposureTime.value,
        fStop: exifData.FNumber.value,
    }

    callback(null, extractedValues);
}
```

Now we're ready to call this on the UI side.

Inside of your front-end files (in this case I'll be using blog-post.html), we can call the filter.

```twig
{{ "images/beach/sandy-waves.jpg" | getExifData }}
```

If you run this code, you'll probably see that it returns `[object Object]`. That means we're on the right path! Javascript doesn't let you print objects to the DOM as text, so we know that the `eleventy.js` filter is doing what it is supposed to do.

Now we can save this object into its own variable using a nunjucks function called `set`. And because we used a filter in the beginning, we can chain these together like so:

Inside your blog-post.html file change your expression syntax `{{ }}` to be the function syntax `{% %}` and call `set`:

```twig
{% set PhotoData = "images/beach/sandy-waves.jpg" | getExifData %}
```

Now we have our object stored in a way that we can work with.

In our UI, try this out:

```twig
<h1>Camera: {{ PhotoData.camera }}</h1>
```

And you should see your object data being compiled to the UI!

## Completed Code

```javascript
eleventyConfig.addNunjucksAsyncFilter("getExifData", async function(image, callback) {
   const exifData = await ExifReader.load(image);

    const extractedValues = {
        camera: exifData.Model.value,
        shutterSpeed: exifData.ExposureTime.value,
        fStop: exifData.FNumber.value,
    }

    callback(null, extractedValues);
}
```

```twig
{% set PhotoData = "images/beach/sandy-waves.jpg" | getExifData %}

<h1>Camera: {{ PhotoData.camera }}</h1>
```

This is how we can bring objects into our 11ty UI and parse them into our code. Hope you enjoyed this tutorial. 

This same process will work even if you dont use an asynchronous call. Just remove all the async / await parameters and then change your  `addNunjucksAsyncFilter` to `addFilter`.