/ Gists / Vue.js

Gists - Vue.js

On gists

Accordion

Vue.js

Accordion.vue #

<template>
  <ul>
    <slot></slot>
  </ul>
</template>

<script>
export default {
  props: {},
  data() {
    return {
      AccordionState: {
        count: 0,
        active: 1
      }
    }
  },
  provide() {
    return { AccordionState: this.AccordionState }
  }
}
</script>

<style lang="scss" scoped></style>

On gists

Mounted teleport

Vue.js

MountedTeleport.vue #

<!--https://stackoverflow.com/questions/63652288/does-vue-3-teleport-only-works-to-port-outside-vue-->

<template>
    <Teleport :to="to" v-if="isMounted"><slot></slot></Teleport>
</template>

<script>
export default {
    name: "MountedTeleport",
    props: ['to'],
    data() {
        return {isMounted: false}
    },
    mounted() {
        this.isMounted = true;
    }
}
</script>

On gists

Computed with delay, resize window

Vue.js

app.vue #

<!-- https://codepen.io/sandrarodgers/pen/porZbxW --> 

<template>
  <div id="app">
    <div>Height: {{ height }}</div>
    <div>Width: {{ width }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      debouncedHeight: 0,
      debouncedWidth: 0,
      heightTimeout: null,
      widthTimeout: null
    };
  },
  computed:  {
	  height:  {
		  get()  {
			  return  this.debouncedHeight;
		  },
		  set(val)  {
			  if  (this.timeout)  clearTimeout(this.timeout);
			  this.heightTimeout =  setTimeout(()  =>  {
				  this.debouncedHeight = val;
				},  5000);
			}
		},
		width:  {
		  get()  {
			  return  this.debouncedWidth;
		  },
		  set(val)  {
			  if  (this.timeout)  clearTimeout(this.timeout);
			  this.widthTimeout =  setTimeout(()  =>  {
				  this.debouncedWidth = val;
				},  5000);
			}
		},
	},
  methods: {
    resizeHandler(e) {
      this.height = window.innerHeight;
      this.width = window.innerWidth;
    },
  },
  mounted() {
    this.height = window.innerHeight;
    this.width = window.innerWidth;
  },
  created() {
    window.addEventListener("resize", this.resizeHandler);
  },
  destroyed() {
    window.removeEventListener("resize", this.resizeHandler);
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  font-size: 1.5rem;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

On gists

JS Doc

JavaScript DOC Vue.js

example.js #

  data() {
    return {
      // https://medium.com/@trukrs/type-safe-javascript-with-jsdoc-7a2a63209b76
      // https://devhints.io/jsdoc
      // better suggestion ... eg:  this.overlay?. + tab

      /**
       * @type {boolean}
       */
      delaySet: false, // je nastaveny delay?

      /**
       * @type {?number}
       */
      timeout: null, // timeout delay

      /**
       * @type {number}
       */
      ttlIn: 400, // delka prodlevy najeti

      /**
       * @type {number}
       */
      ttlOut: 300, // delka prodlevy pri odjeti

      /**
       * @type {HTMLElement}
       */
      overlay: null, // HTML prvek zacileneho overlaySelector-u

      /**
       * @type {Array}
       */
      listChildren: [] // LI-cka pod zacilenym UL-kem
    }
  },

On gists

Multiple methods in one event

Vue.js

events.js #

<transition
  name="card"
  @enter="startTransition($event), onEnter($event)"
  @leave="onLeave"
  @after-enter="(el) => { endTransition(el), onFoo(el) }"
  @before-leave="startTransition"
  @after-leave="endTransition"
>

On gists

Ref slot (Options vs Composition, Parent / Child, Child / Parent)

Popular ⭐ Vue.js

ChildToParent-ScopedSlot.vue #

<!-- 1) From Child to Parent via scoped slot and dynamic ref-->

<!-- App.vue-->
<template>
  <div id="app">
    <HelloWorld v-slot="{ setRef }">
      <input type="text" :ref="(el) => setRef(el)" />
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
};
</script>


<!-- Hello world -->
<template>
    <slot v-bind="{ setRef }"></slot>
    <button @click="setFocus">SET</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      ref: ''
    }
  },
  methods: {
    setFocus()
    {
      this.ref.focus()
    },
    setRef(el) {
     this.ref = el
     console.log(el)
    }
  }
}
</script>

On gists

Emit, callback in parent

Vue.js

example.vue #

<template>
<ClickWait color="red" @click="doFoo">Darn Button</ClickWait>
 <AnotherClickWait style="background-color:#c0c0c0" @click="doFoo">One More Darn Button</AnotherClickWait>
</template>

import ClickWait from '@/components/ClickWait.vue';
import AnotherClickWait from '@/components/AnotherClickWait.vue';

export default {
  name: "App",
  components: {
    ClickWait, AnotherClickWait
  },
  methods:{
    doFoo(done) {
      setTimeout(() => {
        console.log('im done with whatever biz logic');
        done();
      },3000);
    }
  }
};

On gists

Filtering & searching in list + URL history state

Vue.js

app.vue #

// https://www.raymondcamden.com/2021/05/08/updating-and-supporting-url-parameters-with-vuejs

// hard coded for simplicity...
const ITEMS = [
	{ name: "Ray", type: "person" },
	{ name: "Lindy", type: "person" },
	{ name: "Jacob", type: "person" },
	{ name: "Lynn", type: "person" },
	{ name: "Noah", type: "person" },
	{ name: "Jane", type: "person" },
	{ name: "Maisie", type: "person" },
	{ name: "Carol", type: "person" },
	{ name: "Ashton", type: "person" },
	{ name: "Weston", type: "person" },
	{ name: "Sammy", type: "cat" },
	{ name: "Aleese", type: "cat" },
	{ name: "Luna", type: "cat" },
	{ name: "Pig", type: "cat" },
	{ name: "Cayenne", type: "dog" }
]

const app = new Vue({
	el:'#app',
	data: {
		allItems: ITEMS,
		filter:'',
		typeFilter:[]
	},
	created() {
		let qp = new URLSearchParams(window.location.search);
		let f = qp.get('filter');
		if(f) this.filter = qp.get('filter');
		let tf = qp.get('typeFilter');
		if(tf) this.typeFilter = tf.split(',');
	},
	computed: {
		items() {
			this.updateURL();
			return this.allItems.filter(a => {
				if(this.filter !== '' && a.name.toLowerCase().indexOf(this.filter.toLowerCase()) === -1) return false;
				if(this.typeFilter.length && !this.typeFilter.includes(a.type)) return false;
				return true;
			});
		}
	},
	methods:{
		updateURL() {
			let qp = new URLSearchParams();
			if(this.filter !== '') qp.set('filter', this.filter);
			if(this.typeFilter.length) qp.set('typeFilter', this.typeFilter);
			history.replaceState(null, null, "?"+qp.toString());
		}
	}
});

On gists

nextTick

Vue.js

component.vue #

<!--
https://chafikgharbi.com/vuejs-nexttick/
https://codepen.io/chafikgharbi/pen/WNGarPG

-->


<template>
  <div id="app">
    <div ref="box" class="box">
      <div v-for="(message, key) in messages" :key="key">{{message.body}}</div>
      ...
    </div>
    <button v-on:click="addMessage">Add message</button>
  </div>
</template>

<style>
.box {
  height: 100px;
  width: 300px;
  overflow: scroll;
  border: 1px solid #000
}
</style>

<script>
export default {
  name: "App",
  data: () => {
    return {
      messages: [
        { body: "Message 1" },
        { body: "Message 2" },
        { body: "Message 3" },
        { body: "Message 4" },
        { body: "Message 5" }
      ]
    };
  },
  methods: {
    async addMessage() {

      // Add message
      this.messages.push({ body: `Message ${this.messages.length+1}`})

      // Scroll bottom
      this.$refs.box.scrollTop = this.$refs.box.scrollHeight
    },
    
    async addMessage() {
  
  // Add message
  this.messages.push({ body: `Message ${this.messages.length+1}`})

  // Wait for DOM to update
  await this.$nextTick()
  
  // Scroll bottom
  this.$refs.box.scrollTop = this.$refs.box.scrollHeight
},
    
    async addMessage() {
  
  // Add message
  this.messages.push({ body: `Message ${this.messages.length+1}`})

  // Wait for DOM to updaten then scroll down
  this.$nextTick(() => {
    this.$refs.box.scrollTop = this.$refs.box.scrollHeight
  })
  
}
  }
};
</script>

On gists

Simple global state hook aka composable

Vue.js

state.js #

import { ref, readonly } from 'vue';

const sharedVar = ref(false);

const toggleOutside = () => {
  sharedVar.value = !sharedVar.value;
};

export const useTestState = () => {
  const toggleInside = () => {
    sharedVar.value = !sharedVar.value;
  };

  return {
    sharedVar: readonly(sharedVar),
    toggleOutside,
    toggleInside,
  };
};