Skip to content

Instantly share code, notes, and snippets.

@aelbore
Last active July 24, 2019 10:32
Show Gist options
  • Save aelbore/75fce6239e86f5a3889e81f126169254 to your computer and use it in GitHub Desktop.
Save aelbore/75fce6239e86f5a3889e81f126169254 to your computer and use it in GitHub Desktop.
Step by Step creating web components in Vue

Getting Started

mkdir vue-app
cd vue-app

npm init -y
npm install --save-dev vue @vue/cli http-server

Create Vue App

  • Create src folder
  • Create ./src/Card.vue please see code
  • Create ./src/App.vue please see code
  • Create ./src/main.js please code code
  • Add or Update scripts in package.json
    • serve run your vue app into dev mode
    • build.card.wc bundle and wrap your Card Component into web components
    • start.wc start or run your web component bundle into dev mode
      "scripts": {
        "serve": "vue serve ./src/main.js",
        "build.card.wc": "vue build --target wc --name ar-card ./src/Card.vue",
        "start.wc": "http-server ./dist",
      },
  • Run your vue app
    npm run serve
    
  • Bundle your vue Card Component into web component
    npm run build.card.wc
    
  • Run your bundle web component
    • Update ./dist/demo.html
      <style>
        #cards {
          display: grid;
          grid-template-columns: repeat(4, 1fr);
          grid-auto-rows: auto;
          grid-gap: 1rem; 
      
          font-family: "Avenir", Helvetica, Arial, sans-serif;
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
          text-align: center;
          color: #2c3e50;
        }
      </style>
      
      <div id="cards"></div>
      
      <script>
        const profiles = [{
            name: "Jane Doe",
            profession: "Framework Developer",
            motto: "I never wanted to be famous, I wanted to be great.",
            photo: "https://pymwoqn637.codesandbox.io/default.png"
          },
          {
            name: "Kurtis Weissnat",
            profession: "Developer",
            motto: "When in doubt, iterate faster!",
            photo: "https://pymwoqn637.codesandbox.io/default.png"
          }
        ]
      
        const root = document.getElementById('cards')
        
        profiles.forEach(profile => {
          const card = document.createElement('ar-card')
          root.appendChild(card)
      
          card.profile = { ...profile }
        })
      </script>
      • Run your demo
        npm run start.wc
        
      • Browse app
        http://localhost:8080/demo.html
        

Use Vue Elements in Angular application

  • Create new project using angular cli
    ng new ng-vue-elements
    
  • Run the project
    cd ng-vue-elements
    ng serve --open
    
  • Replace the content of your ./src/app/app.component.html to:
    <div style="text-align:center">
      <h1>
        Welcome to {{ title }}!
      </h1>
      <img width="300" alt="Angular Logo" src="https://angular.io/assets/images/logos/angular/angular.svg">
    </div>
  • Update ./src/app/app.module.ts
    • add schemas and provide CUSTOM_ELEMENTS_SCHEMA to allow non angular component
    • add imports CommonModule to use common directives i.e *ngFor
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
    import { CommonModule } from '@angular/common'
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        CommonModule
      ],
      providers: [],
      bootstrap: [AppComponent],
      schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
    })
    export class AppModule { }
  • Update ./src/app/app.component.html
    <div style="text-align:center">
      <h1>
        Welcome to {{ title }}!
      </h1>
      <img width="300" alt="Angular Logo" src="https://angular.io/assets/images/logos/angular/angular.svg">
    </div>
    <div class="cards">
      <div *ngFor="let profile of profiles">
        <ar-card [profile]="profile"></ar-card>
      </div>
    </div>
  • Update your styles
    • ./src/app/app.component.css
    .cards {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      grid-auto-rows: auto;
      grid-gap: 1rem; 
    
      font-family: "Avenir", Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
  • Update ./src/app.component.ts, add property profiles
    profiles = [
      {
        name: "Jane Doe",
        profession: "Framework Developer",
        motto: "I never wanted to be famous, I wanted to be great.",
        photo: "https://pymwoqn637.codesandbox.io/default.png"
      },
      {
        name: "Kurtis Weissnat",
        profession: "Developer",
        motto: "When in doubt, iterate faster!",
        photo: "https://pymwoqn637.codesandbox.io/default.png"
      },
      {
        name: "Chelsey Dietrich",
        profession: "UX Developer",
        motto: "Genius is the ability to reduce the complicated to the simple.",
        photo: "https://pymwoqn637.codesandbox.io/default.png"
      },
      {
        name: "Leanne Graham",
        profession: "UI Developer",
        motto: "The key to performance is elegance, not battalions of special cases.",
        photo: "https://pymwoqn637.codesandbox.io/default.png"
      }
    ]
  • Copy the bundle of your vue-elements (from <path-of-your-vue-elements-bundle> to ./src/assets/vue)
  • Update ./src/index.html
    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>NgVueElements</title>
      <base href="/">
    
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
      <script src="https://unpkg.com/vue"></script>
      <script src="/assets/vue/ar-card.min.js"></script>
    </head>
    <body>
      <app-root></app-root>
    </body>
    </html>
  • Run your app
    ng serve --open
    
<template>
<div id="app">
<div style="text-align:center;margin-bottom:20px;">
<h1>Welcome to Vue</h1>
<img width="200" src="https://vuejs.org/images/logo.png" />
</div>
<div class="cards">
<div v-for="(profile, index) in profiles" v-bind:key="index">
<Card :profile="profile" />
</div>
</div>
</div>
</template>
<script>
import Card from './Card.vue'
const getProfiles = () => {
return [
{
name: "Jane Doe",
profession: "Framework Developer",
motto: "I never wanted to be famous, I wanted to be great.",
photo: "https://pymwoqn637.codesandbox.io/default.png"
},
{
name: "Kurtis Weissnat",
profession: "Developer",
motto: "When in doubt, iterate faster!",
photo: "https://pymwoqn637.codesandbox.io/default.png"
},
{
name: "Chelsey Dietrich",
profession: "UX Developer",
motto: "Genius is the ability to reduce the complicated to the simple.",
photo: "https://pymwoqn637.codesandbox.io/default.png"
},
{
name: "Leanne Graham",
profession: "UI Developer",
motto: "The key to performance is elegance, not battalions of special cases.",
photo: "https://pymwoqn637.codesandbox.io/default.png"
},
]
}
export default {
name: 'app',
components: {
Card
},
data() {
return {
profiles: {
...getProfiles()
}
}
}
}
</script>
<style>
.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: auto;
grid-gap: 1rem;
}
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
<template>
<div class="card-container">
<div class="card">
<div class="front">
<div align="right" class="cover">
<div class="icon-x">
<a @click="remove()">X</a>
</div>
</div>
<div class="user">
<img class="img-circle" alt="" :src="profile.photo" />
</div>
<div class="content">
<div class="main">
<h3 class="name">{{ profile.name }}</h3>
<p class="profession">{{ profile.profession }}</p>
<p class="text-center">{{ profile.motto }}</p>
</div>
</div>
</div>`
</div>
</div>
</template>
<script>
/// https://github.com/vuejs/vue/issues/4638
/// vue will not support non standard syntax
const profile = {
name: '',
profession: '',
motto: '',
photo: ''
}
export default {
props: {
profile: {
type: Object,
default() {
return { ...profile }
}
}
},
methods: {
remove(e) {
this.$emit("remove", this.profile);
}
}
};
</script>
<style>
.icon-x {
padding: 20px;
cursor: pointer;
}
.card-container {
perspective: 800px;
margin-bottom: 30px;
}
.card {
transition: transform 0.5s;
transform-style: preserve-3d;
position: relative;
}
.front {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
background-color: #fff;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.14);
}
.front {
z-index: 2;
}
.card {
background: none repeat scroll 0 0 #ffffff;
border-radius: 4px;
color: #444444;
}
.card-container,
.front {
width: 100%;
height: 370px;
border-radius: 4px;
}
.card .cover {
height: 90px;
overflow: hidden;
border-radius: 4px 4px 0 0;
background: #42b883;
}
.card .cover img {
width: 100%;
display: none;
}
.card .user {
border-radius: 50%;
display: block;
height: 120px;
margin: -55px auto 0;
overflow: hidden;
width: 120px;
}
.card .user img {
background: none repeat scroll 0 0 #ffffff;
border: 4px solid #ffffff;
width: 100%;
}
.card .content {
background-color: rgba(0, 0, 0, 0);
box-shadow: none;
padding: 10px 20px 20px;
}
.card .content .main {
min-height: 160px;
}
.card .back .content .main {
height: 215px;
}
.card .name {
font-size: 20px;
line-height: 28px;
margin: 10px 0 0;
text-align: center;
text-transform: capitalize;
font-weight: lighter;
}
.card .profession {
color: #999999;
text-align: center;
margin-bottom: 20px;
}
.card .motto {
border-bottom: 1px solid #eeeeee;
color: #999999;
font-size: 14px;
font-weight: 400;
padding-bottom: 10px;
text-align: center;
}
</style>
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment