Keith

Vue CDN Playground

by Keith Rowles • 27/10/2023Vue

Silver key on a black desk

Summary

Playing around with the Vue JS library using CDN links. 3 examples - product page, data binding and dynamic pages.

I will demonstrate some of these Vue JS features:

  • v-bind
  • v-for
  • v-if
  • v-else
  • v-text
  • v-html
  • v-show
  • v-on
  • v-model
  • v-slot
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

I will also add the bootstrap css framework for some basic styles.

<link
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
  rel="stylesheet"
  integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
  crossorigin="anonymous"
/>

<!-- Bootstrap Icons -->
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css"
/>

Demo 1

Product page example using v-bind and v-for. Using Vue JS options API.

<div class="container-sm">
  <div id="app">
    <h1>{{ name }}</h1>
    <img v-bind:src="imgSrc" v-bind:alt="name" class="img-fluid" />
    <p>{{description}}</p>
    <div>{{price}}</div>

    <section class="container">
      <div
        v-for="product in products"
        v-bind:key="product.id"
        id="display-list"
        class="row d-flex mb-3 align-items-center"
      >
        <div class="col-sm-4">
          <img
            class="img-fluid d-block"
            v-bind:src="product.image"
            v-bind:alt="product.name"
          />
        </div>
        <div class="col">
          <h3 class="text-info">{{product.name}}</h3>
          <p class="mb-0">{{product.description}}</p>
          <div class="h5 float-right">{{product.price}}</div>
        </div>
      </div>
    </section>
  </div>
</div>
// Global API Initialization
Vue.createApp({
  data() {
    return {
      name: 'Bamboo Thermal Ski Coat',
      description:
        'Youll be the most environmentally conscious skier on the slopes - and the most stylish - wearing our fitted bamboo thermal ski coat, made from organic bamboo with recycled plastic down filling.',
      imgSrc:
        'https://hplussport.com/wp-content/uploads/2016/12/ski-coat_LYNDA_29940.jpg',
      price: 99,
      products: [
        {
          id: '532',
          name: 'Slicker Jacket',
          description:
            'Wind and rain are no match for our organic bamboo slicker jacket for men and women. Triple stitched seams, zippered pockets, and a stay-tight hood are just a few features of our best-selling jacket.',
          price: '125',
          image_title: 'slicker-jacket_lynda_29941',
          image:
            'https://hplussport.com/wp-content/uploads/2016/12/slicker-jacket_LYNDA_29941.jpg',
        },
        {
          id: '530',
          name: 'Bamboo Thermal Ski Coat',
          description:
            "You'll be the most environmentally conscious skier on the slopes - and the most stylish - wearing our fitted bamboo thermal ski coat, made from organic bamboo with recycled plastic down filling.",
          price: '99',
          image:
            'https://hplussport.com/wp-content/uploads/2016/12/ski-coat_LYNDA_29940.jpg',
        },
        {
          id: '516',
          name: 'Unisex Thermal Vest',
          description:
            "Our thermal vest, made from organic bamboo with recycled plastic down filling, is a favorite of both men and women. You'll help the environment, and have a wear-easy piece for many occasions.",
          price: '95',
          image:
            'https://hplussport.com/wp-content/uploads/2016/12/unisex-thermal-vest_LYNDA_29944.jpg',
        },
      ],
    };
  },
}).mount('#app');

Open demo on Code Pen.

Link to Demo

Demo 2

This is a sandbox and playground showing Vue JS options API.

const { createApp } = Vue;

const app = createApp({
  data() {
    return {
      count: 0,
      message: 'Hello Vue!',
      rawHtml:
        '<span style="color: red">This should be red. Output the html.</span>',
      imageSrc: 'http://127.0.0.1:5500/business-blocks.jpg',
      isPale: true,
      isButtonDisabled: true,
      alert: {
        role: 'alert',
        class: 'alert alert-primary',
      },
      number: 10,
      ok: true,
      seen: true,
      url: 'https://vuejs.org',
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - Mystery Guide',
        ],
      },
      firstName: 'Keith',
      lastName: 'Rowles',
      isActive: true,
      hasError: true,
      error: null,
      activeLinkClass: 'link-primary',
      bigLinkClass: 'fs-3',
      errorClass: 'alert-danger',
      activeColour: 'blue',
      fontOne: 30,
      styleObject: {
        color: 'green',
        fontSize: '20px',
      },
      styleObjectOverride: {
        fontWeight: 'bold',
        fontStyle: 'italic',
      },
      isVueAwesome: false,
      sometype: 'B',
      items: [
        { message: 'Foo', id: 1 },
        { message: 'Bar', id: 2 },
      ],
      myObject: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2016-04-10',
      },
      numbers: [1, 2, 3, 4, 5],
      name: 'Vue JS',
      newmessage: 'This is a new message',
      picked: 'One',
      selected: 'A',
      names: ['Tom', 'Bob', 'Sam'],
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' },
      ],
      show: true,
    };
  },
  provide() {
    return {
      footertitle: 'My fav footer title',
    };
  },
  methods: {
    showMe() {
      this.seen = !this.seen;
    },
    increment() {
      this.count++;
    },
    onSubmit() {
      console.log('Prevent Default');
    },
    greet(event) {
      alert(`Hello ${this.name}`);
      if (event) {
        alert(event.target.tagName);
      }
    },
    say(message) {
      alert(message);
    },
    warn(message, event) {
      if (event) {
        event.preventDefault();
      }
      alert(message);
    },
  },
  // Lifecycle Hooks
  mounted() {
    this.increment(), console.log(this.$refs.names);
  },
  onMounted() {},
  onUnmounted() {},
  // Computed Property - Are Cached - Must Return Something
  computed: {
    publishedBooksMessage() {
      return this.author.books.length > 0 ? 'Yes' : 'No';
    },
    fullName: {
      get() {
        return this.firstName + ' ' + this.lastName;
      },
      set(newValue) {
        [this.firstName, this.lastName] = newValue.split(' ');
      },
    },
    classObject() {
      return {
        active: this.isActive && !this.error,
        'text-dark': !this.error,
        'text-uppercase': !this.error,
      };
    },
    evenNumbers() {
      return this.numbers.filter((n) => n % 2 === 0);
    },
  },
});

app.component('myheading', {
  props: ['headingtext'],
  template: `
        <h6>{{headingtext}} <span class="badge bg-secondary">New</span></h6>
        `,
});

app.component('blogpost', {
  props: ['title'],
  template: `
        <h4>{{ title }}</h4>
        `,
});

app.component('newspost', {
  props: {
    newsTitle: String,
    newsDate: Number,
  },
  template: `
        <h4>{{ newsTitle }}</h4>
        <p>{{ newsDate }}</p>
        <button @click="$emit('customEvent')" class="btn btn-primary">click me</button>
        `,
  emits: ['customEvent'],
});

app.component('appFooter', {
  inject: ['footertitle'],
  template: `
        <h2>This is the footer - {{ footertitle }}</h2>
        `,
});

app.mount('#app');

Some styles.

.pale {
  color: palevioletred;
}

.boldtext {
  font-weight: bold;
}

.italictext {
  font-style: italic;
}
.active {
  background-color: brown;
  padding: 20px;
  color: white;
}
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

.fade-enter-active {
  transition: all 0.5s ease-out;
}

.fade-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.fade-enter-from,
.fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}
.imgSrc {
  width: 50%;
}

Demo

Open demo on JS Fiddle.

Link to Demo