solution • VueJS coding - martindubenet/wed-dev-design GitHub Wiki

Vue.js coding  ][  Vuetify templates  ][  Vue.js setup for Vuetify  ][  Vue headless CMS site  ]

Vue.js

Although Vue.JS is a complete JavaScript middleware framework, this documentation will only address the front-end coding part of the solution.

Basically, a middleware framework consists of four parts: an interface definition language (IDL), an interoperability protocol, an object request broker (ORB), and additional supporting services. - sciencedirect.com

File skeleton

HTML (DOM) JavaScript CSS
<template>
</template>
<script>
</script>
<style>
</style>

Use these optional props by default for Sass: <style lang="scss" scoped>

Click to toggle FileExample.vue code
<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
<template>
  <fragment>
    <div class="container" :class="{ 'value-is-true' : valueState }">
      <h3>{{ propStringExample }}</h3>
      <p>{{ dataStringExample }}</p>
    </div>

    <required-component />

  </fragment>
</template>

<script>
import RequiredComponent from "@/components/RequiredComponent";

export default {
  name: "FileExample",
  components: { RequiredComponent },
  
  props: {
    propStringExample: {
      type: String,
      default: "This is a string example",
    },
  },
  data() {
    return {
      dataStringExample: "Static Data String",
      valueState: true,
    };
  },
};
</script>

<style lang="scss" scoped>
.container {
  h3 {
    color: Red;
  }
  &.value-is-true {
    h3 {
      color: Green;
    }
  }
}
</style>

Debugging in VS Code

https://v2.vuejs.org/v2/cookbook/debugging-in-vscode.html

vDOM and refs

Vue, like some other frameworks, uses a virtual DOM (VDOM) to manage elements. This means that Vue keeps a representation of all of the nodes in our app in memory. Any updates are first performed on the in-memory nodes, and then all the changes that need to be made to the actual nodes on the page are synced in a batch.

Since reading and writing actual DOM nodes is often more expensive than virtual nodes, this can result in better performance. However, it also means you often should not edit your HTML elements directly through native browser APIs (like Document.getElementById) when using frameworks, because it results in the VDOM and real DOM going out of sync.

Instead, if you need to access the underlying DOM nodes (like when setting focus), you can use Vue refs. For custom Vue components, you can also use refs to directly access the internal structure of a child component, however this should be done with caution as it can make code harder to reason about and understand.

To use a ref in a component, you add a ref attribute to the element that you want to access, with a string identifier for the value of the attribute. It's important to note that a ref needs to be unique (id) within a component. No two elements rendered at the same time should have the same ref.

— source MDN

 

JavaScript Properties

name:

Name is a basic single declaration property. As best practices it is expected to match the name of your .vue component file.

<script>
export default {
  name: "ExampleFile",
}
</script>

The $options object is the resolved component options used for instantiating the current component instance. It has various metadata, namely the property « $options.name » which returns the name value, that can be useful within the <template>.

data()

  data() {
    return {
      stringExample: "Lorem ipsum",
      arrayExample: ["primary", "secondary", "neutral"],
    },
  },

props:

Props are custom parameters that we can register on a customized component which allow us to pass different types of settings and HTML attributes to the DOM of the rendered components.

Types of Props
  1. Array
  2. Boolean
  3. Date
  4. Function
  5. Number
  6. Object
  7. String
  8. Symbol.
Add a new dynamic prop
  1. Make sure that you can NOT match the required result using existing Vue or Vuetify Props
  2. Within <script>, under the name: "", if not already existing, declare props: {},
  3. Within props: {}, add an array named according to the chamelCase nomenclature of your myNewProp: {},.
  4. Declare the type (pick one from the list above),
  5. For validation set your prop required: true, or required: false,
  6. Set either a default: "value" (or false for an opt-in), or declare your prop required: true to set validation.
  7. Make sure every line ends with a coma « , » character,
  8. Apply it within <template>.
<template>
  <p v-if="aDynamicHello" :class="{ "dynamic-class-example": isDynamicCssClass }" class="static-class">
    {{ aDynamicHello }}
  </p>
  <p v-else-if="!aDynamicHello && aDynamicDoI" class="static-class">
    {{ aDynamicDoI }}
  </p>
  <p v-else class="static-class">
    {{ defaultText ? defaultText : "Well goodby then." }}
  </p>
<template>
<script>
export default {
  props: {
    isDynamicCssClass: { 
      type: Boolean,
      default: false,
    },
    aDynamicHello: { 
      type: String,
      required: true,
      default: "Hello world!",
    },
    aDynamicDoI: { 
      type: String,
      required: false,
      default: "Do I know you?",
    },
},
</script>

Vue Life Cycle Hooks

  1. Creation
    1. beforeCreated() : Called when the instance is initialized.
    2. created() : Called after the instance has finished processing all state-related options.
  2. Mounting
    1. beforeMount() : Called right before the component is to be mounted.
    2. mounted() : Called after the component has been mounted. Could be seen as the javascript onload event.
  3. Updating
    1. beforeUpdate() : Called right before the component is about to update its DOM tree due to a reactive state change.
    2. updated() : Called after the component has updated its DOM tree due to a reactive state change.
  4. Unmounting
    1. beforeUnmount() : Called right before a component instance is to be unmounted.
    2. unmounted() : Called after the component has been unmounted.
  5. Miscellaneous
    • <KeepAlive> (caches dynamically toggled components wrapped inside)
      1. activated() : Called after the component instance is inserted into the DOM.
      2. deactivated() : Called after the component instance is removed from the DOM.
    1. errorCaptured() : Called when an error propagating from a descendent component has been captured.
    2. renderTracked() : Called when a reactive dependency has been tracked by the component's render effect.
    3. renderTriggered() : Called when a reactive dependency triggers the component's render effect to be re-run.

When advanced conditions requires more then a simple inline ternary operator we create a function within either the computed: {} or methods: {} properties or other life cycle hooks.

computed:

For complex logic that includes reactive data, it is recommended to use a computed: {} property. Those properties are cached based on their reactive dependencies. This means a computed property will only re-evaluate when some of its reactive dependencies have changed.

Computed properties are by default getter-only. If you attempt to assign a new value to a computed property, you will receive a runtime warning. In the rare cases where you need a "writable" computed property, you can create one by providing both a getter (get()) and a setter (set()) within your computed function.

methods:

A Vue method is an object associated with the Vue instance. Functions are defined inside the methods object. Methods are useful when you need to perform some action with v-on directive on an element to handle events. Functions defined inside the methods object can be further called for performing actions.

watch:

With Options API, replaced by Composition API since Vue v.3, we can use the watch option to trigger a function whenever a reactive property changes.

«Watchers, a generic way to react to data changes»

See basic examples of computed: and watch: properties.

 

Dealing with Vue Components

« Components allow us to split the UI into independent and reusable pieces, and think about each piece in isolation. »
Vue Essentials Guide

Requirements

  1. camelCase : Within <script> parts of a file,
  2. kebab-case : Within <template> and <style> parts of a file,
  3. PascalCase : FileName.vue.

HTML attribute names are NOT case-sensitive, so browsers will interpret any uppercase characters as lowercase. That means when you’re coding within <template> (and <style>), camelCased nomenclature for prop names need to use their kebab-cased (hyphen-delimited) equivalents.

calmelCase -VS- PascalCase typographie : The later starts with a capital letter.

 

Vue templating

Attribute Bindings

v-bind : Binding dynamic parameters as attribute to an HTML tag or Vue component.

Syntaxe :

  • Default : v-bind:*
  • Shorthand : :* or . (when using .prop modifier)

Any HTML attributes expected to be interpreted by Vue.js requires the binding shorthand colon as prefix (:*) otherwise you get either an error or Vue will simply not read it.

// Vue template
<a class="static-class" :class="dynamic-class" :title="$t('example.actionTitle')">

// Rendered in DOM
<a class="static-class dynamic-class" title="Click this link example">

Template containers : <template>

At the first level, template tags are DOM containers. The first level root <template> is required to interfere within the DOM of an HTML file. Where child <template> tags are usually slot containers.

But sub <template> tags are also used to inject content within slots <template v-slot:>.

Load on DOM ready

v-cloak attribute added to the app container for prevents the rendering of the DOM until all Vue {{…}} is processed in the browser. So the users don’t experience a jump effect in the page.

<template>
  <div id="app" v-cloak></div>
</template><style>
[v-cloak] {
  display: none;
}
</style>

Slots : <slot> tag or v-slot:

Slots are perfect container of repetitive HTML elements that you distribute in different templates. We can see then as kind-of static components all do the y are not static, simply less flexible the component's <template>.

<template>
  <fragment><template v-slot:selection="{ item }">
      <span class="time-selector-option">{{ item.title }} </span>
      <span class="time-selector-date ml-1">{{ timeSelectorOptionSuffix(item.queryFor) }}</span>
    </template>

Fragment tags : <fragment>

Fragments are basically a root-less components, invisible wrapper tags that do not affect the node structure of the DOM, thereby achieving accessibility. They come useful in many situations where you don't want to pollute the DOM with an other useless <div> container, or you want to return many elements at once.

Conditional rendering markup

Interpolation

Binding the rendered DOM to the underlying Vue instance’s data using these boolean options:

  1. {{ rawHtml }} Double curly brackets, or as I call them, are the default syntaxe to render as raw text within DOM.
  2. v-html="rawHtml" on any HTML tags renders the same as double curly brackets (mustache) {{ }}
  3. v-bind directive as attributes for HTML tags.
    • In case of boolean condition, use disable attribute on <span v-bind:disabled> to render a negative context (null, undefined, or false)
  4. We can use JavaScript Expressions within data bindings.
    {{ key ? true : false }}
    {{ key ? 'Yes' : 'No' }}
    <div v-bind:id="'list-' + id" />

Real life interpolation examples of conditional class declarations within the DOM with keys of different natures :

data

<li class="state-related-item" :class="hardCodedData" />
<li class="state-related-item" :class="hardCodedData ? 'classname--true' : 'classname--false'" />

prop

<li class="state-related-item" :class="editableStringProp" />
<li class="state-related-item" :class="{ 'boolean-prop-classname': booleanProp }" />
<li class="state-related-item" :class="[editableStringProp, { 'first-boolean-prop-classname': firstBooleanProp }, { 'second-boolean-prop-classname': secondBooleanProp}]" />
<li class="state-related-item" :class="{ [editableStringProp + '--text']: editableStringProp }" />

If, else and show

v-if, v-else and v-else-if are available on elements as we could expect it.

But we also have the simpler v-show that always renders the content in the DOM then manage toggle its visibility with CSS display property.

For loop

v-for renders the element or template block multiple times based on the source data.

Importing Vue file within Vue file

ImportThisComponentFile.vue = <import-this-component-file />
Remember that VueJS components requires PascalCase nomenclature while within the <template> those same components are translated to snake-case nomenclature:.

ChildComponent.vue

<template>
  <li>Child component item to be included</li>
</template>
<script>
export default {
  name: "ChildComponent",
};
</script>

ParentComponent.vue

<template>
  <h1>Parent component container</h1>
  <ol>

    <child-component />
    <child-component />

  </ol>
</template>
<script>
import ChildComponent from "@/components/ChildComponent.vue";

export default {
  name: "ParentComponent",
  components: {
    ChildComponent,
  },
};
</script>

$emit, Emitted event flow

The logic of using $emit is when you want to pass data from the child to its parent against a normal «prop flow», where info usually pass from parent to child.


  computed: {
    
    show: {
      set(value) {
        this.$emit("input", value);
      },
    },
  

Unlike components and props, (emitted) event names don’t provide any automatic case transformation. Instead, the name of an emitted event must exactly match the name used to listen to that event. For example, if emitting a camelCased event name:

 

Styling templates

CSS

All stylesheets are declared within a <style> tag, usually the last of the 3 root tags.

Scoped CSS

When a <style> tag has the optional scoped attribute, its CSS will apply to elements of the current component only. This is similar to the style encapsulation found in Shadow DOM.

 

Tips & Tricks

Using console.log within Vue

Get console.log on @click event

<template>
  <v-btn color="info" x-large @click="atClick_consoleLog">
    atClick_consoleLog
  </v-btn>
</template>
<script>
export default {
  methods: {
    atClick_consoleLog() {
      console.log(this.$vuetify.application.top);
    },
  },
};
</script>

To validate form fields

<template>
  <fragment>
    <v-checkbox
      v-model="inspectCheckedValues"
      value="firstCheckBoxValue"
    ></v-checkbox>
    <v-checkbox
      v-model="inspectCheckedValues"
      value="secondCheckBoxValue"
    ></v-checkbox>
  </fragment>
</template>
<script>
export default {
  
  data() {
    return {
      inspectCheckedValues: [],
    };
  },
  watch: {
    inspectCheckedValues() {
      console.log(this.inspectCheckedValues);
    },
  },
};
</script>

 

Conditional slots

Use a conditional statement that if true will dynamically load CSS classes on the parent container as well as the slot itself:

ChildComponentFile.vue : Where the slot is declared

<template>
  <section v-if="setDisplayOptionalSection()" :class="optionalSectionClass" class="child-slot-container">
    <slot name="optional-child-container"></slot>
  </section>
</template>
<script>
export default {
  name: "ChildComponentName",
  props: {
    displayOptionalSection: {
      type: Boolean,
      default: () => false,
    },
    optionalSectionClass: {
      type: String,
      default: () => "",
    },
  },
  methods: {
    setDisplayOptionalSection() {
      return this.displayOptionalSection;
    },
  },
};
</script>

ParentComponentFile.vue : Where the slot is used

<template>
  <article>
    <child-component-file
      :display-optional-section="true"
      optional-section-class="d-flex align-center"
    >
      <template v-slot:optional-child-container></template>
    </child-component-file>
  </article>
</template>
<script>
import ChildComponentFile from "@/components/ChildComponentFile";

export default {
  name: "ParentComponentFile",
};
</script>
<style lang="scss" scoped>

// this class is hard coded in ChildComponentFile.vue
::v-deep .child-slot-container {
  width: 50%;
}

</style>

 

i18n multilingual dynamic strings

Locale culture lang strings are stored within jSon files

  • ./src/locales/en.json
  • ./src/locales/fr.json

Supported string syntaxes

vue context
{{ string }} Mustache templating syntaxe (default in Vue)
<b>{{ $t("string") }}</b> Vue template
${ string }
`backtick-concatenated-${ string }` Wrapping within backticks (instead of single quotes) allow concatenating static code with a dynamic string.
{{ this.computedString }}
{{ this.conditionalString }}
:label="stringViaProp" Prop type: String

Code exmples

<template>
  <fragment>

  <p>{{ $t("vueFile.htmlContext.textStringID") }}</p>
  <abbr :title="$t('common.requiredField')">*</abbr>
  <p>{{ this.computedString }}</p>
  <p>{{ this.conditionalString }}</p>
  <chart-component :options="chartOptions" />

  </fragment>
</template>
<script>
import ChartComponent from "@/components/ChartComponent";
import i18n from "@/i18n";

export default {
  name: "vueFileExample",
  data() {
    return {
      chartOptions: {
        footer: function dataRow(dataRowItem, data) {
          
          const concatenatedString = `${i18n.t( "computedString", )} : ${liveValue} %`;
          
          title: function getString() {
            return i18n.t("computedString");
          },
          
          return dataRowItem.yLabel.toFixed(1) + concatenatedString;
        }
      }
    }
  }
  computed: {
    computedString() {
      return this.$t("vueFileExample.htmlContext.computedStringID");
    },
    conditionalString() {
      return this.value !== null ? $t("vueFileExample.htmlContext.conditionalString.ifTrue") : $t("vueFileExample.htmlContext.conditionalString.ifNull");
    }
  },
};
</script>

Dealing with {{ mustache.key }}

a11y translated strings within ternary operators

Passing down the necessary a11y options through the slots scope allow us to print mustache key {{…}} within ternary operator.

Integrating multilingual stings

As data ...TODO

 

Vue plugins

isMobile

Vue.js prototype function this.$isMobile() that returns a boolean (true/false) value depending on whether or not the user is browsing with a mobile phone.

This function is imbedded within Vuetify «$vuetify.breakpoint.mobile»

 

References

Forums

Official documentation

Migrating from v2 to v3 guide

Tutorials

 

 

⚠️ **GitHub.com Fallback** ⚠️