Understanding hooks in Sequelize (Returning vs. Attaching) - express

I am following my school's workshop regarding how to integrate Sequelize with Express. There is a section where we are learning to leverage hooks in our models—and in it I was confused by this:
Returning vs. Attaching
A hook runs with the instance of a Page being
saved given as an argument. We want to, therefore, attach a created
urlTitle to this page instance instead of returning it from the
function.
var Sequelize = require('sequelize');
var db = new Sequelize('postgres://localhost:5432/__wikistack__', {
logging: false,
});
const Page = db.define(
'page',
{
title: {
type: Sequelize.STRING,
},
urlTitle: {
type: Sequelize.STRING,
},
content: {
type: Sequelize.TEXT,
},
status: {
type: Sequelize.ENUM('open', 'closed'),
},
},
{
hooks: {
beforeValidate: function(page) {
if (page.title) {
// Removes all non-alphanumeric characters from title
// And make whitespace underscore
return (page.urlTitle = page.title.replace(/\s/g, '_').replace(/\W/g, ''));
} else {
// Generates random 5 letter string
return (urlTitle = Math.random()
.toString(36)
.substring(2, 7));
}
},
},
}
);
Can someone explain this? How can the function in the hook not return something? The above works, so the hook/function is returning something.
Thanks in advance!

Hooks are just code that gets run at certain life cycle points of a record instance. You can have them be purely side effects. In your case, all you need to do is modify the page object that the hook is passed, return doesn't help or hurt.
However, the return value of a hook is not useless. If you need to do anything async inside a hook, you have to return a promise.

Related

Use Vue instance inside data() function

First I know its not best practice and not recommended at all, but there are really some rare cases when it might be useful. As an example I am using an external js library to display JSON content and seems the component accepts an options attribute. In this property there are couple of callback function I can use to validate the JSON content.
Here is the implementation:
<v-jsoneditor ref="editor"
v-bind:plus="true"
v-bind:options="options"
height="500px"
v-model="value"
v-on:error="onError">
</v-jsoneditor>
Below is the data function.
data() {
return {
value: "",
options: {
mode: 'code',
onValidate(value) { //this is the function I am talking about
if (Vue.isRequired) {//need the Vue instance here because I can not say this.isRequered
console.log("required");
}
console.log(value);
return null;
}
}
}
}
I know I can have a create method like below and use closure on the vue instance:
async created() {
let vue = this;
this.options.onValidate = function (value) {
if (vue.isRequired) {
console.log("required");
}
console.log(value);
return null;
}
await this.loadRules();
}
but was hoping there is a better way to do it, because create method will look very convoluted if I keep adding more and more callback function like this one.
Is there any better way to access current Vue instance in the data() function ?
The lib I am using is this one.
Seems it much easier than I thought, completely forgot about changing data() to use array function notation like below:
data: (vue) => ({
value: "",
state: null,
errorMessage: "",
showRulesModal: false,
rules: [],
options: {
mode: 'code',
onValidate(json) {
let errors = [];
if (Object.keys(json).length === 0 && vue.isRequired) {
vue.$emit("on-value-validation-failed");
errors.push({
path: [''],
message: 'Value is required to continue'
})
return errors;
}
return null;
}
}
}),
Thanks #Antoly for pointing me to the right direction :)

How to react once to a change in two or more watched properties in vuejs

There are many use cases for this but the one I'm dealing with is as follows. I have a page with a table, and two data properties, "page" and "filters". When either of these two variables are updated I need to fetch results from the server again.
However, there is no way as far as I can see to watch two variables and react only once, especially in the complicated instance updating filters should reset page to zero.
javascript
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
page (nv) {
this.fetchAPI()
},
filters: {
deep: true,
handler (nv) {
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
}
}
If i update filters, its going to reset page to 0 and call fetchAPI() twice. However this seems like the most intuitive way to have a page with a table in it? filters should reset page to zero as you may be on page 500 and then your filters cause there to only be 1 page worth of results, and a change to either page or filters must call the api again.
Interested to see how others must be tackling this exact same problem reactively?
Take into the rule - watchers are the "last hope". You must not use them until you have other ways.
In your case, you could use events. This way the problem will go by itself:
Add #click="onPageChange" event to the page button (or whatever do you use).
Add #change="onFilterChange" event to the filter component (or whatever do you use). You can also use #click="onFilterChange" with some additional code to detect changes. Still, I am pretty sure you must have something like #change on the filter component.
Then your code will look like:
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
methods: {
onPageChange () {
this.fetchAPI()
},
onFilterChange () {
this.page = 0
this.fetchAPI()
},
fetchAPI () {
// fetch data via axios here
}
}
In this case, the onFilterChange will change the page data but will not trigger the onPageChange method. So your problem will not exist.
Since the filters object is really complicated and has many options i have decided to keep it on a watcher that triggers setting page to zero and reloading the api. I now solve the problem as stated below. The 'pauseWatcher' data variable is a bit messy and needing to disable it in nextTick seems unideal but its a small price to pay for not having to manually hook up each and every filters input (some of them are far more complex than one input, like a date filter between two dates) and have them each emit onChange events that trigger reloading the api. It seems sad Vuejs doesnt have a lifecycle hook where you can access data properties and perhaps route paramters and make final changes to them before the watchers and computed properties turn on.
data: {
return {
pauseWatcher: true,
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
filters: {
deep: true,
handler (nv) {
if (this.pauseWatcher) {
return
}
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
},
goToPage(page) {
this.page = page
this.fetchAPI()
},
decodeFilters () {
// decode filters from base64 URL string
}
},
created () {
this.pauseWatcher = true
this.page = Number(this.$route.query.page) || 0
this.filters = this.decodeFilters(this.$route.query.filters)
this.$nextTick(() => {
this.pauseWatcher = false
})
}

Prevent graphql query in vue-apollo if input variable is null

I've been doing some reading and have come up with a query setup for a contact input field. I would like to avoid running this query at component startup with null input. I could manually run the queries through computed methods maybe, but is there a simple way to prevent this?
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
debounce: 300,
update(data) {
console.log("received data: " + JSON.stringify(data));
},
result({ data, loading, networkStatus }) {
console.log("We got some result!")
},
error(error) {
console.error('We\'ve got an error!', error)
},
prefetch() {
console.log("contact search, in prefetch");
if ( this.searchText == null ) return false;
return true;
},
},
},
I think I'm not understanding something about prefetch, or if it's even applicable here?
You should utilize the skip option for that, as shown in the docs:
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
skip() {
return !this.searchText;
},
...
},
},
Anytime searchText updates, skip will reevaluate -- if it evaluates to false, the query will be ran. You can also set the skip property directly if you need to control this logic elsewhere in your component:
this.$apollo.queries.search.skip = true
The prefetch option is specific to SSR. By default, vue-apollo will prefetch all queries in server-side rendered components. Setting prefetch to false disables this functionality for a specific query, which means that particular query won't run until the component is rendered on the client. It does not mean the query is skipped. See here for more details about SSR in vue-apollo.
Apollo Client
const { loading, error, data } = useQuery(GET_SOME_DATA_QUERY, {
variables: { param1 },
skip: param1 === '',
})
This query will be fired only when param1 is not empty.

How to detect the user comes back to a page rather than starts browsing it?

I've got a grid component that I use in many routes in my app. I'd like to persist its state (ie. paging, search param) and restore it when the user comes back to the grid (ie. from editing a row). On the other hand, when the user starts a new flow (ie. by clicking a link) then the page is set to zero and web service is called with the default param.
How can I recognise the user does come back rather then starts a new flow?
When I was researching the problem I've come across the following solutions.
Unfortunatelly they didn't serve me
1/ using router scroll behaviour
scrollBehavior(to, from, savedPosition) {
to.meta.comeBack = savedPosition !== null;
}
It does tell me if the user comes back. Unfortunately the scroll behaviour runs after grid's created and mounted hooks are called. This way I have no place to put my code to restore the state.
2/ using url param
The grid's route would have an optional param. When the param is null then the code would know it's a new flow and set a new one using $router.replace routine. Then the user would go to editing, come back and the code would know they come back because the route param != null. The problem is that calling $router.replace re-creates the component (ie. calling hooks etc.). Additionally the optional param mixes up and confuses vue-router with other optional params in the route.
HISTORY COMPONENT
// component ...
// can error and only serves the purpose of an idea
data() {
return {
history: []
}
},
watch: {
fullRoute: function(){
this.history.push(this.fullRoute);
this.$emit('visited', this.visited);
}
},
computed: {
fullRoute: function(){
return this.$route.fullPath
},
visited: function() {
return this.history.slice(-1).includes(this.fullRoute)
}
}
the data way
save the information in the browser
// component ...
// can error and only serves the purpose of an idea
computed: {
gridData: {
get: function() {
return JSON.parse(local.storage.gridData)
},
set: function(dataObj){
local.storage.gridData = JSON.stringify(dataObj)
}
}
}
//...
use statemanagement
// component ...
// can error and only serves the purpose of an idea
computed: {
gridData: {
get: function() {
return this.$store.state.gridData || {}
},
set: function(dataObj){
this.$store.dispatch("saveGrid", gridData)
}
}
}
//...
use globals
// component ...
// can error and only serves the purpose of an idea
computed: {
gridData: {
get: function() {
return window.gridData || {}
},
set: function(dataObj){
window.gridData = dataObj
}
}
}

Prop not Reactive using VUE to send configuration

I have started learning Vue and came quite far :) but still consider myself beginner. I have bought a vue based single page application template
Themeforest Template Link
I am building my application over it and it uses FlatPickr
I am trying to use FlatPickr's enable functionality
{
enable: ["2025-03-30", "2025-05-21", "2025-06-08", new Date(2025, 8, 9) ]
}
I asked the developer how to use this as it was not in instructions so he replied:
you can use the config prop to pass all Flatpickr options.
I have codes which looks like this
<c-flatpickr
v-validate="'required'"
v-model="dateRange"
:config="enabledDates"
name="Select Date Range"
value="" range
/>
enabledDates: function (){
var data = this.trip.tripsDetails;
var options = {
enable: [],
};
for (let i in data){
options.enable.push(data[i].startingDate);
}
return options;
},
my problem is if I pass the data statically it works so if I put values in array manually like
var options = {
enable: ['01-06-2018', '03-06-2018', '06-06-2018'],
};
it works but if I try to push values dynamically (as per my codes above) it doesn't work.
What am I doing wrong?
Thanks for your help in advance.
// Here are the codes
data() {
return {
trip: new Form({
//All Trips
tripsDetails: [],
}),
dateRange: undefined,
}
},
methods: {
getData(pageid){
this.trip.tripsDetails = [];
axios
.get('/trip/getTrips',{
params: {
equipment_id : this.trip.equipment_id,
dispatch_id: this.pageid
}
})
.then(response => {
this.trip.tripsDetails = response.data.data;
this.loading();
}).catch(error => {
this.loading();
this.errortoast();
});
},