⬆️ ⬇️

Using slots in Vue using the product snippet as an example

When working with projects where SSR (Server Side Rendering) is not used or its implementation is impossible, the problem arises that some functions or logic are written twice for static elements that print backend and for components that render Vue.



For example, we need to implement a product snippet component that has a number of requirements:





One solution:

')

  1. Write logic for static snippets, while hanging up click events, add and remove load classes on the "Buy" button
  2. Separately write a component on Vue that implements the same logic in template format only.
  3. Use the first item to output from the backend, the second for example to output pure data obtained from the state or ajax


Thus, we will write two logic, one will work directly with the DOM, the other with clean data.



My solution to this problem is to use - slots, namely the ability to set the default content, which will just contain dynamic parameters, but at the same time if the component is used as an inline-template, all these parameters will be replaced with the backend drawn.



Let's write a component that can handle both static and dynamic:



Component listing
<template> <div> <!--   image   ,       --> <slot name="image"> <!--    --> <a :href="url" class="snippet__image"> <img :src="image"> </a> </slot> <slot name="title"> <a :href="url" class="snippet__title">{{ title }}</a> </slot> <div v-if="!inCart" @click="add" :class="{ 'snippet__buy--load': load }" class="snippet__control" > <slot name="button"> <div class="snippet__button"></div> </slot> </div> <div v-if="inCart" class="snippet__control"> <div class="snippet__button"></div> </div> <div v-if="load" class="snippet__load"></div> </div> </template> <script> //  vuex     import { mapState, mapActions } from 'vuex' export default { props: { id: { type: Number, required: true }, url: { type: String }, image: { type: String }, title: { type: String } }, data() { return { //   load: false, //     inCart: false, } }, computed: { ...mapState({ //     cartItems: ({cart}) => cart.items }), }, mounted() { this.$nextTick(() => { //      this.inCart = this.cartItems.some(item => item.id === this.id) }) }, methods: { ...mapActions([ //     'addToCart' ]), add() { //    this.load = true //   this.addToCart({ id: this.id }) } }, watch: { //      cartItems(items) { //    this.load = false //      this.inCart = items.some(item => item.id === this.id) } } } </script> 




Using the component via the backend:



 <snippet :id="1" class="snippet"> <a slot="image" href="#" class="snippet__image"> <img src="photo.jpg"> </a> <a slot="title" href="#" class="snippet__title"> 1</a> <div slot="button" class="snippet__button"></div> </snippet> 


Use of component in other components:



 <catalog-list> <snippet v-for="item in items" :key="item.id" v-bind="item"></snippet> </catalog-list> 


Now we have one component that can be used in different cases.



I would like to hear your opinion on this approach, perhaps there is a better solution.

Source: https://habr.com/ru/post/422465/



All Articles