Categories
BootstrapVue

BootstrapVue — Accordions and Dropdowns

To make good looking Vue apps, we need to style our components.

To make our lives easier, we can use components with styles built-in.

In this article, we’ll look at how to add an accordion and a dropdown with BootstrapVue.

Accordions

We can create an accordion with the b-collapse component.

To create one, we’ve to nest it inside a b-card component.

For instance, we can write:

<template>
  <div id="app">
    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-1 variant="info">accordion 1</b-button>
      </b-card-header>
      <b-collapse id="accordion-1" visible accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>accordion 1 text</b-card-text>
        </b-card-body>
      </b-collapse>
    </b-card>

    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-2 variant="info">accordion 2</b-button>
      </b-card-header>
      <b-collapse id="accordion-2" accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>accordion 2 text</b-card-text>
        </b-card-body>
      </b-collapse>
    </b-card>

    <b-card no-body class="mb-1">
      <b-card-header header-tag="header" class="p-1" role="tab">
        <b-button block href="#" v-b-toggle.accordion-3 variant="info">accordion 3</b-button>
      </b-card-header>
      <b-collapse id="accordion-3" accordion="my-accordion" role="tabpanel">
        <b-card-body>
          <b-card-text>accordion 3 text</b-card-text>
        </b-card-body>
      </b-collapse>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have 3 cards which are combined to create our accordion.

We have the b-card components on the outside.

Then we have the b-collapse component on the inside to create the accordion.

This way, we have the headers always displayed.

But the body is only displayed when we clicked on the heading, which also hides the other cards.

Dropdowns

Dropdowns are toggleable and contextual overlays for displaying list of links and actions.

To create one, we create the b-dropdown component with some b-dropdown-item components inside.

For instance, we can create a simple dropdown by writing:

<template>
  <div id="app">
    <b-dropdown id="dropdown-1" text="menu" class="m-md-2">
      <b-dropdown-item>apple</b-dropdown-item>
      <b-dropdown-divider></b-dropdown-divider>
      <b-dropdown-item active>active</b-dropdown-item>
      <b-dropdown-item disabled>disabled</b-dropdown-item>
    </b-dropdown>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the b-dropdown component with the text prop, which is set to the text of the drop-down button.

Therefore, we set our dropdown button text to 'menu' since that’s the value we have for text .

Inside it, we have the b-dropdown-item with various props.

The content is between the tags.

active makes a dropdown item active.

disabled makes a dropdown item disabled.

b-dropdown-divider is a divider on our dropdown menu.

Dropdown Button Content

We can customize our content of a drop with a template element to fill the button-content slot.

For instance, we can write:

<template>
  <div id="app">
    <b-dropdown>
      <template v-slot:button-content>
        <b>menu</b>
      </template>
      <b-dropdown-item>apple</b-dropdown-item>
    </b-dropdown>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the template element which fills the button-content as indicated by v-slot:button-content .

Inside it, we have <b>menu</b> to show bold text.

Below it, we have the usual dropdown items.

Menu Alignment

The menu alignment can change according to our preference.

The default is to left-align the menu.

If we want to right-align the menu, we can add the right prop to the b-dropdown component.

For instance, we can write:

<template>
  <div id="app" style="padding: 100px">
    <b-dropdown right text="right">
      <b-dropdown-item>apple</b-dropdown-item>
    </b-dropdown>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

to right-align the menu if there’s enough room on the left edge of the screen.

Otherwise, it’ll go back to left aligning the menu.

Dropup

We can also make the menu stay above the button instead of below it.

We just have to add the dropup prop.

For example, we write:

<template>
  <div id="app" style="padding-top: 100px">
    <b-dropdown dropup text="right">
      <b-dropdown-item>apple</b-dropdown-item>
    </b-dropdown>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

to do that.

Drop Right or Left

We can add similar props to make the menu show on the right or left instead of above or below the button.

dropright makes it show to the right of the button.

dropleft makes it show on the left of the button.

Conclusion

We can create accordion with cards and the collapse components.

Also, BootstrapVue has menu components we can use.

Categories
BootstrapVue

BootstrapVue — Carousel and Collapse

To make good looking Vue apps, we need to style our components.

To make our lives easier, we can use components with styles built-in.

In this article, we’ll look at how to add a carousel and collapse components into a Vue app.

Carousel

A carousel is a slide show for cycling through a series of content.

It can include image, text, or custom markup.

BootstrapVue’s carousel has support for previous/next controls and indicators.

We can add one by using the b-carousel component.

To use it, we can write:

<template>
  <div id="app">
    <b-carousel
      v-model="slide"
      :interval="5000"
      controls
      indicators
      background="#ababab"
      img-width="1024"
      img-height="480"
      @sliding-start="onSlideStart"
      @sliding-end="onSlideEnd"
    >
      <b-carousel-slide caption="cat" text="cat" img-src="https://placekitten.com/g/600/200"></b-carousel-slide>

<b-carousel-slide img-src="https://picsum.photos/1024/480/?image=54">
        <h1>hello</h1>
      </b-carousel-slide>
    </b-carousel>

<p class="mt-4">
      Slide #: {{ slide }}
      <br>
      Sliding: {{ sliding }}
    </p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      slide: 0,
      sliding: null
    };
  },
  methods: {
    onSlideStart(slide) {
      this.sliding = true;
    },
    onSlideEnd(slide) {
      this.sliding = false;
    }
  }
};
</script>

We have a carousel in the component above.

interval indicates that we cycle through the code every 5 seconds.

slide is the index of the slide we’re on.

controls indicates that we show the controls

indicators indicates that we show the dashes to show which slide we’re on.

background is the background color if it’s shown.

img-width is the slide image width in pixels.

img-height is the slide image height in pixels.

sliding-start is the handler that runs when the slide starts sliding.

sliding-end is the handler that runs when the slide finishes sliding.

The handlers have the slide parameter to get the slide index.

v-model is set to slide to set the slide state with the value being the slide index.

Crossfade Animation

We can add crossfade animation with the fade prop on the b-carousel component.

Disabling Animation

To disable animation, we can use the no-animation prop on the b-carousel component.

Slide Wrapping

The no-wrap prop added to b-carousel will disable the carousel wrapping back to the first slide.

Collapse

The collapse component is a box with content that can be toggled on and off.

To use it we use the v-b-toggle directive with the b-collapse component.

For instance, we can write:

<template>
  <div id="app">
    <b-button v-b-toggle.collapse-1 variant="primary">toggle</b-button>
    <b-collapse id="collapse-1" class="mt-2">
      <b-card>
        <p class="card-text">collapse content</p>
      </b-card>
    </b-collapse>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have a b-button that has the v-b-toggle directive and with the collapse-1 modifier to indicate that it’s toggling the b-collapse component with the ID collapse-1 on and off.

Then we can see the b-collapse component being toggled.

We can nest collapse component inside another component by adding b-collapse and a component with the v-b-toggle .

For instance, we can write:

<template>
  <div id="app">
    <b-button v-b-toggle.collapse-1 variant="primary">toggle</b-button>
    <b-collapse id="collapse-1" class="mt-2">
      <b-card>
        <p class="card-text">collapse content</p>
        <b-button v-b-toggle.collapse-1-inner size="sm">toggle inner</b-button>
        <b-collapse id="collapse-1-inner" class="mt-2">
          <b-card>inner content</b-card>
        </b-collapse>
      </b-card>
    </b-collapse>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have:

<b-button v-b-toggle.collapse-1-inner size="sm">toggle inner</b-button>
<b-collapse id="collapse-1-inner" class="mt-2">
  <b-card>inner content</b-card>
</b-collapse>

which is the inner collapse component.

We can toggle it on and off the same way.

We just have to make sure that we don’t use the same IDs as the outer one.

v-model

We can get the collapsed state of the collapse component by binding a state with v-model .

For instance, we can write:

<template>
  <div id="app">
    <b-button v-b-toggle.collapse-1 variant="primary">toggle</b-button>
    <b-collapse id="collapse-1" class="mt-2" v-model="visible">
      <b-card>
        <p class="card-text">collapse content</p>
      </b-card>
    </b-collapse>
    <p>{{visible}}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      visible: false
    };
  }
};
</script>

We added v-model to bind to the visible state so we can set the visibility state as the value of that property.

Then visible will show true if the collapse is expanded and false otherwise.

Conclusion

We can create a carousel with BoostrapVue. The slide options and controls can be changed.

Collapse is a component that we can toggle on and off.

Categories
BootstrapVue

BootstrapVue — Cards

To make good looking Vue apps, we need to style our components.

To make our lives easier, we can use components with styles built-in.

In this article, we’ll look at how to add cards to our Vue app.

Cards

In Bootstrap, cards are a flexible and extensible content container.

It can gold header, footer, and content.

The background colors can also be changed.

To add a card, we can use the b-card component.

For instance, we can write:

<template>
  <div id="app">
    <b-card
      title="Card Title"
      img-src="https://placekitten.com/g/600/200"
      img-alt="cat"
      img-top
      tag="article"
      style="max-width: 20rem;"
      class="mb-2"
    >
      <b-card-text>a cat</b-card-text>

<b-button href="#" variant="primary">Go</b-button>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the b-card component with a few props.

The img-src is the URL for an image.

img-alt is the alt attribute for the image.

style has the styles for the card.

class has the class for the card.

b-card-text has the content for the card.

title has the title for the card.

img-top indicates that the card image is at the top of the card.

We also have a button with the link.

We can also place thre image at the bottom with image-bottom .

To add a reader, we can use the header prop.

Likewise, we can add the footer prop for the footer.

We can add a header and footer by writing the following:

<template>
  <div id="app">
    <b-card
      header="header"
      header-tag="header"
      footer="footer"
      footer-tag="footer"
      title="title"
    >
      <b-card-text>Header and footers</b-card-text>
      <b-button href="#" variant="primary">Go</b-button>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We just add the props the place we expect them to be added in the b-card component to add headers and footers.

Horizontal Layout

We don’t have to stick with a vertical layout.

We can also create a card with a horizontal layout with b-row and b-col for layout.

For instance, we can write:

<template>
  <div id="app">
    <b-card no-body class="overflow-hidden">
      <b-row no-gutters>
        <b-col md="6">
          <b-card-img src="https://placekitten.com/200/50" alt="cat"></b-card-img>
        </b-col>
        <b-col md="6">
          <b-card-body title="Horizontal Card">
            <b-card-text>card</b-card-text>
          </b-card-body>
        </b-col>
      </b-row>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have b-col to divide up the card into 2 halves.

md with value 6 means that we have a left column and a right column.

no-body means that we don’t let BootstrapVue add the card body automatically.

Instead, we used b-card-body to place the body of the card.

b-row makes sure that the b-col components stay in the row.

Text Variants

We can change the text string with the bg-variant prop.

For instance, we can write:

<template>
  <div id="app">
    <b-card bg-variant="dark" text-variant="white" title="title">
      <b-card-text>more content</b-card-text>
      <b-button href="#" variant="primary">Go</b-button>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We set bg-variant to 'dark' to make the background dark gray.

title is still the card title.

text-variant is the styling for the card text, which we change to white.

Other variants include primary, secondary, success, info, warning, danger, light, and dark.

Bordered

We can add borders to our cards.

To do that, we use the border-variant prop.

For instance, we can write:

<template>
  <div id="app">
    <b-card border-variant="warning" header="header" header-border-variant="warning" align="center">
      <b-card-text>lorem ipsum.</b-card-text>
    </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the header prop to set the header text.

header-border-variant to set the border style of the header.

border-variant is used to set the border style of the body.

Header and Footer variants

Headers and footers have their own variants.

We can set them with the header-bg-variant and footer-bg-variant respectively.

Those will set the background color.

There’s also the header-border-variant and footer-border-variant props to set the border styles of the header and footer.

Nav Integration

We can add navbar integration into a card by using the b-nav component.

To add a navbar to a card, we can write:

<template>
  <div id="app">
  <b-card title="Ctitle" body-class="text-center" header-tag="nav">
    <template v-slot:header>
      <b-nav card-header tabs>
        <b-nav-item active>active</b-nav-item>
        <b-nav-item>inactive</b-nav-item>
        <b-nav-item disabled>disabled</b-nav-item>
      </b-nav>
    </template>

    <b-card-text>
      lorem ipsum.
    </b-card-text>

<b-button variant="primary">go</b-button>
  </b-card>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We just put the b-nav component in the template tag to add the navbar.

It’s put into the header slot as indicated by the v-slot:header directive.

We have the active prop to set a link to be active.

If there’s no active prop set then it’s inactive.

If there’s a disabled prop, then it’s disabled.

Conclusion

There are many things that we can do with cards.

We can style it, change the layout, and add a navbar to it.

Categories
Node.js Tips

Node.js Tips — Overwrite Files, POST Request, and Run Async Code in Series

As with any kind of app, there are difficult issues to solve when we write Node apps. In this article, we’ll look at some solutions to common problems when writing Node apps.

Overwrite a File Using fs in Node.js

fs.writeFileSync and fs.writeFile both overwrite the file by default.

Therefore, we don’t have to add any extra checks.

Also, we can set the 'w' flag to make sure we write to the file:

fs.writeFileSync(path, content, {
  encoding: 'utf8',
  flag: 'w'
})

We set the option in the 3rd argument.

Defining an Array as an Environment Variable in Node.js

We can set environment variables as a comma-separated string as its value.

Then we can get the string and call split to split the environment variable string with a comma.

For example, we can write:

app.js

const names = process.env.NAMES.split(',');

Then when we run:

NAMES=bar,baz,foo node app.js

Then process.env.NAMES will be 'bar,baz,foo' .

And then we can call split as we did above to convert it to an array.

Make POST Request Using Node.js

We can make a POST request using the http module.

For instance, we can write:

const http = require('http')

const body = JSON.stringify({
  foo: "bar"
})

const request = new http.ClientRequest({
  hostname: "SERVER_NAME",
  port: 80,
  path: "/some-path",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Content-Length": Buffer.byteLength(body)
  }
})

request.end(body);

We use the http.ClientRequest constructor to create our request.

We specify the hostname which is the hostname.

port is the port.

path is the path relative to the hostname.

method is the request method, which should be 'POST' to make a POST request.

headers have the request headers.

Then we call request.end to make a request with the body , which is the request body.

Then to listen to the request-response, we listen to the response event.

For instance, we can write:

request.on('response', (response) => {
  console.log(response.statusCode);
  console.log(response.headers);
  response.setEncoding('utf8');
  response.on('data', (chunk) => {
    console.log(chunk);
  });
});

We listen to the response event on the request object.

The callback has the response object which is the read stream with the response .

It also has the statusCode to get the status code.

headers have the response headers.

We listen to the data event on the response to get the response in the chunk parameter.

Together, we have:

const http = require('http')

const body = JSON.stringify({
  foo: "bar"
})

const request = new http.ClientRequest({
  hostname: "SERVER_NAME",
  port: 80,
  path: "/some-path",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Content-Length": Buffer.byteLength(body)
  }
})

request.end(body);

request.on('response', (response) => {
  console.log(response.statusCode);
  console.log(response.headers);
  response.setEncoding('utf8');
  response.on('data', (chunk) => {
    console.log(chunk);
  });
});

Running Async Code in Series

We can use the async module’s series method to run multiple pieces of async code in series.

For instance, we can write:

const async = require('async');

const foo = (callback) => {
  setTimeout(() => {
    callback(null, 'foo');
  }, 5000);
}

const bar = (callback) => {
  setTimeout(() => {
    callback(null, 'bar');
  }, 2000);
}

async.series([
  foo,
  bar
], (err, results) => {
  console.log(results);
});

We have 2 functions foo and bar which runs setTimeout and takes a Node-style callback.

The callback parameter in each function takes an error object and the result.

Then we can pass the functions to the async.series method after putting them in an array.

Since the signature of the functions match with async.series is looking for, we’ll get the results of each function in the results parameter, which is an array.

It has all the results of each function that we passed as the 2nd argument of callback .

This means results is ['foo', 'bar'] .

Set Navigation Timeout with Node Puppeteer

We can set the navigation timeout with the setDefaultNavigationTimeout method.

For instance, we can write:

await page.setDefaultNavigationTimeout(0);

to set the default timeout in milliseconds.

The timeout will affect goBack , goForward , goto , reload , setContent , and waitForNavigation .

Conclusion

We can set the navigation timeout with Puppeteer. To make a POST request, we can use the http.ClientRequest constructor. Also, we can use the async.series method to run functions that run asynchronously in the Node format sequentially. Environment variables are always strings. writeFile and writeFileSync always overwrite files.

Categories
Node.js Tips

Node.js Tips — Download Files, Async Test, Socket.io Custom

As with any kind of app, there are difficult issues to solve when we write Node apps. In this article, we’ll look at some solutions to common problems that we might encounter when writing Node apps.

Log Inside page.evaluate with Puppeteer

We can log output when page.evaluate is run by listening to the console event.

For instance, we can write:

const page = await browser.newPage();
page.on('console', consoleObj => console.log(consoleObj.text()));

We call browser.newPage to create a new page object.

Then we listen to the console event with it.

It takes a callback, which we can convert to text with the text method.

Testing Asynchronous Function with Mocha

We can run async functions with Mocha tests to test them.

For instance, we can write:

it('should do something', async function() {
  this.timeout(40000);
  const result = await someFunction();
  assert.isBelow(result, 3);
});

We call this.timeout to set the timeout before the test times out.

Then we use await to run our async function, which returns a promise.

Finally, we get the result and use assert to check it.

Acknowledgment for socket.io Custom Event

We can listen to events after a connection is established.

On the server-side, we can listen to the connection event to see if a connection is established.

Once it is, then we emit an event to acknowledge the connection is made.

Likewise, we can do the same on the client-side.

For instance, in our server-side code, we write:

io.sockets.on('connection', (sock) => {
   sock.emit('connected', {
     connected: 'yes'
   });

   sock.on('message', (data, callback) => {
     console.log('received', data);
     const responseData = {
       hello: 'world'
     };

     callback(responseData);
   });
 });

We listen to the connection event to listen to connections.

Then we emit the connected event to acknowledge the connection.

We also listen to the message event which takes data and a callback, which we call to send data back to the client.

The on the client-side, we write:

const socket = io.connect('http://localhost:3000');
socket.on('error', (err) => {
  console.error(err);
});

socket.on('connected', (data) => {
  console.log('connected', data);
  socket.emit('message', {
    payload: 'hello'
  }, (responseData) => {
    console.log(responseData);
  });
});

We connect to the server with io.connect .

And we listen to the error even which runs a callback if there’s an error.

We also listen to the connected event for any data that’s sent.

And we emit a message event with some data and a callback which the server calls to get the data we passed into the callback function in the server-side code.

responseData is the responseData passed into callback in the server-side code.

Download Large File with Node.js while Avoiding High Memory Consumption

We can make an HTTP request with the http module.

Then we listen to the response event, which has the downloaded file’s content.

We create a write stream with the file path.

Then we listen to the data event emitted on the response object to get the chunks of data.

We also listen to the end event emitted from response so that we can stop writing to the stream.

For example, we can write:

const http = require('http');
const fs = require('fs');

const download = (url, dest, cb) => {
  const file = fs.createWriteStream(dest);
  const request = http.get(url, (response) => {
    response.pipe(file);
    file.on('finish', () => {
      file.close(cb);
    });
  }).on('error', (err) => {
    fs.unlink(dest);
    if (cb) cb(err.message);
  });
};

We created a write stream with fs.createWriteStream .

Then we make our request with the http.get method.

We call response.pipe to pipe the response to our write stream, which writes it to the file.

We listen to the finish event and close the write stream in the callback.

If there’s an error, we delete what’s written so far with unlink .

We listen to the error event to watch for errors.

cb is a callback that we call to deliver the results.

Since the data is obtained in chunks, memory usage shouldn’t be an issue.

Conclusion

We can download a file and write it to disk with a write stream. We can log the output with Puppeteer. Mocha can run async functions in tests. We can send events to acknowledge the emission of custom events.