Categories
BootstrapVue

BootstrapVue — Popovers

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.

We look at how to customize popovers.

Variants and Custom Class

We can change the variants and custom classes to customize our popups.

To change to a styling variant, we can use the variant prop.

For example, we can write:

<template>
  <div id="app" class="text-center">
    <b-button id="popover-button" href="#">Button</b-button>
    <b-popover target="popover-button" variant="danger" triggers="focus">
      <template v-slot:title>Danger!</template>danger
    </b-popover>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}
</style>

We changed the variant to danger , so we’ll see that the title and content are red.

We can add custom classes with the custom-class prop on b-popover .

For example, we can write:

<template>
  <div id="app" class="text-center">
    <b-button id="popover-button" href="#">Button</b-button>
    <b-popover target="popover-button" custom-class="popover-class" triggers="focus">
      <template v-slot:title>Danger!</template>danger
    </b-popover>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}

.popover-class {
  background: orange
}
</style>

We added the popover-class class and set it as the value of custom-class .

Therefore, the background is now orange.

Show and Hide Popovers Programmatically

We can show and hide popovers programmatically with the show prop.

It takes an expression to indicate whether we want to show the popover or not.

For instance, we can write:

<template>
  <div id="app" class="text-center">
    <b-button id="popover-button" href="#" @click="show = !show">toggle</b-button>
    <b-popover :show="show" target="popover-button">
      <template v-slot:title>title</template>content
    </b-popover>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false
    };
  }
};
</script>

<style>
#app {
  margin: 200px;
}
</style>

The toggle button toggles the popover on and off.

We have the click handler on b-button to do the toggling by toggling show between true and false .

The show prop is set to show so that when the show state changed, the popover will be toggled on or off.

We can also assign a ref to the b-popover component and emit the open and close events to open and close the popover respectively.

For example, we can write:

<template>
  <div id="app" class="text-center">
    <b-button @click="onOpen">Open</b-button>
    <b-button @click="onClose">Close</b-button>
    <b-button id="popover-button" variant="primary">button with popover</b-button>
    <b-popover ref="popover" target="popover-button" title="title">content</b-popover>
  </div>
</template>

<script>
export default {
  methods: {
    onOpen() {
      this.$refs.popover.$emit("open");
    },
    onClose() {
      this.$refs.popover.$emit("close");
    }
  }
};
</script>

<style>
#app {
  margin: 200px;
}
</style>

We have the Open and Close buttons, which references the ref of the popover to emit the open and close events respectively.

That will open and close the popover.

We also have the ‘button with popover’ button that’s associate with the popover.

Now when we click Open, the popover will open.

When we click Close, the popover will close.

The popover is displayed beside the ‘button with popover’ button since the id and target match.

Also, we can make the popover show on the initial render.

We can just add the show prop to do that:

<template>
  <div id="app">
    <b-button id="popover-button" variant="primary">Button</b-button>

    <b-popover show target="popover-button" title="title">content</b-popover>
  </div>
</template>

<script>
export default {};
</script>

We have the b-popover with the show prop to show the popover when the page loads.

Programmatically Disabling Popover

We can use the disabled prop to disable a popover.

For example, we can write:

<template>
  <div id="app">
    <b-button id="popover-button" variant="primary">Button</b-button>

    <b-popover disabled target="popover-button" title="title">content</b-popover>
  </div>
</template>

<script>
export default {};
</script>

Then we won’t see the popover no matter what we do.

v-b-popover Directive

We can add modifiers to the v-b-popover directive to change the placement.

For instance, we can write:

<template>
  <div id="app" class="text-center">
    <b-button v-b-popover.hover.top="'Popover'" title="Title">Button</b-button>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}
</style>

to place the popover on top of the button with the top modifier.

And the hover modifier lets us change the button to show the popover on hover.

Other possible modifiers are right , left , or bottom to change the placement according to those names.

Conclusion

We can create popovers by in the way we want.

The placement can be changed. Showing and hiding of popovers can be done programmatically.

Categories
BootstrapVue

BootstrapVue — Table Customizations

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.

We look at how to customize table contents.

Colgroups

We can group table columns into colgroups.

For example, we can write:

<template>
  <div id="app">
    <b-table :items="items">
      <template v-slot:table-colgroup="scope">
        <col
          v-for="field in scope.fields"
          :key="field.key"
          :style="{ width: field.key === 'firstName' ? '120px' : '180px' }"
        >
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { firstName: "alex", lastName: "green" },
        {
          firstName: "may",
          lastName: "smith"
        },
        { firstName: "james", lastName: "jones" }
      ]
    };
  }
};
</script>

We have the col component that populates the table-colgroup slot to loop through the fields.

This way, we can style each column with the style prop.

Busy State

b-table lets us pass in the busy prop to flag the table if it’s busy.

This way, we can display something if the table is busy.

For example, we can write:

<template>
  <div id="app">
    <b-table :items="items" busy>
      <template v-slot:table-busy>
        <div>
          <b-spinner></b-spinner>
          <strong>Loading...</strong>
        </div>
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { firstName: "alex", lastName: "green" },
        {
          firstName: "may",
          lastName: "smith"
        },
        { firstName: "james", lastName: "jones" }
      ]
    };
  }
};
</script>

We have the busy prop on b-table and populated the table-busy slow with a spinner and the ‘Loading’ text.

Therefore, instead of seeing the table’s content, we’ll see what’s in the slot instead.

Custom Data Rendering

We can render data in a custom way.

For example, we can write:

<template>
  <div id="app">
    <b-table :items="items">
      <template v-slot:cell(firstName)="data">{{ data.value }}</template>

<template v-slot:cell(lastName)="data">
        <b>{{ data.value }}</b>
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { firstName: "alex", lastName: "green" },
        {
          firstName: "may",
          lastName: "smith"
        },
        { firstName: "james", lastName: "jones" }
      ]
    };
  }
};
</script>

We populate the slots for the columns.

The column’s data can be obtained with data .

v-slot:cell(firstName)=”data” gets the firstName property and assigned it to data .

v-slot:cell(lastName)=”data” does the same for lastName .

And the value property has the value of each property.

Therefore, we can render each piece of data our way.

data in the example above has many properties.

index has the row number.

item has the raw record of each entry.

value is the value for the given key.

unformatted has raw values before passing through the formatter function.

field has the normalized field definition object.

detailsShowing is true if the row-details scoped slot is visible.

toggleDetails can be called to toggle the visibility of the row-details scope slot.

rowSelected is true if the row is selected.

selectRow selects the current row when called.

unselectRow unselects the current row when called.

Display Raw HTML

We can display raw HTML.

To do that we just populate the slot with an element that has the v-html directive.

For instance, we can write:

<template>
  <div id="app">
    <b-table :items="items">
      <template v-slot:cell(lastName)="data">
        <span v-html="data.value"></span>
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [{ firstName: "alex", lastName: "<b>green</b>" }]
    };
  }
};
</script>

We have a span that has the v-html directive set to data.value which has our lastName ‘s value.

So the HTML code will be rendered without sanitization.

However, we have to be careful so that we don’t get attacked with cross-site scripting attacks.

Formatter Callback

We can add a formatted to out fields array entries to format our cells.

For instance, we can write:

<template>
  <div id="app">
    <b-table :items="items" :fields="fields"></b-table>
  </div>
</template>
<script>
export default {
  data() {
    return {
      fields: [
        {
          key: "name",
          label: "Full Name",
          formatter: "fullName"
        }
      ],
      items: [
        { firstName: "alex", lastName: "green" },
        {
          firstName: "may",
          lastName: "smith"
        },
        { firstName: "james", lastName: "jones" }
      ]
    };
  },
  methods: {
    fullName(value, key, item) {
      return `${item.firstName} ${item.lastName}`;
    }
  }
};
</script>

We have the fullName method.

It takes the item parameter, which has the object in items , and return the firstName and lastName properties combined together.

The name of it is set as the value of formatter in our field’s array entry.

Therefore, now we have one Full Name column that has both field’s values displayed together.

We can also write:

<template>
  <div id="app">
    <b-table :items="items" :fields="fields"></b-table>
  </div>
</template>
<script>
export default {
  data() {
    return {
      fields: [
        {
          key: "name",
          label: "Full Name",
          formatter(value, key, item) {
            return `${item.firstName} ${item.lastName}`;
          }
        }
      ],
      items: [
        { firstName: "alex", lastName: "green" },
        {
          firstName: "may",
          lastName: "smith"
        },
        { firstName: "james", lastName: "jones" }
      ]
    };
  }
};
</script>

We moved the formatted function to fields entry object instead of having it in methods .

Photo by Marylou Fortier on Unsplash

Conclusion

We can format table cells with formatted functions.

Also, we can customize cell layout and formatting with slots or raw HTML.

We can set the busy state to display something when the table’s data is loading.

Categories
BootstrapVue

BootstrapVue — Pagination Nav Customization and Popovers

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.

We look at how to customize pagination nav links to our page.

Also, we look at how to add popovers.

First and the Last Button

We can add the first-button prop to always include the button for the first page.

And we can use the last-button prop to always include the button for the last page.

For example, we can write:

<template>
  <div>
    <b-pagination-nav v-model="currentPage" :number-of-pages="pages" base-url="#" first-number></b-pagination-nav>
  </div>
</template>

<script>
export default {
  data(){
    return {
      currentPage: 1,
      pages: 20
    }
  }
};
</script>

Then the first page will always be shown since first-number is included.

Button Size

We can change the size prop to change the size of the buttons.

For example, we can write:

<template>
  <div>
    <b-pagination-nav size="sm" v-model="currentPage" :number-of-pages="pages" base-url="#" first-number></b-pagination-nav>
  </div>
</template>

<script>
export default {
  data(){
    return {
      currentPage: 1,
      pages: 20
    }
  }
};
</script>

We have the size prop set to sm to make the buttons smaller than the default.

Also, we can set it to lg to make them larger than the default.

Pill Style

The pills prop can be used to make the buttons look like pills:

<template>
  <div>
    <b-pagination-nav
      pills
      v-model="currentPage"
      :number-of-pages="pages"
      base-url="#"
    ></b-pagination-nav>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentPage: 1,
      pages: 20
    };
  }
};
</script>

Alignment

We can use the align prop to align the pagination nav in the position we want.

For example, we can write:

<template>
  <div>
    <b-pagination-nav align="center" v-model="currentPage" :number-of-pages="pages" base-url="#"></b-pagination-nav>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentPage: 1,
      pages: 20
    };
  }
};
</script>

Now we put the pagination nav in the center.

Other values include 'right' to align the pagination nav to the right.

'fill' makes the nav fill the screen’s width.

Popover

We can add popovers to our Vue app with BootstrapVue.

It provides us with a tooltip.

We can use the v-b-tooltip directive to add it.

For example, we can write:

<template>
  <div id="app" class="text-center">
    <b-button v-b-popover.hover.top="'content'" title="Title">Hover Me</b-button>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}
</style>

We have the v-b-popover component with the top modifier to display the popover on top of the button if there’s room above it.

The value of it is the content.

title is the title of the popover.

Also, we can use the b-popover to add a popover.

To change the title, we can populate the title slot:

<template>
  <div id="app" class="text-center">
    <b-button id="popover">Hover Me</b-button>
    <b-popover target="popover" triggers="hover" placement="top">
      <template v-slot:title>Title</template>
      <b>content</b>
    </b-popover>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}
</style>

We have the b-popover component.

The button has an id that has to match the target prop’s value.

The triggers prop indicates how the popover is triggered.

placement is set to 'top' to show the popover above the button.

We populate the title content by willing the title slot, which is indicated by v-slot:title .

The rest of the code inside the b-popover component is filled as the content.

Popper.js is used for displaying popups.

Positioning

Possible positing include top , topleft , topright , right , righttop , rightbottom , bottom , bottomleft , bottomright , left , lefttop , and leftbottom .

We can set these values as the value of the placement prop.

Triggers

The triggers for showing popovers can be something other than hover.

For instance, we can change it to focus :

<template>
  <div id="app" class="text-center">
    <b-button
      href="#"
      tabindex="0"
      v-b-popover.focus="'content'"
      title="Title"
    >click me</b-button>
  </div>
</template>

<script>
export default {};
</script>

<style>
#app {
  margin: 200px;
}
</style>

Now we only see the popover only when we click on the button.

Conclusion

We can customize the nav page’s button as we wish.

Also, we can add popovers to display something when we click or hover over an element.

Categories
BootstrapVue

BootstrapVue — Customizing Tabs

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. Let’s look at how to customize the tabs that we added.

Vertical Tabs

We can make tabs vertical with the vertical prop:

<template>
  <div id="app">
    <b-card no-body>
      <b-tabs pills card vertical>
        <b-tab title="Tab 1" active>
          <b-card-text>Tab 1</b-card-text>
        </b-tab>
        <b-tab title="Tab 2">
          <b-card-text>Tab 2</b-card-text>
        </b-tab>
      </b-tabs>
    </b-card>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

By default, the controls are rendered on the left.

We can place vertical tab controls on the right with the end prop:

<template>
  <div id="app">
    <b-card no-body>
      <b-tabs pills card vertical end>
        <b-tab title="Tab 1" active>
          <b-card-text>Tab 1</b-card-text>
        </b-tab>
        <b-tab title="Tab 2">
          <b-card-text>Tab 2</b-card-text>
        </b-tab>
      </b-tabs>
    </b-card>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

We can change the width of the tab controls with utility classes:

<template>
  <div id="app">
    <b-card no-body>
      <b-tabs pills card vertical nav-wrapper-class="w-75">
        <b-tab title="Tab 1" active>
          <b-card-text>Tab 1</b-card-text>
        </b-tab>
        <b-tab title="Tab 2">
          <b-card-text>Tab 2</b-card-text>
        </b-tab>
      </b-tabs>
    </b-card>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

The w-75 class makes the controls take up 3/4 of the width of the card.

Active Classes

We can change the active classes by passing in some props.

active-nav-item-class lets us set the class for an active item.

active-tab-class lets us set the class for the active tab content.

content-class lets us set the class for the displayed content.

For example, we can write:

<template>
  <div id="app">
    <b-card no-body>
      <b-tabs
        active-nav-item-class="font-weight-bold"
        active-tab-class="text-success"
        content-class="mt-3"
      >
        <b-tab title="Tab 1" active>
          <b-card-text>Tab 1</b-card-text>
        </b-tab>
        <b-tab title="Tab 2">
          <b-card-text>Tab 2</b-card-text>
        </b-tab>
      </b-tabs>
    </b-card>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

We set active-nav-item-class to font-weight-bold so the control text for the active tab is bold.

active-tab-class is set to text-success , so the content text for the active tab is green.

content-class is set to mt-3 so that we have some margins on top.

Disable Fade

We can add the no-fade prop to disable fade transitions.

Tabs Without Content

We can add tabs without content by populating the tabs-end or tabs-start slot. The tabs without content will be added to the end or start respectively.

For example, we can write:

<template>
  <div id="app">
    <b-tabs>
      <b-tab title="Tab 1" active>
        <b-card-text>Tab 1</b-card-text>
      </b-tab>
      <b-tab title="Tab 2">
        <b-card-text>Tab 2</b-card-text>
      </b-tab>

      <template v-slot:tabs-end>
        <b-nav-item href="#" role="presentation" @click="() => {}">Empty 1</b-nav-item>
        <li class="nav-item align-self-center">Empty 2</li>
      </template>
    </b-tabs>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

Now we have the Empty 1 and Empty 2 tabs that have no content to the right of the other tabs.

Custom Content in Tab Title

We can populate slots to add custom content to them.

To do that, we populate the title slot of each tab.

For example, we can write:

<template>
  <div id="app">
    <b-tabs>
      <b-tab active>
        <template v-slot:title>
          <b-spinner small></b-spinner>
          <b>Tab 1</b>
        </template>
        <p class="p-3">Tab 1</p>
      </b-tab>

      <b-tab>
        <template v-slot:title>Tab 2</template>
        <p class="p-3">Tab 2</p>
      </b-tab>
    </b-tabs>
  </div>
</template>
<script>
export default {
  name: "App"
};
</script>

We populated the title slot of each tab with our own content.

In the first tab, we added a spinner with text.

In the 2nd tab, we just have text.

Conclusion

We can make tabs vertical or wider than usual. The text of the tabs can also be changed. Tabs also don’t have to have content.

Categories
BootstrapVue

BootstrapVue — Tabs

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.

We look at how to add tabs to our Vue apps.

Tabs

We can add tabbable panes into our app with the b-tabs component.

For example, we can write:

<template>  
  <div id="app">  
    <b-tabs content-class="mt-3">  
      <b-tab title="First" active>  
        <p>first tab</p>  
      </b-tab>  
      <b-tab title="Second">  
        <p>second tab</p>  
      </b-tab>  
      <b-tab title="Disabled" disabled>  
        <p>Bisabled tab</p>  
      </b-tab>  
    </b-tabs>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

We added the b-tabs component which houses the b-tab components.

b-tab has the tab heading as set by the title .

We can click on a tab heading to go to a tab.

disabled means the tab is disabled.

We can’t go to a disabled tab.

Cards Integration

We can put tabs into cards.

For instance, we can write:

<template>  
  <div id="app">  
    <b-card no-body>  
      <b-tabs card>  
        <b-tab title="One" active>  
          <b-card-text>Tab 1</b-card-text>  
        </b-tab>  
        <b-tab title="Two">  
          <b-card-text>Tab 2</b-card-text>  
        </b-tab>  
      </b-tabs>  
    </b-card>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

We disable the card body with the no-body prop.

Then we can display the tab content by putting b-tabs inside the b-card .

We can use b-card-text in a b-tab to display the content in the card.

We set the default active tab with the active prop.

The card prop on b-tabs make it work in card mode so that it looks correct in a card.

Subcomponents in b-tab will have the card-body class applied if it’s in card mode.

For example, if we have:

<template>  
  <div id="app">  
    <b-card no-body>  
      <b-tabs card>  
        <b-tab title="One" active>  
          <b-card-img bottom src="https://placekitten.com/200/200" alt="kitten"></b-card-img>  
          <b-card-footer>kitten</b-card-footer>  
        </b-tab>  
        <b-tab title="Two">  
          <b-card-text>Tab 2</b-card-text>  
        </b-tab>  
      </b-tabs>  
    </b-card>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

Then everything inside has the card-body class added.

Pills

We can display tabs in pills form with the pills prop:

<template>  
  <div id="app">  
    <b-card no-body>  
      <b-tabs pills card>  
        <b-tab title="Tab 1" active>  
          <b-card-text>Tab 1</b-card-text>  
        </b-tab>  
        <b-tab title="Tab 2">  
          <b-card-text>Tab 2</b-card-text>  
        </b-tab>  
      </b-tabs>  
    </b-card>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

Fill

The fill prop can be used to fill the available horizontal space proportionally with the tabs.

For example, we can write:

<template>  
  <div id="app">  
    <b-tabs fill>  
      <b-tab title="Tab 1" active>  
        <b-card-text>Tab 1</b-card-text>  
      </b-tab>  
      <b-tab title="Tab 2">  
        <b-card-text>Tab 2</b-card-text>  
      </b-tab>  
    </b-tabs>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

Justified

The justified prop lets us make the tabs equal width:

<template>  
  <div id="app">  
    <b-tabs justified>  
      <b-tab title="Tab 1" active>  
        <b-card-text>Tab 1</b-card-text>  
      </b-tab>  
      <b-tab title="Tab 2">  
        <b-card-text>Tab 2</b-card-text>  
      </b-tab>  
    </b-tabs>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

Alignment

The align prop lets us align the tabs.

The possible values are left , center , and right .

Therefore, we can write:

<template>  
  <div id="app">  
    <b-tabs align="center">  
      <b-tab title="Tab 1" active>  
        <b-card-text>Tab 1</b-card-text>  
      </b-tab>  
      <b-tab title="Tab 2">  
        <b-card-text>Tab 2</b-card-text>  
      </b-tab>  
    </b-tabs>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

Bottom Placement of Tab Controls

We can place the tab controls below the tab content with the end prop:

<template>  
  <div id="app">  
    <b-card no-body>  
      <b-tabs pills card end>  
        <b-tab title="Tab 1" active>  
          <b-card-text>Tab 1</b-card-text>  
        </b-tab>  
        <b-tab title="Tab 2">  
          <b-card-text>Tab 2</b-card-text>  
        </b-tab>  
      </b-tabs>  
    </b-card>  
  </div>  
</template>  
<script>  
export default {  
  name: "App"  
};  
</script>

It looks better will the pills prop to make the controls look like a button.

Conclusion

We can add tabs to display tabbed content.

It also integrates with cards.