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
Vue 3

Vue 3 — Render Functions Events and Plugins

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions and create plugins with Vue 3.

Event Modifiers Equivalents

The event modifiers have the following equivalents.

They are:

  • .stop — event.stopPropagation()
  • .prevent — event.preventDefault()
  • .self — if (event.target !== event.currentTarget) return
  • .enter or .13 — if (event.keyCode !== 13) return
  • .ctrl, .alt, .shift, or .meta — if (!event.ctrlKey) return

For example, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-input></custom-input>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-input", {
        render() {
          return Vue.h("input", {
            onKeyUp: event => {
              if (event.target !== event.currentTarget) return;
              if (!event.shiftKey || event.keyCode !== 23) return;
              event.stopPropagation();
              event.preventDefault();
              //...
            }
          });
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We can call the plain JavaScript event methods and check their properties to add the modifier equivalents.

Slots

The this.$slots lets us add slots to our Vue apps.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-heading :level="1">foo</custom-heading>
    </div>
    <script>
      const app = Vue.createApp({});
      app.component("custom-heading", {
        render() {
          const { h } = Vue;
          return h(`h1`, {}, this.$slots.default());
        },
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

to add or custom h1 element with a slot inside.

this.$slots.default is the default slot.

JSX

We can also use JSX in our render functions if we install this Babel plugin.

This way, we can use the more convenient JSX syntax in our render functions.

For instance, we can write:

import AnchoredHeading from './AnchoredHeading.vue'

app.component("custom-heading", {
  render() {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
      );
    }
});

to write JSX instead of plain JavaScript in our render function.

Plugins

Plugins are a self-contained code that can be reused.

They add global-level functionality in Vue.

They can add anything, which includes global methods or properties.

It can also include global assets and mixins.

Instance methods can also be added with plugins.

We can add plugins by exporting an object with the install method:

export default {
  install: (app, options) => {
    //...
  }
}

We can then add our own global properties by writing:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }
  }
}

We can also inject other plugins within a plugin.

For instance, we can write:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }

    app.provide('i18n', options);
  }
}

We can add global mixins and directives right into our plugin code with the app.directive and app.mixin methods:

export default {
  install: (app, options) => {
    app.config.globalProperties.$foo = (key) => {
      return 'foo';
    }

    app.provide('i18n', options)

    app.directive('my-directive', {
      bind (el, binding, vnode, oldVnode) {
        // some logic ...
      }
      ...
    })

    app.mixin({
      created() {
        //...
      }
      //...
    })
  }
}

To use a plugin, we call the app.use method:

app.use(myPlugin, options)

Conclusion

There’re equivalents to directive event modifiers in render functions.

Render functions can also contain JSX if we add a plugin.

We can create plugins with various method calls.

Categories
Vue 3

Vue 3 — Teleport

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to use the teleport component to render elements and components in a different location in the DOM.

Teleport

We can use the teleport component to let us render parts of our Vue template in a location that’s different from its usual location in the DOM

This is handy for creating things like modals and overlays.

The DOM element that we want to render our items in must already exist.

Otherwise, we’ll get an error.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <p>footer</p>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

We added the teleport component to our template with the to prop set to the selector to mount the content inside it.

Therefore, the div with ID footer will hold the p element that’s inside the teleport component.

Using with Vue components

If teleport has a Vue component, then it’ll remain a child component of the teleport ‘s parent.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.component("parent-component", {
        template: `
          <h2>parent</h2>
          <teleport to="#footer">
            <child-component name="james" />
          </teleport>
        `
      });

      app.component("child-component", {
        props: ["name"],
        template: `
          <div>{{ name }}</div>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>

We put the parent-component in the root template of our app.

It has a teleport that has to set to #footer .

So it’ll be rendered in the div with the ID footer .

It has the child-component inside it.

And that’ll be rendered inside div with ID footer also.

Using Multiple Teleports on the Same Target

We can have multiple teleports with the same selector set as their to prop value.

They’ll be rendered by in the order that they’re defined in.

For instance, if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <div>foo</div>
      </teleport>
      <teleport to="#footer">
        <div>bar</div>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

Then the rendered result would be:

<div id="footer">
  <div>foo</div>
  <div>bar</div>
</div>

Conclusion

The teleport component lets us render parts of a Vue template in a location that’s different from the usual location in the DOM.

Categories
Vue 3

Vue 3 — Render Functions Basics

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions with Vue 3.

Render Functions

Templates should be used most of the time to build Vue components.

However, there may be cases where we need more flexibility.

For instance, we may want to add elements with dynamic tags.

Instead of writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        template: `
          <h1 v-if="level === 1">
            <slot></slot>
          </h1>
          <h2 v-else-if="level === 2">
            <slot></slot>
          </h2>
          <h3 v-else-if="level === 3">
            <slot></slot>
          </h3>
          <h4 v-else-if="level === 4">
            <slot></slot>
          </h4>
          <h5 v-else-if="level === 5">
            <slot></slot>
          </h5>
          <h6 v-else-if="level === 6">
            <slot></slot>
          </h6>
        `,
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We can make the heading tags dynamic with a render function.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        render() {
          const { h } = Vue;

          return h(`h${this.level}`, {}, this.$slots.default());
        },
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the variable-heading component with a render method to render our HTML.

The h method lets us render our items.

The first argument is the tag name.

The 2nd is any props or attributes we want to pass to it.

And the 3rd argument is an array fo children.

this.$slots.default() is the default slot to let us populate content,

The rest of the component are the same as the other parts.

Every element in the DOM is a node, and the h method lets us render the nodes by nesting them as we do in a tree.

The Virtual DOM Tree

Vue keeps a virtual DOM tree to keep track of changes is needs to make to the real DOM.

The h method returns an object that has the information needed for Vue to update the read DOM.

h() Arguments

The h method takes 3 arguments.

The tag name is the first argument. It can be a string, object, function, or null .

If it’s an object, then it must be a component or async component.

The 2nd argument is an object with optional props.

The 3rd argument is a string, array, or object of child nodes.

It’s also optional.

VNodes Must Be Unique

We can’t have duplicate VNodes in our render function.

So we can’t have:

render() {
  const myParagraphVNode = Vue.h('p', 'hi')
  return Vue.h('div', [
    paragraph, paragraph
  ])
}

We can’t have 2 paragraph s in the array.

Conclusion

We can use render functions to tell Vue how to render components with JavaScript.

This is an alternative to templates.