Categories
Nodejs

How to Dockerize a Node Web App

Running our Node web app in Docker saves us lots of headaches.

Every time a Docker image is deployed, it’s guaranteed to deploy a fresh container.

This way, we won’t have to worry about messing up our code in the container.

Also, the Docker image is built with an image, so we can build it repeatedly without doing anything manually.

In this article, we’ll look at how to Dockerize a simple Node web app.

Create the Node.js App

We start by creating our Node app.

To start, we create a project folder and run npm init --yes to create package.json .

Then we can replace everything in their with:

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "a simple app",
  "author": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.16.1"
  }
}

Then we create a server.js file in the same folder and add:

'use strict';

const express = require('express');

const PORT = 8080;
const HOST = '0.0.0.0';

const app = express();
app.get('/', (req, res) => {
  res.send('Hello World');
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

It just has one route that responds with ‘hello world’.

Creating a Dockerfile

Next, we create a Dockerfile in the project folder.

Then we add:

FROM node:12
WORKDIR /usr/src/app
COPY package*.json ./

RUN npm install
COPY . .

EXPOSE 8080
CMD [ "node", "server.js" ]

We get the Node 12 image, then create a working directory to build the image.

Then we copy package.json and package-lock.json to the root directory with COPY package*.json ./

Next, we run npm install to install the packages.

And then we bundle the app’s source code with COPY . . .

Then we use EXPOSE 8080 to open the 8080 port for the Docker image.

Finally, we run node server.js to start the app with CMD [ “node”, “server.js” ] .

Next, we create .dockerignore file to stop Docker from copying the local modules to the Docker container.

We do the same with the NPM logs.

So we have the following in .dockerignore :

node_modules
npm-debug.log

Building the Image

We can then build the image with:

docker build -t <your username>/my-app .

Where <your username> is the username of your account.

Then we should see our image when we run docker images .

Run the Image

Once it’s built, we can run our image with:

docker run -p 8888:8080 -d <your username>/my-app

-d runs the container in detached mode, which leaves it running in the background.

-p redirects a public port to a private port in the container.

We can then run docker ps to get the container ID.

The app’s output can be obtained with:

docker logs <container id>

And we can go into the container with:

docker exec -it <container id> /bin/bash

Then to test our app, we can run:

curl -i localhost:8888

Then we should get the ‘hello world’ response from the app.

Conclusion

We can create a Docker image for a Node web app with a simple Dockerfile.

Then we don’t have to do much to get it running with Docker.

Categories
Vuetify

Vuetify — Nested Dialogs

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Loader Dialog

We can create a loader dialog to show a progress bar inside it.

To do that, we write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <div class="text-center">
          <v-btn
            :disabled="dialog"
            :loading="dialog"
            class="white--text"
            color="purple darken-2"
            @click="dialog = true"
          >Start loading</v-btn>
          <v-dialog v-model="dialog" hide-overlay persistent width="300">
            <v-card color="primary" dark>
              <v-card-text>
                Please wait
                <v-progress-linear indeterminate color="white" class="mb-0"></v-progress-linear>
              </v-card-text>
            </v-card>
          </v-dialog>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
  watch: {
    dialog(val) {
      if (!val) {
        return;
      }
      setTimeout(() => {
        this.dialog = false;
      }, 5000);
    },
  },
};
</script>

We added a dialog that shows a progress bar with the v-progress-linear component,

The indeterminate prop makes the progress bar move until the dialog is dismissed.

Nested Dialogs

We can open dialog boxes within another dialog.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <div>
          <v-row justify="center">
            <v-btn color="primary" class="ma-2" dark @click="dialog = true">Open Dialog 1</v-btn>
            <v-btn color="primary" class="ma-2" dark @click="dialog2 = true">Open Dialog 2</v-btn>
            <v-btn color="primary" class="ma-2" dark @click="dialog3 = true">Open Dialog 3</v-btn>
            <v-menu bottom offset-y>
              <template v-slot:activator="{ on, attrs }">
                <v-btn class="ma-2" v-bind="attrs" v-on="on">A Menu</v-btn>
              </template>
            </v-menu>
            <v-dialog
              v-model="dialog"
              fullscreen
              hide-overlay
              transition="dialog-bottom-transition"
              scrollable
            >
              <v-card tile>
                <v-card-text>
                  <v-btn color="primary" dark class="ma-2" @click="dialog2 = !dialog2">Open Dialog 2</v-btn>
                  <v-list three-line subheader>
                    <v-subheader>User Controls</v-subheader>
                  </v-list>
                </v-card-text>

                <div style="flex: 1 1 auto;"></div>
              </v-card>
            </v-dialog>

            <v-dialog v-model="dialog2" max-width="500px">
              <v-card>
                <v-card-title>Dialog 2</v-card-title>
                <v-card-text>
                  <v-btn color="primary" dark @click="dialog3 = !dialog3">Open Dialog 3</v-btn>
                </v-card-text>
                <v-card-actions>
                  <v-btn color="primary" text @click="dialog2 = false">Close</v-btn>
                </v-card-actions>
              </v-card>
            </v-dialog>
            <v-dialog v-model="dialog3" max-width="500px">
              <v-card>
                <v-card-title>
                  <span>Dialog 3</span>
                  <v-spacer></v-spacer>
                  <v-menu bottom left>
                    <template v-slot:activator="{ on, attrs }">
                      <v-btn icon v-bind="attrs" v-on="on">
                        <v-icon>mdi-dots-vertical</v-icon>
                      </v-btn>
                    </template>
                  </v-menu>
                </v-card-title>
                <v-card-actions>
                  <v-btn color="primary" text @click="dialog3 = false">Close</v-btn>
                </v-card-actions>
              </v-card>
            </v-dialog>
          </v-row>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
    dialog2: false,
    dialog3: false,
    notifications: false,
    sound: true,
    widgets: false,
  }),
};
</script>

to show dialog boxes with buttons to open other dialog boxes.

They are all created with the v-dialog component.

Conclusion

We can add a dialog box to show a progress bar or other dialog boxes with Vuetify.

Categories
Vuetify

Vuetify — Dialog

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Modal

We can create a modal dialog with the persistent prop.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-row justify="center">
          <v-dialog v-model="dialog" persistent max-width="290">
            <template v-slot:activator="{ on, attrs }">
              <v-btn color="primary" dark v-bind="attrs" v-on="on">Open Dialog</v-btn>
            </template>
            <v-card>
              <v-card-title class="headline">Title</v-card-title>
              <v-card-text>Lorem ipsum.</v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="green darken-1" text @click="dialog = false">Cancel</v-btn>
                <v-btn color="green darken-1" text @click="dialog = false">OK</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-row>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
};
</script>

We have the persistent prop on the v-dialog to make it closeable only with the buttons inside.

Scrollable

To make a dialog scrollable, we just add a scrollable prop.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-row justify="center">
          <v-dialog v-model="dialog" scrollable max-width="290">
            <template v-slot:activator="{ on, attrs }">
              <v-btn color="primary" dark v-bind="attrs" v-on="on">Open Dialog</v-btn>
            </template>
            <v-card>
              <v-card-title>Select Country</v-card-title>
              <v-divider></v-divider>
              <v-card-text style="height: 300px;">
                <v-radio-group v-model="country" column>
                  <v-radio :label="c" :value="c" v-for="(c, i) of countries" :key="i"></v-radio>
                </v-radio-group>
              </v-card-text>
              <v-spacer></v-spacer>
              <v-card-actions>
                <v-btn color="green darken-1" text @click="dialog = false">Cancel</v-btn>
                <v-btn color="green darken-1" text @click="dialog = false">OK</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-row>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
    country: "",
    countries: [
      "Afghanistan",
      "Albania",
      "Algeria",
      "American Samoa",
      "Andorra",
      "Angola",
      "Anguilla",
      "Antarctica",
      "Antigua and Barbuda",
      "Argentina",
      "Armenia",
      "Aruba",
      "Australia",
      "Austria",
      "Azerbaijan",
    ],
  }),
};
</script>

We display a radio button group that overflows the v-card-text container.

So we add the scrollable prop to the v-dialog to make the radio button group scrollable.

Overflowed

If the modal doesn’t fit the available window space, then the container will be scrolled.

Form

We can add a form inside the v-dialog component.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-row justify="center">
          <v-dialog v-model="dialog" persistent max-width="600px">
            <template v-slot:activator="{ on, attrs }">
              <v-btn color="primary" dark v-bind="attrs" v-on="on">Open Dialog</v-btn>
            </template>
            <v-card>
              <v-card-title>
                <span class="headline">User Profile</span>
              </v-card-title>
              <v-card-text>
                <v-container>
                  <v-row>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field label="first name*" required></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field label="middle name" hint="middle name"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field label="last name*" hint="last name" persistent-hint required></v-text-field>
                    </v-col>
                    <v-col cols="12">
                      <v-text-field label="Email*" required></v-text-field>
                    </v-col>
                    <v-col cols="12">
                      <v-text-field label="Password*" type="password" required></v-text-field>
                    </v-col>
                    <v-col cols="12">
                      <v-select :items="['0-17', '18-29', '30-54', '54+']" label="Age*" required></v-select>
                    </v-col>
                  </v-row>
                </v-container>
                <small>*required field</small>
              </v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="blue darken-1" text @click="dialog = false">Close</v-btn>
                <v-btn color="blue darken-1" text @click="dialog = false">Save</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-row>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
};
</script>

to add a form inside the v-dialog .

This way, we’ll see the form when we open the dialog.

Conclusion

We can create a dialog with various kinds of content and behavior with Vuetify.

Categories
Vuetify

Vuetify — Expandable Chips and Dialogs

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Expandable Chips

A chip can be combined with the v-menu to enable a set of actions for a chip.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card max-width="400" class="mx-auto">
          <v-row class="px-6 py-3" align="center">
            <span class="mr-4">To</span>
            <v-menu v-model="menu" bottom right transition="scale-transition" origin="top left">
              <template v-slot:activator="{ on }">
                <v-chip pill v-on="on">
                  <v-avatar left>
                    <v-img src="https://cdn.vuetifyjs.com/images/john.png"></v-img>
                  </v-avatar>John Leider
                </v-chip>
              </template>
              <v-card width="300">
                <v-list dark>
                  <v-list-item>
                    <v-list-item-avatar>
                      <v-img src="https://cdn.vuetifyjs.com/images/john.png"></v-img>
                    </v-list-item-avatar>
                    <v-list-item-content>
                      <v-list-item-title>John Smith</v-list-item-title>
                      <v-list-item-subtitle>john@smith.com</v-list-item-subtitle>
                    </v-list-item-content>
                    <v-list-item-action>
                      <v-btn icon @click="menu = false">
                        <v-icon>mdi-close-circle</v-icon>
                      </v-btn>
                    </v-list-item-action>
                  </v-list-item>
                </v-list>
                <v-list>
                  <v-list-item @click="() => {}">
                    <v-list-item-action>
                      <v-icon>mdi-briefcase</v-icon>
                    </v-list-item-action>
                    <v-list-item-subtitle>john@gmail.com</v-list-item-subtitle>
                  </v-list-item>
                </v-list>
              </v-card>
            </v-menu>
          </v-row>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    menu: false,
  }),
};
</script>

We have the v-menu that wraps outside the v-chip .

The v-chip has some text and an avatar.

Also, the v-list-item has the list that we show when we click on the chip.

The v-on="on" directive lets us toggle the menu since on includes the click listener.

Also, we have a close circle button to set menu to false when we click it.

This way, the menu will close.

Dialogs

The v-dialog lets us display a dialog.

To use it, we write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-row justify="center">
          <v-btn color="primary" dark @click.stop="dialog = true">Open Dialog</v-btn>

          <v-dialog v-model="dialog" max-width="290">
            <v-card>
              <v-card-title class="headline">Title</v-card-title>
              <v-card-text>Lorem ipsum.</v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="green darken-1" text @click="dialog = false">Cancel</v-btn>
                <v-btn color="green darken-1" text @click="dialog = false">OK</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-row>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
};
</script>

to create a dialog with the v-dialog component.

v-model binds to the dialog state to control the opening and closing of the dialog.

We also have the v-btn to set dialog to false when we click on the buttons.

The Open Dialog button sets dialog to true when we click it so the dialog opens when we click it.

Conclusion

We can make a chip show a menu when we click it.

Also, we can create a dialog box with the v-dialog component.

Categories
Vuetify

Vuetify — Chips

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Chips

We can use the v-chip component to add a chip.

It conveys a small piece of information.

The close prop lets the chip become interactive.

To add one, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="primary">Primary</v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add the v-chip component with the color prop to change the color.

Icon

We can add an icon inside the chip.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="indigo" text-color="white">
          <v-avatar left>
            <v-icon>mdi-account-circle</v-icon>
          </v-avatar>James
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add a v-avatar component with a v-icon inside.

Then we’ll see the icon to the left of the text.

Outlined

The outlined prop will make the chip display with an outline.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="success" outlined>
          <v-icon left>mdi-server-plus</v-icon>Status
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Now we’ll see a white background with a border on the chip.

Label

We can make the chip border-radius less round to make a label.

The label prop will adjust the border-radius to be less round:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="pink" label text-color="white">
          <v-icon left>mdi-label</v-icon>Tags
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Sizes

We can have various sizes with the v-chip component.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" x-small>x-small</v-chip>
        <v-chip class="ma-2" small>small</v-chip>
        <v-chip class="ma-2">Default</v-chip>
        <v-chip class="ma-2" large>large</v-chip>
        <v-chip class="ma-2" x-large>x-large</v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the x-small , small , large and x-large props to change the chip size.

Filter

The filter prop lets us show an additional icon if it’s active.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" :input-value="active" filter filter-icon="mdi-minus">chip</v-chip>
        <v-switch v-model="active" label="Active"></v-switch>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    active: false,
  }),
};
</script>

We have the v-chip component with the input-value prop.

When it’s true , then we show an extra icon with the filter prop.

filter-icon lets us set the icon to show with active is true .

Conclusion

We can add chips to show small amounts of information.

The icon can be shown and toggled.