All posts by webammer_anand

Vue.js with TypeScript

By John Papa

I spent a few days on and off learning parts of Vue to write a small app. I wrote the same app with Angular. I’m sharing my experience of working through Vue for the first time to help others that may be curious about the JavaScript framework landscape.

This post explores how I refactored the Vue app to use TypeScript. The two places I found the most help were in this (Microsoft resource](https://github.com/microsoft/typescript-vue-starter) and the Vue docs. Neither was exactly what I needed, but together they were helpful in gaining success. Read on to learn more.

Disclaimer: This was my first attempt at using TypeScript in Vue. My intent is to share the journey, not to claim this is the “best” way to use TypeScript. I know TypeScript well, but I am learning Vue and leaning on the experts from the Vue document and TypeScript team’s resources on Vue for much of what I accomplished. I give credit to both of these teams for making these resources available and helping the community.

Why TypeScript?

Hold on a second … Why? I mean, why use TypeScript at all? JavaScript is awesome, why do we need TypeScript? Well, we don’t need* it. But I want it. But again … why? Why do I want TypeScript? This is a question we should always ask when someone tells us we should use something. Far too often we find ourselves reading how to do something … but the **why is often far more important.

Simply put TypeScript allows me to code faster and to catch and rectify more problems at development time than without it. Intellisense, auto-complete, and great tooling are enabled when editors can use the TypeScript features. Need some examples? OK … We can tell what the return type of an asynchronous function is an array of Hero models. Or we can find that we use a heroes array in one place as an array and in another as an ES promise. When the editor is smarter, I can adapt quickly and fix problems before I deploy.

If you are interested in how I got started with Vue, here are some other posts that may interest you:

Setup

I had to start somewhere, so I started by branching off of my connect2017 branch in this repo. This gave me a functional Vue app out of the box that I could begin refactoring to use with TypeScript.

I started by adding in the files that tell TypeScript how to do its job.

tsconfig.json

The first new file is tsconfig.json. I started with the file that the Vue documentation recommends here. Then I enhanced it a bit. I found the changes to be super helpful.

The following file tells TypeScript to put the compiled JavaScript in the ./built folder, to generate source maps for debugging and use TypeScript decorators (among other settings). I also added in lib support for some core ES2016 features.

webpack.config.js

I knew I needed to change how the build process worked. For this, I opened the webpack.config.js and immediately started to Google for some help. I find WebPack configuration to be painful … hey, it’s an awesome tool, but I admit that it’s not easy for me to figure out what I need to modify in there. Luckily I found this guide by the TypeScript team that showed how to modify the file.

These are the git changes I made to my file.

We can see that the entry file is now the main.ts instead of main.js. That makes sense. Then there are settings for a ts-loader, which helps load the TypeScript with Vue. Finally, I added some extensions to the resolve section.

Do I expect this is all perfect? Nope. It works … and this is the kind of thing that once the Vue CLI adds a TypeScript template to its tooling, we won’t have to mess with it at all. So let’s proceed.

npm packages

I’m adding TypeScript to the project so it makes sense that I had to add some packages to support this. (The Vue CLI should handle this once it adds the new TypeScript template, too.)

First I ran npm run typescript ts-loader --save-dev to add them as a development dependency. This came from the helpful Microsoft doc here. I deviated from that helped doc because I already had some of the others and I like to code dangerously.

Next, I ran npm install vue-property-decorator vue-class-component --save to install support for the TypeScript decorators. The decorators will help define classes as Vue components and define props.

Note: I only ended up using one of these directly. The @Component() decorator is in vue-class-component but it is imported and re-exported from vue-property-decorator.

vue.shims.d.ts

We can define files that help TypeScript and the tooling know how to handle some specific types. The helpful Microsoft doc here suggested adding the following code to a new file named vue.shims.d.ts. It basically makes it easier for the tooling to know how to handle *.vue files, also known as Single File Components (SFC).

Refactoring the Code Files

A great way to look at the changes required during my refactor is to use a file by file comparison. Let’s begin.

main.js –> main.ts

I renamed the main.js file to main.ts. Yep, no code changes in this one. Move along.

App.vue

The App component is the root component in the Vue app. Here is its original file using Babel with JavaScript.

The next image shows the TypeScript. Take a glance, then read on to learn how I refactored the code to get here.

First, I added a hint to the tag to let it know I will be using TypeScript. Notice the tag is now in the image below.

I added the @Component() decorator to both tell Vue that the class following the decorator is indeed a Vue component and that this component will reference a child component named HeroList. This components property in the decorator was lifted right from the components property in the JavaScript example.

I refactored the default export to become a named class App that extends Vue. This makes sense as the name of our component is App. We extend Vue as a way of preparing our class to be a component. See an example in the Vue docs here.

I refactored the data properties to become public class properties. Then I initialized the title property in the constructor. I could also have done this in a lifecycle event, which probably makes more sense.

Notice I added a few import statements to the top of the file. This helped the tooling and TypeScript know where to get the Vue and Component symbols.

HeroList.vue

The HeroList.vue file contains the logic for getting and displaying the heroes. The JavaScript version is shown below for reference. Notice I collapsed the components and methods to make it easier to follow. Full source code can be found at the end of this post.

Here follows the TypeScript version of the file, after my refactoring effort. Take a moment to see the differences and how they map to each other.

Just as with the App.vue file, the components moved up to the decorator and the data properties became public class properties. There are some subtle differences here though as we look closer. Notice that I added explicit types to selectedHero and heroes. I did not need to do this, but it helped clarify things.

The selectedHero was initialized to null so the details wouldn’t appear until a selection had been mad. But now that I have a Hero type
(we’ll explore that in the next section), I wanted to be clear to the compiler of my intentions that the selectedHero should be allowed to be null or of type Hero.

But perhaps far more interesting is how I went from initializing the heroes array right in the data function in the JavaScript example to initializing it in the created life cycle event in the TypeScript example. This is one place I found a bug in the JavaScript example. I was setting the heroes to the return value from the getHeroes function … which was not an array of heroes, but instead a promise of an array of heroes. This did not bite me in the running code because I was, but it could have. It worked only because the getHeroes function sets the heroes array … so it immediately ran and filled the array anyway. But this just hid the bug. When I refactored it to use TypeScript the editor alerted me that I had a type mismatch. Then I moved the call to getHeroes to the created lifecycle event. Ladies and gentlemen, that’s a real bug … not something I staged. Oops! But thankfully the editor caught my mistake and I was able to correct it during the refactor.

We also have some new imports up top in the TypeScript file and I added some more explicit typings in a few places, so I could make sure I had no more hidden bugs. That’s the value of setting noImplicityAny: true in the tsconfig.json file.

Hero Model

Now that I have types, I added a hero.ts file to define a Hero class. This helped make sure the components that deal with heroes are using the right types and properties.

HeroDetail.vue

The HeroDetail component had many of the same changes as the HeroList component. Some imports up top, the @Component() decorator, moving the data properties to public class properties, and defining some explicit types.

Check out the JavaScript version here.

Now take a glance at the TypeScript version, after the refactoring effort.

What’s new here is that I am using the @Props() and @Watch() decorators in the TypeScript version. Notice the hero that I was watching in the JavaScript version is now a public class property decorated with @Prop(). This was a simple refactoring.

Then I defined a named function onHeroChanged and decorated it with the @Watch() decorator. This went from a function named hero in the JavaScript version to a named function with a @Watch() decorator in the TypeScript version.

I access the $refs to grab the elements to set focus on them. I made a public class property named $refs and typed it with the names of the two references I had in the Vue template. Both are of type HTMLElement.

Hero Service

The hero service refactor was quite simple. The TypeScript version became an instance of a class that I could import into the components and use as needed. Nothing out of the ordinary here. This could have simply been a set of exported functions, too. There are lots of ways to handle services in JavaScript/TypeScript.

Summary

The best question to ask is “was it worth it?”. Only you can decide. I think it was worth it. It caught a bug I had in the JavaScript version and now I get a lot more help from my tools, like VS Code, when writing the app.

I’m looking forward to the official TypeScript template for the Vue CLI. But in the meantime, there are a few ways to use TypeScript with Vue projects.

Source Code

You can find the source code for these apps here:

Source:: johnpapa

Node.js Weekly Update - November 24

By Tamas Kadlecsik

Node.js Weekly Update - November 24

Below you can find RisingStack‘s collection of the most important Node.js updates, projects & tutorials from this week:

Putting the helmet on – Securing your Express app

Express is a great way to build a web server using Node.js. However, out of the box Express doesn’t adhere to all security best practices. Let’s look at how we can use modules like helmet to improve the security of an application.
Node.js Weekly Update - November 24

Node.js + OpenCV for Face Recognition

Today we are going to take a look at the Fisher-, Eigen- and LBPH FaceRecognizers implemented in the OpenCVs’ face module and build a simple Node.js face recognition example.

Node.js Weekly Update - November 24

Monitoring the performance of a Node.js web application

This article explains the key elements to building a monitoring tool – from instrumenting code to reporting collected data, in a clear visualisation.

Node.js Weekly Update - November 24

Writing fast and safe native Node.js modules with Rust

RisingStack faced a shocking event last year: we reached the maximum speed that Node.js had to offer at the time, while our server costs went over the roof.

What can you do if you are facing a similar issue? Implementing Rust modules in your Node.js code can be a possible solution. In this Rust tutorial we’ll show you how to do it.

Survey: Learning Node.js in 2018

We created a 2-minute survey to ask the Node.js community what would they like to learn in 2018. Let us know which areas are troublesome for you and we’l make sure to cover it.

https://rstck.typeform.com/to/lqxo1P

Node.js Weekly Update - November 24

Node Color Log

node-color-log is a Node.js package that enables you to log text with colorful font and colorful background. It provides a better coding experience when developing your apps. Here’s how to set it up.

How to Setup Node.js Server on your Local Windows or Linux Machine

This article walks you through installing a Node.js server on your PC – if you haven’t yet.

Previously in the Node.js Weekly Update

In the previous Node.js Weekly Update we collected the latest news on Node.js such as the Node v9.2.0, as well as the best tutorials on debugging memory leaks, Node security, native patterns, and wiring up a GraphQL server with Node and Express. Click if you missed it!

We help you to stay up-to-date with Node.js on a daily basis too. Check out our Node.js news page and its Twitter feed!

Source:: risingstack.com

A Look at Angular Alongside Vue - Familiar Code

By John Papa

I spent a few days on and off learning parts of Vue to write a small app. I wrote the same app with Angular. I’m sharing my experience of working through Vue for the first time to help others that may be curious about the JavaScript framework landscape.

This post explores the code for both the Angular and Vue apps. Spoiler: you’ll find that the code and concepts are very similar. 🙂 I had a lot of fun working through Vue with Sarah Drasner. Pair programming or simply just talking to friends about code is a great way to learn and share ideas.

We all benefit when we learn to enjoy and embrace the similarities and the differences that help push the Web forward.

Both apps are very small. Both apps are not indicative of how we may refactor to expand to build a large scale app. In fact, the tldr; is that both apps in Angular and Vue have a lot in common.

Here are the posts in this series, if you want to catch up or jump around:

index.html

Let’s begin with the file that starts it all: index.html. Notice that both of these are very similar and very short. Each uses WebPack for a build process and thus defers to WebPack to crank things up.

On the left, Angular adds a few extra things. Here we see a tag which is useful once we add routing. We haven’t added routing yet, so the tag is uncesessary at this moment. Angular also adds the viewport and a favicon. None of this is a big deal.

The is where we find Angular hosting the root component app-root and Vue hosting a

where the root component will be associated. Again, fairly similar.

The Vue code adds in a build.js script, and at first glance, it seems the Angular one does not. Angular adds the startup scripts in the built version of the app. In other words, when we build the Angular app, the index.html will contain these scripts too.

Thus the index.html files are fairly similar, and it’s kinda cool how small they are.

main.js

Both apps kick their code off from a main.js file. Wait, the Angular app uses TypeScript, so isn’t it a main.ts file? Yeah – but – TypeScript compiles to JavaScript, and it becomes – you guessed it – main.js.

Both apps import from their frameworks, respectively. Both apps then import the root file for their frameworks. Angular imports the AppModule while Vue imports the root App component. Angular then has a quick check to disable some features when it is running a production build vs. a development build which is helpful for turning off debug capabilities.

The syntax to kick things off and launch the starting root component then follows. Angular bootstraps the AppModule while Vue associates the App component with the HTML element with the id app that we saw in index.html.

Notice that the Vue app also declares the component App. We’ll come back to this in the next section.

Different syntax, different frameworks, but again, pretty simple stuff to kick things off from both.

app.module

This is one of the more apparent differences between Angular and Vue: Angular apps contain a manifest of the leading players in an app while Vue doesn’t have this concept. The file app.module.ts contains AppModule which defines a class decorated with the @NgModule() decorator.

In this Angular app, the app.module.ts file declares the components used by the app, imports Angular features (e.g., browser, forms, and HTTP), declares the services and defines which component should kick things off. Remember I mentioned how the Vue app declared one of its components in the main.js? It’s not the same exact thing, but we can see here that the Angular app has a similar declaration. Except with Angular, the file app,module.ts declares all of the components in this small app.

The Vue app kicks off the root component App in it’s main.js, while the Angular app kicks off its root component AppComponent in app.module. Different patterns, but very once again, easily digestible patterns to follow in both cases.

App Component

Each app has a root component. The Angular one uses a convention to name its components with a suffix of Component. So Angular has AppComponent while Vue has App. Both have similar templates. Vue’s App component has an id in the div and both frameworks list their child components’ selectors differently with app-hero-list and HeroList, for Angular and Vue respectively. Both apps also contain some root styling.

The Angular app has a small class with one model property for title. The Vue component defines one model property for title, too. The Vue App component declares it’s child component, HeroList here, directly in the component that uses it. The Angular app declares these back in the manifest in the app.module.ts file.

One difference that stands out is that the Angular app uses a *.ts file while Vue uses a *.vue file. As long as the editor and tooling know how to display them, let us edit them, give us tooling, and build them, I don’t think it matters much what the extension is.

Once again, we see how easy it is to observe the similarities.

Hero List Component

Each app has a component for displaying the heroes to the user, from where they can select a hero to see his/her details. Both are using inline templates, which define the HTML for the compoennts.

The Angular component, hero-list.component.ts, imports from its framework, then it imports a model for the Hero class, then imports a HeroService. The Vue app imports a hero service (down on line 31) and then it imports its child component, from HeroDetail.vue. They both import a service, but the other files differ. These are minor differences, though.

Angular already declared its HeroDetailComponent component in app.module. The Angular app uses a declared Hero model class. The Vue app could certainly use a model, but there was no need to do so here.

Here comes the coolest part of the similarities: the templates are extremely similar to each other. Take a moment to look at the HTML templates side by side. They both have the same number of lines and very similar syntax. The Angular app defines the template in a string while the Vue app establishes the template inside of a tag.

The binding syntax is quite interesting too. We’ll explore the syntax for template bindings in more the next post in this series.

The bindings for Angular and Vue are indeed interesting. In Angular, we have a (click) binding syntax while in Vue we use @click. Here are some other similarities in the bindings, in the following table.

binding Angular Vue
click event (click) @click
enter key-up event (keyup.enter) @keyup.enter
add/remove content *ngIf v-if
model binding [(ngModel)] v-model
HTML element reference # ref

Wait! What about the code? Once again, very very similar code.

The Angular app defines a TypeScript class with properties that define the hero list’s model. The Vue app establishes the model in a data property. These are the same three model properties.

The Angular app uses dependency injection to get a reference to the hero service, while the Vue app imports its similar service. Different styles, but it is easy for us to make the transition in either direction here.

Both apps have the same set of methods. Angular uses RxJS by convention to communicate with HTTP while Vue uses Axios with promises by convention. That’s why we see subscribe for Angular and then for Vue in the calls to the service, for the asynchronous operations. These are just different techniques, and the code is very similar in this side by side example.

Both of these components also have scoped styling using SASS. Scope styling is kinda cool since the styles will only apply to the components. This makes it easy to identify which styles will apply to the component and perhaps, more importantly, this makes it easy to ensure that styles only apply where we intend.

Hero Detail Component

Both hero detail components are also very similar. The Angular file hero-detail.component.ts starts by importing the features (referencing them). Then they both define their respective templates. Angular tends to explicitly define the features they use, which is why we see more imports at the top of the file.

The templates are similar other than binding syntax, just like the hero list components’ templates.

The code for these components is quite interesting. The Angular HeroDetailComponent uses the @Input() decorator to identify that the hero model property can be passed in (via a binding) from the parent component. The Vue HeroDetail component uses the props property to identify its hero model property can be passed in from its parent component. We get the same result, with different techniques.

Each component defines two model properties for addingHero and editingHero. Angular does this with class properties while Vue does it as members of the data property.

Both apps set focus to elements once they are rendered and ready on the Web page. However, the way they get to those elements is interesting. The Angular app grabs references to the two elements using the @ViewChild() decorators while the Vue app uses an inline reference in the template combined with this.$refs in its code to reference its elements. The Angular the app handles the focus in the life cycle event ngAfterViewInit while the Vue app is handling setting the focus in the mounted event.

Take a close look at the @Output() decorators in the Angular app and we’ll see that these help us emit events from the HeroDetailComponent to its parent component. Notice that for Angular, the event emits on line 64 in the clear method. The Vue app also emits events. Vue uses the this.$emit function to do the same thing (see line 56 in Vue’s HeroDetail component). Obviously Angular and Vue have different syntax for some things.

Overall, the components are once again very similar and it is easy to see how once you know one of these frameworks, it can be easy to use the other.

Summary

Where does this leave us? Good question. The takeaway for me is that I found it quite easy to straddle the line between Vue and Angular. I can take an Angular app and see fairly quickly how to translate the way I do things in Angular and how Vue might do them.

The intent here is to show two interesting ways to solve a problem – to build apps for the modern web. Both Angular and Vue share some interesting concepts. Both are paying close attention to how the Web is evolving, and they are evolving with it.

We all benefit when there are multiple great options, especially when we look at them in a friendly, welcoming, and open-minded way. Both Angular and Vue are popular (along with some others like React). All of these are great solutions.

I’ll repeat this because it’s important …

We all benefit when we learn to enjoy and embrace the similarities and the differences that help push the Web forward.

Source Code

You can find the source code for these apps here:

Source:: johnpapa

Webinar: Going From jQuery to Vue - Dec 13 - 11am PST

By Chris Sevilleja

Hey there Scotchers. We’ll be doing a free webinar coming up on one of our favorite JS libraries, Vue. Make sure to sign up and add it to your calendar to join in on the fun.

This is a good crash course for those of you that have heard about Vue but have yet to check it out.

Register for Free

About the Webinar

Vue.js is taking the JavaScript world by storm. Currently sitting at #6 on GitHub’s most starred repos, Vue is the progressive JS framework that is tons of fun to work with.

We’ll see how to get started with Vue for developers coming from vanilla JavaScript or jQuery backgrounds. Learn why you should invest into a JS framework like Vue.

We’ll learn Vue basics and build a simple Vue app!

Discussion Topics

We’ll be talking on:

  • Learning why Vue is awesome
  • How to move from jQuery to Vue
  • Why you would want to use Vue
  • Build a simple Vue app

Your Teacher

Scotch Founder, Chris Sevilleja (that’s me!) (@chrisoncode) will be handling the webinar/workshop duties.

Source:: scotch.io

Getting Started with Firebase Cloud Firestore: Build a Vue Contact App

By Yomi Eluwande

Sometime back in October, Google announced the beta release of Firebase’s Cloud Firestore which is a fully-managed NoSQL document database for both mobile and web app development. Firestore was designed to easily store and sync your app’s data at global scale.

Cloud Firestore includes features such as:

  • Documents and collections with powerful querying
  • iOS, Android, and Web SDKs with offline data access
  • Real-time data synchronization
  • Automatic, multi-region data replication with strong consistency
  • Node, Python, Go, and Java server SDKs

In this tutorial, I will demonstrate how to get started with Firebase Cloud Firestore and also highlight the differences between Cloud Firestore and the existing Firebase Realtime Database.

We’ll be building a Contact Management app, it’s a basic CRUD app and Cloud Firestore acts as the backend database. You’ll be able to see all contacts, add a new contact and view an individual contact. The Contact Management app will be built using Vue.js.

A screenshot of the contact management app can be seen below and you can check out a live demo here.

To get started with the app, we’ll be using vue-cli to quickly scaffold a Vue.js app and even more specifically, the webpack template will be used.

Open a terminal window and run the following command to install the vue-cli tool:

npm install -g vue-cli

Let’s now create the Vue.js app, Run the following command in a working directory:

vue init webpack firestore

This will go ahead and create a folder titled firestore containing a working Vue.js app with features like Hot Module Reloading, Webpack and vue-loader for single file components, and routes.
Navigate to the firestore folder and run the npm run dev command to see the app live at http://localhost:8080.

Setting up Cloud Firestore

To begin using Firestore, you’ll need a Gmail account, once that’s good to go, you can sign in to https://console.firebase.google.com and create a new project.

You should get a dashboard similar to the one below. This dashboard contains all of Firebase’s other services that can be used in your application.

Click on “Add Firebase to your web app” and copy the config details specific to your project to a safe location as you’ll be needing it later.

Under the Develop menu, click on the Database link and under the database tab, click “Try Firestore Beta” and “Start in Test Mode”. That should take you to a dashboard where you can view the database as it changes in real time.

With Firestore, we’ll be dealing with documents and collections. Data is usually stored in documents, documents are stored in collections. A document may contain a collection and a collection may contain a document.

Vue.js and Firestore

Let’s get back to coding. Open the components folder and create two JavaScript files in it; firebaseConfig.js and firebaseInit.js.
The firebaseConfig.js file will contain the config values gotten from the Firebase dashboard, we’ll be exporting so it can be used in other locations.

export default {
  apiKey: 'AIzaSyDr0-Mef6D1RZsD2NoBaPOwordhUW58MyU',
  authDomain: 'contacts-app-dca62.firebaseapp.com',
  databaseURL: 'https://contacts-app-dca62.firebaseio.com',
  projectId: 'contacts-app-dca62',
  storageBucket: 'contacts-app-dca62.appspot.com',
  messagingSenderId: '715354469790'
}

We have a Firebase config file, but we need to initialize it somewhere, we can do that in the firebaseInit.js file like this:

import firebase from 'firebase'
import 'firebase/firestore'
import firebaseConfig from './firebaseConfig'
const firebaseApp = firebase.initializeApp(firebaseConfig)
export default firebaseApp.firestore()

In the code block above, firebase and firestore were both imported, as well as the firebaseConfig.js file created earlier. The config file is used to initialize Firebase in the project and is also exported with the firestore function.

Let’s create the different components that will be used for the app. We’ll be needing Home.vue, NewContact.vue and ViewContact.vue components. You can go ahead and create these files in the components folder.

Before we edit each component, let’s update the router file to accommodate these new components. Navigate to the router folder, open up the index.js file in it and edit it with the code block below:

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import ViewContact from '@/components/ViewContact'
import NewContact from '@/components/NewContact'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/add',
      name: 'new-contact',
      component: NewContact
    },
    {
      path: '/:person',
      name: 'view-contact',
      component: ViewContact
    }
  ]
})

In the code block above, we imported the components to be used for the different routes that will be used in the app. The Home component is responsible for serving the / route, the NewContact component is used to serve the /add route and the ViewContact component is used to serve the /:person route.

Now that the routes have been defined, we can begin editing the components. Let’s start with the NewContact component. Open up the NewContact.vue file in the components folder. It’s very common for .vue files to be divided into three sections; , and . I’ll highlight the code in each section before putting it all together at the end.

The section contains the main JavaScript code and you can proceed to put in the following code block

  <script>
    import db from './firebaseInit'
    export default {
      name: 'new-contact',
      data () {
        return {
          firstname: null,
          lastname: null,
          emailaddress: null,
          phonenumber: null
        }
      },
      methods: {
        saveContact () {
          db.collection('contacts').add({
            firstname: this.firstname,
            lastname: this.lastname,
            emailaddress: this.emailaddress,
            phonenumber: this.phonenumber,
            slug: this.generateUUID()
          })
            .then(function (docRef) {
              console.log('Document written with ID: ', docRef.id)
            })
            .catch(function (error) {
              console.error('Error adding document: ', error)
            })
        },
        generateUUID () {
          let d = new Date().getTime()
          let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            let r = (d + Math.random() * 16) % 16 | 0
            d = Math.floor(d / 16)
            return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
          })
          return uuid
        }
      }
    }
</script>

In the code block above, the firebaseInit.js file which was created earlier, is imported as db. In the methods object, there’s the saveContact function which essentially saves the new contact to the Firestore DB.
This line db.collection('contacts').add() is a way to add data to the Firestore DB, it’s a way of saying add this data to the collection called contacts (or create one if it doesn’t exist.). Cloud Firestore stores data in Documents, which are stored in Collections.
Inside the .add() function, the contact details gotten from the form which will be created soon is being sent to Firestore along with a unique slug which is generated by the generateUUID function.

Next, we’ll edit the section and edit the HTML needed to create the form to add new contacts.

<template>

  <section class="container">
    <h1>Add New Contact</h1>

    <form @submit.prevent="saveContact">

      <div class="field">
        <label class="label">First Name</label>
        <div class="control">
          <input class="input" type="text" placeholder="First Name" v-model="firstname" required>
        </div>
      </div>

      <div class="field">
        <label class="label">Last Name</label>
        <div class="control">
          <input class="input" type="text" placeholder="Last Name" v-model="lastname" required>
        </div>
      </div>

      <div class="field">
        <label class="label">Email Address</label>
        <div class="control">
          <input class="input" type="email" placeholder="Email Address" v-model="emailaddress" required>
        </div>
      </div>

      <div class="field">
        <label class="label">Phone Number</label>
        <div class="control">
          <input class="input" type="text" placeholder="Phone Number" v-model="phonenumber" required>
        </div>
      </div>

      <div class="field">
        <div class="control">
          <button type="submit" class="button is-link">Submit</button>
        </div>
      </div>

    </form>
  </section>

</template>

In the code block above, we created the HTML form that adds a new contact to the Firestore DB. The value of each input tag is bound to the defined properties in the data() function via Vue.js’ v-model.

The styling for this form was done in the </style section and can be seen below.

<style scoped>

  section {
    height: 100vh;
  }

  h1 {
    font-size: 30px;
    margin: 30px 0;
  }

  .input {
    height: 40px;
  }

</style>

Here’s the NewContact.vue file in its whole entirety.

The next component we’ll go through is the Home component. This component is used to serve the / route and it simply displays all the contacts in the Firestore DB and an optional button to view a contact individually.

Open up the Home.vue file and edit it with the content of the GitHub Gist below.

Let’s go through the code block above, the div with a class of loader-section and conditional rendering v-if=loading is essentially a placeholder and acts as a loading state until the required data is successfully fetched from Firestore.

The div with a class of user-list and directive v-for=person in contacts is the UI needed to populate all the contacts from the data gotten from Firestore. We’ll see how to query data from Firestore next.

In the section of Home.vue, the first line of code is used to import the Firebase config just like we did in the NewContact component. In the created() function, the line db.collection('contacts').get() is used to get all data from the collection named contacts. The data is then pushed into the existing contacts array which was defined in the data() function.

One other thing to note in the code block above is the button/link that leads to the page where you can view an individual contact.

<router-link class="button is-primary" v-bind:to="{ name: 'view-contact', params: { person: person.slug }}">View Person</router-link>

The v-bind:to directive is used to dynamically bind the person segment to the contact’s slug. This basically means that you use the slug to access an individual contact page, so something like this: http://localhost:8080/#/7099198a-5ec5-48ae-b8de-2befb5352f78 where the random string of characters is the slug. This was established in the routes file above where we did something like this:

{
      path: '/:person',
      name: 'view-contact',
      component: ViewContact
    }

The next and last component to be edited is the ViewContact component. Open up the ViewContact.vue file and edit it with the content of the GitHub Gist below:

Let’s go through the code above. The section contains the UI that’s set to display the details of the contact. The various text interpolations in the template section are set to defined properties in the data() function in the section.

The beforeRouteEnter() function is a navigation guard. Navigation guards are used to guard navigations either by redirecting it or canceling it. Navigation Guards may be resolved asynchronously, and the navigation is considered pending before all hooks have been resolved.
That means until the guard has been resolved, users won’t be able to visit the route in which the navigation guard is. It can be used to check if a user is authenticated in an app or some data has finished loading.

The beforeRouteEnter() function accepts three parameters, to, from, next.

  • to: the target Route Object being navigated to.

  • from: the current route being navigated away from.

  • next(): this function must be called to resolve the hook.

In this case, we are using the navigation guard to ensure that the data from Firestore has been successfully retrieved. So how exactly are we getting the data for a particular document in Firestore?

We use the .where() function to query for the data required as seen below:

db.collection('contacts').where('slug', '==', this.$route.params.person).get().then((querySnapshot) => {
      querySnapshot.forEach((doc) => {
        console.log(doc.id, ' => ', doc.data())
        this.firstname = doc.data().firstname
        this.lastname = doc.data().lastname
        this.emailaddress = doc.data().emailaddress
        this.phonenumber = doc.data().phonenumber
      })
})

The where() function takes in three parameters; an existing name of a field key in the Firestore DB (in this case, slug), the == sign, and the string in which to query the Firebase DB with (in this case, the $route.params.person string).

One more thing before running the app, we’ll need to add the Bulma CSS framework to the project. You can do that by simply adding the line of code below to the index.html between the head tags.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css">

With that being done, you can run the app with the command npm run dev and you should the app live at http://localhost:8080

Differences and similarities between Cloud Firestore and Realtime Database

Now that we have seen how easy it is to get started with Firestore, it’s time to ask questions like:

  • What’s the difference between Cloud Firestore and Realtime Database?
  • How do I determine when to use Cloud Firestore or Realtime Database?
  • Will Realtime Database be deprecated?

There really is no difference between the two technologies, they both offer the same functionalities as seen here, the only advantage Cloud Firestore has, is in terms of scaling. Cloud Firestore was built in close collaboration with the Google Cloud Platform team and that means Cloud Firestore was built to scale right from the beginning.

Also, the Realtime Database is basically a JSON tree where you can store anything with no real structure or organization, Cloud Firestore is actually structured. Cloud Firestore is a document-model database, which means that all of your data is stored in objects called documents that consist of key-value pairs — and these values can contain any number of things, from strings to floats to binary data to JSON-y looking objects the team likes to call maps. These documents, in turn, are grouped into collections.

The Realtime Database may be used in cases where you are trying to optimize for cost and latency, and you can even go ahead to use both databases together. This also means Realtime Database is not going anywhere, both databases co-exist.
It’s also important to note that Cloud Firestore is in beta and it would be good to keep abreast of new developments so as to know if there will be any breaking changes.

Conclusion

In this tutorial, we saw how easy it is to get started with the new Cloud Firestore by Google and also touched on some points when comparing it to the already existing Realtime Database.

The codebase for the app we built above can be seen here. Feel free to play with it and build stuff. The live demo also exists here.

Source:: scotch.io

Custom Overlays with Angular's CDK

overlay preview

You have probably heared of Angular Material haven’t you? If you haven’t, it’s a library that provides you with high-quality Material Design components for Angular. Material Design itself is a visual design language that aims for consistency of user experience across all platforms and device sizes. That’s cool but what if your company has its own opinions about styles and the overall look and feel of the UI? How do we get the best of Angular Material without adopting the Material Design visual language?

Tada 🎉! That’s where Angular Material’s Component Dev Kit (CDK for short) comes into play. The CDK provides us with tools to build awesome and high-quality Angular components without adopting the Material Design visual language. Its goal is to make our life as developers easier and extract common behaviors and patterns shared between multiple Angular Material components. For instance, the datepicker, snackbar, or tooltip have something in common; they need to dynamically open up some floating panel on the screen. But that’s just the tip of the ice berg. There are many different packages for all sorts of things such as a11y that helps us improve the accessibility of our UI components. There’s even a layout package with utilities to build responsive UIs that react to screen-size changes. For a more complete list, please check out the official documentation.

Over at MachineLabs, we thought it would be useful to provide a way to preview generated output files (mostly images), so users don’t have to download it every single time just to take a quick look. So we sat down to build a Google Drive like overlay with the CDK. This post is meant to share our knowledge with the community and to make you comfortable using the CDK for your own purposes.

In this post, we’ll use the CDK to build a Google Drive-like custom overlay that looks and feels much like the one built for MachineLabs. Here’s how it looks like:

The building blocks

Let’s start simple and work our way up to the final, fully-fledged solution which will have a very similar API as the MatDialog service provided by Angular Material. It’s not important to know exactly how the MatDialog works but it’s definitely helpful. If this is new to you, we recommend to check out our post on Easy Dialogs with Angular Material.

Our solution will be a little less flexible but specifically made for showing a file preview inspired by Google Drive. That said, we’d like to have a nice toolbar at the top and the image being rendered in the middle of the screen.

In general, the MatDialog is great for showing content in a dialog box but as soon as we want a little bit of a custom look and feel, something that does not look like a white box with content inside, we would need to roll our own overlay. Luckily, we can use the overlay package from the CDK that has most of the core logic for opening floating panels already baked in. More on that in just a second.

Here are the core building blocks of our application:

application architecture

As we can see, we have two components, one service and a class that represents a remote control to an opened overlay. The AppComponent is the root (or entry point) of our application. This component contains a toolbar and the list of files that we can preview. In addition, it has access to a FilePreviewOverlayService which provides us with the core logic for opnening an overlay. At the same time it’s an abstraction for some “heavy” lifting that should be implemneted in a resuable manner. Don’t be scared, it’s not going to be super heavy and we’ll break it down into comprehensible chunks. Last but not least, there’s a FilePreviewOverlayRef which, as mentioned, is a handle used to control (e.g. close) a particular overlay.

For the overlay we choose to render a component, so we can attach some logic and also add animations to our overlay to engage our users and make them happy. We call this component FilePreviewOverlayComponent.

That’s about it. Now that we have the basic structure in place, we’re ready to look at some code.

Note that this post is the first part out of two in which we lay the foundation for our custom overlay. We’ll build on top of this in the next part and add keyboard support, image preloading and animations.

Setup

Before we can start implementing the custom overlay we need to install the CDK. Simply run npm install @angular/cdk and we’re all set!

Our first overlay

From the MatDialog we know that when we open an overlay we must specify a component type that is then created dynamically at runtime. This means it is not created by using the component tags inside an HTML template. Also, we know that whenever a component is created at runtime, we must add it to our application module’s entryComponents.

Let’s do that and add the FilePreviewOverlayComponent to the arry of entryComponents. In addition, we need to add the OverlayModule to the imports list of the root AppModule:

import { OverlayModule } from '@angular/cdk/overlay';
...

@NgModule({
  imports: [ ... ],
  declarations: [ ..., FilePreviewOverlayComponent ],
  bootstrap: [ AppComponent ],
  providers: [ ... ],
  entryComponents: [
    // Needs to be added here because otherwise we can't
    // dynamically render this component at runtime
    FilePreviewOverlayComponent
  ]
})
export class AppModule { }

From there, creating an overlay is easy. First, we inject the Overlay service. This service has a create() function that we need to call in order to create a PortalHost for our FilePreviewOverlayComponent. Finally we need to create a ComponentPortal from this component and attach it to the PortalHost. Wait, what? Let’s give it a moment and look at some code before taking it apart:

@Injectable()
export class FilePreviewOverlayService {

  // Inject overlay service
  constructor(private overlay: Overlay) { }

  open() {
    // Returns an OverlayRef (which is a PortalHost)
    const overlayRef = overlay.create();

    // Create ComponentPortal that can be attached to a PortalHost
    const filePreviewPortal = new ComponentPortal(FilePreviewOverlayComponent);

    // Attach ComponentPortal to PortalHost
    overlayRef.attach(filePreviewPortal);
  }
}

The first step is to create a PortalHost. We do that by calling create() on the Overlay service. This will return an OverlayRef instance which is basically a remote control for the overlay. One unique attribute of this OverlayRef is that it’s a PortalHost, and once created, we can attach or detach Portals. We can think of a PortalHost as a placeholder for a component or template. So in our scenario, we are creating a ComponentPortal that takes a component type as its fist argument. In order to actually display this component we need to attach the portal to the host.

Ok, but where does the overlay get rendered?

Good question. There’s an OverlayContainer service which creates a container div under the hood that gets appended to the body of the HTML Document. There are a few more wrapper elements created but our component eventually ends up in a div with a class of cdk-overlay-pane. Here’s what the DOM structure looks like:

<div class="cdk-overlay-container">
  <div id="cdk-overlay-0" class="cdk-overlay-pane" dir="ltr">
    <!-- Component goes here -->
  </div>
</div>

Done. That’s all we need to create our very first custom overlay using the CDK. Let’s try it out and see what we got so far:

Our service only exposes one public method open() that will take care of creating a custom overlay. For now, the service is quite simple but it gets more complicated as we implement a more sophisticated and complete (functional-wise) overlay. Therefore it’s a good idea to extract the common logic into a service to stay DRY. Imagine we would have the same logic defined in each component we want to show an overlay. No good, right?

Now that we have layed the foundation for our custom overlay, let’s take it one step further and improve on what we have so far. Let’s add a backdrop and specify a scroll and position strategy. Don’t worry if it’s unclear what scroll and position strategy is all about. We’ll cover that in a second.

Configuring the overlay

When creating an overlay, we can pass an optional configuration object to create() to set the desired options, e.g. whether it has backdrop, the position or scroll strategy, width, height and many more. Here’s an example:

// Example configuration
overlay.create({
  width: '400px',
  height: '600px'
});

First of all, we allow the consumer of our API to override certain options. Therefore, we update the signature for open() to also take a configuration object. In addition, we define an interface that describes the shape of the configuration from a consumer perspective:

// Each property can be overridden by the consumer
interface FilePreviewDialogConfig {
  panelClass?: string;
  hasBackdrop?: boolean;
  backdropClass?: string;
}

@Injectable()
export class FilePreviewOverlayService {
  open(config: FilePreviewDialogConfig = {}) {
    ...
  }
}

Next, we define some initial values for the config, so that, by default, every overlay has a backdrop alongside a backdropClass and panelClass:

const DEFAULT_CONFIG: FilePreviewDialogConfig = {
  hasBackdrop: true,
  backdropClass: 'dark-backdrop',
  panelClass: 'tm-file-preview-dialog-panel'
}

@Injectable()
export class FilePreviewOverlayService {
  ...
}

With that in place, we can define a new method getOverlayConfig() which takes care of creating a new OverlayConfig for the custom overlay. Remember, it’s better to break down the logic into smaller parts instead of implementing everything in one giant function. This ensures better maintainability but also readability of our code.

@Injectable()
export class FilePreviewOverlayService {

  ...

  private getOverlayConfig(config: FilePreviewDialogConfig): OverlayConfig {
    const positionStrategy = this.overlay.position()
      .global()
      .centerHorizontally()
      .centerVertically();

    const overlayConfig = new OverlayConfig({
      hasBackdrop: config.hasBackdrop,
      backdropClass: config.backdropClass,
      panelClass: config.panelClass,
      scrollStrategy: this.overlay.scrollStrategies.block(),
      positionStrategy
    });

    return overlayConfig;
  }
}

Our method is quite simple. It takes a FilePreviewDialogConfig and creates a new OverlayConfig with the values from the given configuration. However, there are two important things to mention. One is the scrollStrategy and the other one is the positionStrategy.

Scroll strategy

The scroll strategy is a way of defining how our overlay should behave if the user scrolls while the overlay is open. There are several strategies available as part of the CDK, such as

  • NoopScrollStrategy: does nothing
  • CloseScrollStrategy: automatically closes the overlay when scrolling
  • BlockScrollStrategy: blocks page scrolling
  • RepositionScrollStrategy: will reposition the overlay element on scroll

For our file preview overlay, we are going to use the BlockScrollStrategy because we don’t want the user to be scrolling in the background while the overlay is open.

The scrollStrategy takes a function that returns a scroll strategy. All strategies are provided by the Overlay service and can be accessed via the scrollStrategies property:

const overlayConfig = new OverlayConfig({
  ...
  // Other strategies are .noop(), .reposition(), or .close()
  scrollStrategy: this.overlay.scrollStrategies.block()
});

If we don’t specify a strategy explicitly, all overlays will use the NoopScrollStrategy.

Position strategy

The position strategy allows us to configure how our overlay is positioned on the screen. There are two position strategies available as part of the CDK:

  • GlobalPositionStrategy: used for overlays that need to be positioned unrelated to other elements on the screen. This strategy is mostly used for modals or root-level notifications.
  • ConnectedPositionStrategy: used for overlays that are positioned relative to other elements. This is commonly used for menus or tooltips.

We’ll be using the GlobalPositionStrategy for our overlay because it’s supposed to be positioned globally on screen, unrelated to other elements.

Similar to the scrollStrategy we can access all position strategies through the Overlay service like so:

const positionStrategy = this.overlay.position()
  .global()
  .centerHorizontally()
  .centerVertically();

const overlayConfig = new OverlayConfig({
  ...
  positionStrategy
});

With the configuration in place, we go ahead and define another method createOverlay() that hides the complexity of creating an overlay with a given configuration:

@Injectable()
export class FilePreviewOverlayService {
  ...
  private createOverlay(config: FilePreviewDialogConfig) {
    // Returns an OverlayConfig
    const overlayConfig = this.getOverlayConfig(config);

    // Returns an OverlayRef
    return this.overlay.create(overlayConfig);
  }
}

We now refactor our open() method to generate a default config and utilize createOverlay():

export class FilePreviewOverlayService {
  ...
  open(config: FilePreviewDialogConfig = {}) {
    // Override default configuration
    const dialogConfig = { ...DEFAULT_CONFIG, ...config };

    const overlayRef = this.createOverlay(dialogConfig);
    ...
  }
}

Here’s what it looks like in action:

Our overlay looks much more like an overlay as we have imagined it in the beginning. The good thing is that most of the heavy lifting is taken care of by the CDK, such as dynamically creating a component, block page scrolling, or positioning.

So far, so good, but we are still missing some very fundamental functionality. We can open an overlay but what about closing it? This it not yet possible, so let’s go ahead and add this feature.

Closing overlays with a remote control

Just like we use remote controls to snap between television channels, we want a remote control to close our overlays. It will provide an API for modifying, closing, and listening to events on the overlay instance. Especially if we want to be able to close the dialog from within the overlay component, and optionally return a value to the consumer.

Our remote control will be a simple class that exposes only one public method – close(). For now we keep simple and extend it as we introduce more features. Here’s what it looks like:

import { OverlayRef } from '@angular/cdk/overlay';

export class FilePreviewOverlayRef {

  constructor(private overlayRef: OverlayRef) { }

  close(): void {
    this.overlayRef.dispose();
  }
}

When implementing the remote control, the only thing we have to make sure is that we need access to the OverlayRef. It’s a reference to the overlay (portal host) that allows us to detach the portal. Note that, there’s no @Injectable decorator attached to the class which means that we can’t leverage the DI system for this service. This, however, is no big deal because we will manually create an instance for every overlay and therefore we don’t need to register a provider either. Theoretically, we could open multiple overlays stacked on top of each other where each overlay has its own remote control. The DI system creates singletons by default. That’s not what we want in this case.

What’s left to do is to update our open() method to create a remote control and return it to the consumer of our API:

@Injectable()
export class FilePreviewOverlayService {
  ...
  open(config: FilePreviewDialogConfig = {}) {
    ...
    const overlayRef = this.createOverlay(dialogConfig);

    // Instantiate remote control
    const dialogRef = new FilePreviewOverlayRef(overlayRef);
    ...
    // Return remote control
    return dialogRef;
  }

Notice how we pass in the overlayRef when creating a new FilePreviewOverlayRef? That’s how we get a hold of the PortalHost inside the remote. Instead of implementing a class that represents a reference to the open overlay, we could have returned the OverlayRef directly. However, it’s not a good idea to expose lower-level APIs because users could mess with the overlay and detach the backdrop for instance. Also, we need a little bit more logic later on when we introduce animations. A remote control is a good way of limiting the access to the underlying APIs and expose only those that we want to be publicly available.

From a consumer perspective we now get a handle to the overlay that allows us to programatically close it at some point. Let’s go ahead and update AppComponent accordingly:

@Component({...})
export class AppComponent  {
  ...
  showPreview() {
    // Returns a handle to the open overlay
    let dialogRef: FilePreviewOverlayRef = this.previewDialog.open();

    // Close overlay after 2 seconds
    setTimeout(() => {
      dialogRef.close();
    }, 2000);
  }
}

Here’s our code in action. Remember, once we open an overlay it will automatically close after 2 seconds:

Awesome! We are making serious progress and it’s not far until we reach the top of the mountain.

Improving ergonomics

In the previous sections we have mainly improved the overlay under the hood and layed a foundation for upcoming features. In this section we want to focus on improving the overlay’s ergonomics. This means that we want to be able to close the dialog when we click on the backdrop.

Turns out that the backdrop logic is extremely easy with the CDK. All we have to do is to subscribe to a stream that emits a value when the backdrop was clicked:

@Injectable()
export class FilePreviewOverlayService {
  open(config: FilePreviewDialogConfig = {}) {
    ...
    // Subscribe to a stream that emits when the backdrop was clicked
    overlayRef.backdropClick().subscribe(_ => dialogRef.close());

    return dialogRef;
  }
}

That’s it! Imagine how much work this would be without the CDK.

From here we could take it one step further and also close the overlay when a user naviagtes back in the browser history. For our application, however, this doesn’t make much sense because we are not using the router and there’s only one page that we render out to the screen. But feel free to give it a shot! Hint: use the Location service and subscribe to the browser’s popState events.

Sharing data with the overlay component

The goal of this post was to implement a generic file preview dialog rather than a static one. At the moment the overlay is quite static and there’s no way we can share data with the overlay component. Sharing data means we want to be able to provide an image that will be available within the component. After all it’s supposed to be a file preview. Therefore, we need to think about how we can share data with the component that is dynamically created.

Luckily, Angular has a hierarchical dependency injection system (DI for short) that we can leverage for our purpose. For more information on Angular’s DI system, check out this post.

In a nutshell, the DI system is flexible enough that we can reconfigure the injectors at any level of the component tree. That said, there is no such thing as the injector. An application may have multiple injectors and each component instance has its own injector. You hear the bells ring? Right, we can create our own custom injector and provide it with a list of custom injection tokens. It sounds more complicated than it actually is.

Turns out, the CDK already has a class PortalInjector that that we can use to provide custom injection tokens to components inside a portal. This is exactly what we need. Let’s break ground and implement a function createInjector() that creates a new PortalInjector and defines a list of custom injection tokens.

@Injectable()
export class FilePreviewOverlayService {
  ...
  private createInjector(config: FilePreviewDialogConfig, dialogRef: FilePreviewOverlayRef): PortalInjector {
    // Instantiate new WeakMap for our custom injection tokens
    const injectionTokens = new WeakMap();

    // Set custom injection tokens
    injectionTokens.set(FilePreviewOverlayRef, dialogRef);
    injectionTokens.set(FILE_PREVIEW_DIALOG_DATA, config.data);

    // Instantiate new PortalInjector
    return new PortalInjector(this.injector, injectionTokens);
  }

In the code above we create a new WeakMap, set our custom injection tokens that we want to be available (injectable) in the overlay component, and finally instantiate a new PortalInjector. The important part though is that we also specify a parent injector (first argument) which is mandatory. Also notice the second argument where we pass in our injection tokens.

There are two things that we are providing. The first token is the FilePreviewDialogRef. Having the remote control at hand, allows the overlay component to close itself. This is very useful because there will definitely be a close button somewhere. The second token is a custom InjectionToken that stores the data that we want to share with the component.

For the InjectionToken we create new file file-preview-overlay.tokens and instantiate a new InjectionToken:

import { InjectionToken } from '@angular/core';

import { Image } from './file-preview-overlay.service';

export const FILE_PREVIEW_DIALOG_DATA = new InjectionToken<Image>('FILE_PREVIEW_DIALOG_DATA');

Next, let’s update our FilePreviewDialogConfig so that the user can specify an image that will be used by the overlay component:

interface Image {
  name: string;
  url: string;
}

interface FilePreviewDialogConfig {
  panelClass?: string;
  hasBackdrop?: boolean;
  backdropClass?: string;
  data?: Image;
}

@Injectable()
export class FilePreviewOverlayService {
  ...
}

For better readability we’ll also refactor our open() method and create a new attachDialogContainer() function that now takes care of creating the injector and component portal, as well as attaching the portal to the host.

@Injectable()
export class FilePreviewOverlayService {
  ...
  private attachDialogContainer(overlayRef: OverlayRef, config: FilePreviewDialogConfig, dialogRef: FilePreviewOverlayRef) {
    const injector = this.createInjector(config, dialogRef);

    const containerPortal = new ComponentPortal(FilePreviewOverlayComponent, null, injector);
    const containerRef: ComponentRef<FilePreviewOverlayComponent> = overlayRef.attach(containerPortal);

    return containerRef.instance;
  }
}

With that in place, we can now update our FilePreviewOverlayComponent and inject the tokens that we have defined on a component level with the help of a custom injector.

export class FilePreviewOverlayComponent {
  constructor(
    public dialogRef: FilePreviewOverlayRef,
    @Inject(FILE_PREVIEW_DIALOG_DATA) public image: any
  ) { }
}

Finally with a little bit of styling we come much closer to what we were trying to achieve. We can now define data that will be passed to the overlay component and render an image onto the screen:

Where to go from here

This is it. Although creating custom overlays is something that’s more or less tricky to do, this task becomes rather easy with UI libraries like Angular Material that provide us with a common set of tools to build awesome and high-quality Angular components. More specifically, by extracting common behaviors and patterns into a so called Component Dev Kit, it becomes extremely easy to build a custom overlay.

Where to go from here? As mentioned in the beginning, this was only part one and we haven’t fully re-built the Google Drive-like file preview yet. In the next post we will build on top of this and implement keyboard support, image preloading and add animations in order to make our overlay more engaging.

Source:: Thoughtram