solution • VueJS coding - martindubenet/wed-dev-design GitHub Wiki
[ Vue.js coding ][ Vuetify templates ][ Vue.js setup for Vuetify ][ Vue headless CMS site ]
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
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>
https://v2.vuejs.org/v2/cookbook/debugging-in-vscode.html
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
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 thename
value, that can be useful within the<template>
.
data() {
return {
stringExample: "Lorem ipsum",
arrayExample: ["primary", "secondary", "neutral"],
},
},
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.
- Array
- Boolean
- Date
- Function
- Number
- Object
- String
- Symbol.
Add a new dynamic prop
- Make sure that you can NOT match the required result using existing Vue or Vuetify Props
- Within
<script>
, under thename: "",
if not already existing, declareprops: {},
- Within
props: {}
, add an array named according to the chamelCase nomenclature of yourmyNewProp: {},
. - Declare the
type
(pick one from the list above), - For validation set your prop
required: true,
orrequired: false,
- Set either a
default: "value"
(orfalse
for an opt-in), or declare your proprequired: true
to set validation. - Make sure every line ends with a coma «
,
» character, - 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>
-
Creation
-
beforeCreated()
: Called when the instance is initialized. -
created()
: Called after the instance has finished processing all state-related options.
-
-
Mounting
-
beforeMount()
: Called right before the component is to be mounted. -
mounted()
: Called after the component has been mounted. Could be seen as the javascriptonload
event.
-
-
Updating
-
beforeUpdate()
: Called right before the component is about to update its DOM tree due to a reactive state change. -
updated()
: Called after the component has updated its DOM tree due to a reactive state change.
-
-
Unmounting
-
beforeUnmount()
: Called right before a component instance is to be unmounted. -
unmounted()
: Called after the component has been unmounted.
-
-
Miscellaneous
-
<KeepAlive>
(caches dynamically toggled components wrapped inside)-
activated()
: Called after the component instance is inserted into the DOM. -
deactivated()
: Called after the component instance is removed from the DOM.
-
-
errorCaptured()
: Called when an error propagating from a descendent component has been captured. -
renderTracked()
: Called when a reactive dependency has been tracked by the component's render effect. -
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.
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.
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.
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.
« Components allow us to split the UI into independent and reusable pieces, and think about each piece in isolation. »
— Vue Essentials Guide
-
camelCase
: Within<script>
parts of a file, -
kebab-case
: Within<template>
and<style>
parts of a file, -
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 theirkebab-cased
(hyphen-delimited) equivalents.
calmelCase
-VS-PascalCase
typographie : The later starts with a capital letter.
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">
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:>
.
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 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>
…
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.
Binding the rendered DOM to the underlying Vue instance’s data using these boolean options:
-
{{ rawHtml }}
Double curly brackets, or as I call them, are the default syntaxe to render as raw text within DOM. -
v-html="rawHtml"
on any HTML tags renders the same as double curly brackets (mustache){{ }}
-
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
, orfalse
)
- In case of boolean condition, use
- 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 }" />
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.
v-for
renders the element or template block multiple times based on the source data.
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>
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:
All stylesheets are declared within a <style>
tag, usually the last of the 3 root tags.
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.
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>
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>
Locale culture lang strings are stored within jSon files
./src/locales/en.json
./src/locales/fr.json
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>
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.
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
»
npm i vue-mobile-detection
- Or
npm i [email protected]
for Vue v.2
-
Guides :
- v.3 Guide : https://v3.vuejs.org/guide/
-
v.2 Guide : https://vuejs.org/v2/guide/
- Vue lifecycle diagram,
- Mustache
{{ * }}
andv-*
text interpolations for templating, -
v-on:click
,v-on:submit
and other event modifiers likeevent.preventDefault()
, -
computed:
and basicwatch:
properties, -
watch
ers, a generic way to react to data changes,
- Style guide
- Cookbook
- Examples : Mardown editor / Grid component / Tree view, SVG graph
- How to create a dynamic QR code generator in Vue — blog.logrocket.com