12 KiB
To Do:
Organise Trilium with the Vue development
- Have a note for tutorials
- Have a note for references (grouped by category)
Once done with this, go through this and the auth0 tutorial and pick out any vue/javascript snippets into their own trilium note.
Notes
Vuetify
Forms https://vuetifyjs.com/en/components/forms
To make a form you define a v-model of valid in the <v-form>. lazy-validation will set this to true unless there are visible validation errors.
<v-form v-model="valid" ref="form" lazy-validation="">
The v-form component has three functions that can be access by setting a ref on the component.
- this.$refs.form.validate()
- this.$refs.form.reset()
- this.$refs.form.resetValidation()
Validation is easy: https://vuetifyjs.com/en/components/forms/#usage
Custom rules can be done: https://vuetifyjs.com/en/components/forms/#creating-rules
When the submit() event is made, this.$refs.form.validate() will check the form.
v-model is used to bind data 2 ways to the form and the data instance in vue.
https://vuejs.org/v2/guide/forms.html
v-text-fields are used to render inputs in a form.
https://vuetifyjs.com/en/components/text-fields/
Using : before a prop means that the prop is dynamic and the value of it contains javascript.
:append-icon="passwordVisible ? 'visibility' : 'visibility_off'"
Here this is saying that append-icon takes a dynamic value. We are using the ? : syntax - conditional ternary operator. This is short for a if then else.
Available icons are (with mdi- prefix) https://materialdesignicons.com/ or https://material.io/resources/icons/?style=baseline
Double bang notation
The notation !!variable is double bang notation
https://medium.com/better-programming/javascript-bang-bang-i-shot-you-down-use-of-double-bangs-in-javascript-7c9d94446054
It returns the variable truthy value. I.e if it's a string with a value it will be true. This is similar to doing variable.__bool__ in python.
When writing inputs you can create custom rules.
For example: (in the computed)
passwordRules() {
return {
required: value => !!value || 'Required.',
min: v => v.length >= 8 || 'Min 8 characters',
emailMatch: () => ('The email and password you entered don\'t match'),
}
},
For a <v-text-field:
<v-text-field
v-model="password"
:append-icon="passwordVisible ? 'visibility' : 'visibility_off'"
:rules="[passwordRules.required, passwordRules.min]"
>
</v-text-field>
The value of the field will be passed to the method passwordRules() and passed into the function inside it. This way, we can verify that the value exists (using double bang notation) and that it has a minimum length.
Events on the input can be defined using the following syntax:
@click:append="passwordVisible = !passwordVisible"
This event (each event is documented in the Vuetify docs) will trigger when you click on the append icon you defined.
You can write regex in javascript by placing it between two slash's.
They are useful with arrow notation to check if something matches a regex.
v => /.+@.+/.test(v) || 'E-mail must be valid.'
Backtick strings is javascript's version of f-strings in python. You can then access variables with ${} notation:
console.log(
`SIGN UP username: ${this.username}, password: ${this.password}, email: ${this.username}`
)
You can write an if block like you would a function:
if (this.$refs.form.validate()) {
}
You can also follow this with an else (aligned with the end of the if block)
if (condition) {
// block of code to be executed if the condition is true
} else {
// block of code to be executed if the condition is false
}
You can also use the conditional ternary notation of
condition ? true_action : false_action
Computed (properties and watchers) https://stackoverflow.com/a/44350932
With computed you are creating getters/setters. You do this by writing the function in the computed directive.
Here we have written a computed property for emailRules() - we are using this function to check input on a form is correct.
Computed properties don't take any arguments - but you can access the data scope through $this. if you need access to the data in the scope.
They are better than using watchers/methods if you don't need the extra functionality. A good example is https://michaelnthiessen.com/most-important-feature-vue/
The main difference between computed and a method is that computed will cache automatically. It will only update, if the value updates. Everytime you call it it will use its cache. Expensive operations can be done this way.
They are properties (like in Python). Computed properties will update whenever one of the values needed to make it is also updated. Whenever you want to filter or transform your data, you can use a Computed property.
E.g if you have a list of names, and you want just those names that begin with 'b', you can write it as a computed property that returns this list. The stack overflow answer above demonstrates this.
Remember: anything in data you can access in the <script> with $this.var. In the html you can access the var name directly {{var}}
Watchers https://vuejs.org/v2/guide/computed.html#Watchers
Watchers are like computed but they are a more generic way to watch for datachanges. If you need to add more functionality (rate limiting) or refer to many methods or take many arguments - a watcher might be more appropiate.
The watch directive can be used to watch variables in the component. You create a function that is the same name as the variable you want to watch. This function should take two arguments: new and old. (You can use this to compare inputs for exmaple).
There is a good example of using this functionality to do an ajax call (with rate limiting) in the documentation for watchers above.
Sending form data to an api
You can check the form is in a valid state by doing this.$refs.form.validate()
You can then access each component of the form to the variable you defined in the v-model attribute.
<v-text-field v-model="username" /> can be accessed with this.username.
Remember that the variable defined in the v-model has to be defined in the data.
Usage of $this
The following (under the "No separate this" header) explains well. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
When you write a function and define a variable, you might want to write another function inside. If you use $this inside the second function, you will find it is bound to the global scope, not the parent function!
To fix this behaviour, you should close over the variable $this:
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// The callback refers to the `that` variable of which
// the value is the expected object.
that.age++;
}, 1000);
}
This is similar to "closure" in python.
Events system and having functions as props https://michaelnthiessen.com/pass-function-as-prop/ https://vuejs.org/v2/guide/events.html
<!-- Parent -->
<template>
<ChildComponent @send-message="handleSendMessage" />
</template>
// Parent
export default {
methods: {
handleSendMessage(event, value) {
// Our event handler gets the event, as well as any
// arguments the child passes to the event
console.log('From the child:', value);
}
}
}
And in the child component we emit the event:
// Child
export default {
props: {
method: { type: Function },
},
data() {
return { value: 'I am the child.' };
},
mounted() {
// Instead of calling the method we emit an event
this.$emit('send-message', this.value);
}
}
When the child component is mounted, it emits the event 'send-message'. The parent can then listen for this 'send-message' event and call a corresponding function. As the data flows in only one way, it makes for much easier debugging too. We can also pass up variables from the child to the parent in this way.
Object destructuring
When you define a function and specifiy attributes, you can use javascripts object destructing.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
For example, if you write a function that takes a dictionary, but in the function you only need one of the key-value pairs and not the rest, you can write your function arguments to take this using {} inside the arguments. Then when you pass in the whole dictionary as an argument to the function, it will only use what you've told it to.
Listening to events
In a parent, you can use the @ syntax on a html tag to listen to an event.
<ChildComponent @send-message="handleSendMessage" />
This will listen for a send-message event from this child, and when it receives it it will run the function handleSendMessage.
mounted() {
this.$emit('send-message', this.value);
}
This will trigger the handler in the parent, and pass the this.value parameter up with it.
Slots https://vuejs.org/v2/guide/components-slots.html
Slots are useful if you want to send data from a parent down to a child (or vice versa) and want to include more advanced things.
For example, you can use a slot to send html from a parent down to a child to be rendered.
// parent
<navigation-link url="/profile">
<!-- Add a Font Awesome icon -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
// child (navigation-link)
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
The content between the <slot> will be replaced by what is between the <navigation-link>.
Summary:
Send data
From parent to child: use props. From child to parent: use events.
Send data parent to child: If you want to send data from a parent to a child, but the data is defined on the child but you want to send a reference to it from the parent then have the parent send an arrow function as a prop, which returns the statement/expression you want to be rednered in the child.
As its a function, you can have it accept an argument. In the child you would refer to the prop name and pass in the arguments.
In the child:
{ prop-name(argument)}
Scoped Slots https://www.youtube.com/watch?v=GWdOucfAzTo
If you want to send more than a string down, and want to send more advanced things like whole functions or even html, then you can use a scoped slot.
A scoped slot is a special kind of slot designed for this purpose. They are really powerful in Vue, and using them in design patterns allows you to decouple components.
To use a scoped slot you define a normal slot, but you pass add attributes to the slot tag in the html.
<slot :attr1="param"></slot>
You are passing in a key-value pair. This is slightly different to when you use arrow functions with arguments to render the data (unless your arrow function takes an object of a dictionary with the same key-value pairs).
So if we need to send data from a child to a parent:
In the child: write a <scope :something="a value"> that takes arguments.
In the parent: write a <template> directive that has a slot-scope attribute on it: <template slot-scope="name"> {{name.something}} </template>.
You don't have to use a template, you can use any html tag - you just need some html tag to accept the slot-scope - (an <a> for example). You can fall back on template if needed as a default.
Think of a scoped slot as a function that returns template data. Regular slots are just regular props. Scoped slots are function props.
The video from adam linked above has a really good demonstration of this.
Portals https://www.youtube.com/watch?v=1yWAxrpL3zU&list=PL7CcGwsqRpSOZAiNYyVvgTKSyARERvvij
Portals are useful if you want to render something in a child somewhere in a specific place in the DOM.
This is useful if you have a child component somewhere that is toggling a modal, and you want that model to actually be placed in the parent in a specific place in the DOM for formatting.
Portals will literally render the data where it needs to, then transport it to wherever you want it to actually be placed in the HTML.
Advanced Vue Component Design course https://adamwathan.me/advanced-vue-component-design/