Vue 3 Understanding examples from setup function and applying them in Setup tag - vue.js

I am new to vue but scince i am fresh starting i decied to go straight up to TS and Setup tag because seems like the newest and best way to write vue js components.
Anyways i am now looking at this framework and more specific i'm into this example:
import { IonPage, onIonViewWillEnter, onIonViewDidEnter, onIonViewWillLeave, onIonViewDidLeave } from '#ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Home',
components: {
IonPage,
},
setup() {
onIonViewDidEnter(() => {
console.log('Home page did enter');
});
... the other hooks ...
},
});
And my question come from this block:
name: 'Home',
components: {
IonPage,
},
since i only worked with options api and setup tag i an not sure What does it imply to put an object in the components object and how could i acomplish the same objective with setup tag.
My objective is to make sure i am doing what this Note in the guide warns me about:
Note Pages in your app need to be using the IonPage component in order
for lifecycle methods and hooks to fire properly.

In <script setup> syntax, Home.vue would look like this:
<script setup lang="ts">
import { IonPage, onIonViewDidEnter, ...other hooks used here... } from '#ionic/vue';
onIonViewDidEnter(() => {
console.log('Home page did enter');
});
...other hooks used here...
</script>
The note you quoted draws attention to the template of any page/view contents needing to be wrapped in a <ion-page></ion-page> wrapper for the layout to function as intended, like in their examples.
Generic example:
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Some title</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
some content...
</ion-content>
</ion-page>
</template>
For the above layout, you'd need to import all used components:
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '#ionic/vue'
With <script setup> you don't need to declare them as local components, <script setup> detects them and does it for you, behind the scenes. They're available for usage inside the <template> once imported.
The component name is also inferred by <script setup> from the name of the file. In the above case, it would be Home.
To sum up: behind the scenes, <script setup> takes its contents and wraps it in a
export default defineComponent({
name: // infer from file name,
components: {
// list all imported vue components
},
setup() {
// actual contents of `<script setup>`
}
})
For this to be possible, some helpers were added (defineEmits, defineProps), which allow declaring those parts of Options API inside the setup() function.
Most notably, in this syntax setup() no longer needs a return value. All variables declared or imported inside it are made available to <template>.
Important: <script setup> is a useful tool, designed to reduce boilerplate in the majority of cases, not to replace defineComponent() completely. Read more about it in the docs.

Related

#vuepic/vue-datepicker does not work with vite build [duplicate]

I have the following View in Vue:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>
<script>
export default {
data() {
return {
text: ""
};
},
components: { Overwrite: Overwrite },
};
</script>
Everything works perfectly fine when I start the application with npm run dev.
However, when I build the app for production and run it, I get the following error as soon as I type anything into the textarea:
index.57b77955.js:3 Uncaught ReferenceError: text is not defined
at HTMLTextAreaElement.t.onUpdate:modelValue.s.<computed>.s.<computed> [as _assign] (index.57b77955.js:3:1772)
at HTMLTextAreaElement.<anonymous> (vendor.31761432.js:1:53163)
I also have other form elements that show the exact same behaviour.
You can use a maximum of 1 × <script> tag and a maximum of 1 × <script setup> per vue component.
Their outputs will be merged and the object resulting from merging their implicit or explicit exports is available in <template>.
But they are not connected. Which means: do not expect any of the two script tags to have visibility over the other one's imports.
The worst part is that, although the first <script setup> does declare Ovewrite when you import it (so it should become usable in <template>), the second one overwrites it when you use components: { Overwrite: Overwrite }, because Overwrite is not defined in the second script. So your components declaration is equivalent to:
components: { Overwrite: undefined }
, which overwrites the value already declared by <script setup>.
This gives you two possible solutions:
Solution A:
<script>
import Overwrite from "../components/Overwrite.vue";
export default {
components: {
Overwrite
},
// you don't need `data` (which is Options API). use `setup` instead
setup: () => ({
text: ref('')
})
}
</script>
Solution B:
<script setup>
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
Or even:
<script setup>
import Overwrite from "../components/Overwrite.vue";
</script>
<script>
export default {
data: () => ({ text: "" })
};
</script>
Can you try using only the setup script tag? Using it only for imports this way doesn't make sense. If you import a component in setup script tags you don't need to set components maybe the issue is related to that.
Also you could try the full setup way then:
<script setup>
import { ref } from 'vue'
import Overwrite from "../components/Overwrite.vue";
const text = ref('')
</script>
<template>
<div>
...
<textarea v-model="text" cols="99" rows="20"></textarea>
...
</div>
</template>

What's the most idomatic Vue way of handling this higher-order component?

I have a VueJS organization and architecture question. I have a bunch of pages that serve as CRUD pages for individual objects in my system. They share a lot of code . I've abstracted this away in a shared component but I don't love what I did and I'm looking for advice on the idiomatic Vue way to do this.
I'm trying to create a higher order component that accepts two arguments:
An edit component that is the editable view of each object. So you can think of it as a stand in for a text input with a v-model accept that it has tons of different inputs.
A list component which displays a list of all the objects for that type in the system. It controls navigation to edit that object.
Normally this would be simply something where I use slots and invoke this component in the view page for each CRUD object. So basically I'd have something like this:
<!-- this file is src/views/DogsCRUDPage.vue -->
<template>
<general-crud-component
name="dogs"
backendurl="/dogs/"
>
<template slot="list">
<dogs-list-component />
</template>
<template slot="edit">
<dogs-edit-field v-model="... oops .." />
</template>
</general-crud-component>
</template>
<script>
export default {
name: "DogCRUDPage",
components: {
GeneralCrudComponent,
DogListComponent,
DogEditField,
},
}
</script>
This is nice because it matches the general syntax of all of my other VueJS pages and how I pass props and things to shared code. However, the problem is that GeneralCRUDComponent handles all of the mechanisms for checking if an object is edited, and therefor hiding or unhiding the save button, etc. Therefor it has the editable object in its data which will become the v-model for DogsEditField or any other that's passed to it. So it needs to pass this component a prop. So what I've done this:
// This file is src/utils/crud.js
import Vue from "vue"
const crudView = (listComponent, editComponent) => {
return Vue.component('CrudView', {
template: `
<v-row>
<list-component />
<v-form>
<edit-component v-model="obj" />
</v-form>
</v-row>
`,
components: {
ListComponent: listComponent,
EditComponent: editComponent,
},
data() {
return {
obj: {},
}
},
})
}
export default crudView
This file has a ton of shared code not shown that is doing the nuts and bolts of editing, undo, saving, etc.
And then in my src/router/index.js
//import DogCRUDPage from "#/views/libs/DogCRUDPage"
import crudView from "#/utils/crud"
import DogListComponent from "#/components/DogListComponent"
import DogEditField from "#/components/design/DogEditField"
const DogCRUDPage = crudView(DesignBasisList, DesignBasis)
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{
path: "/dog",
name: "dog",
component: DogCRUDPage,
},
})
This is working, but there are issues I don't love about it. For one, I needed to enable runtimecompiler for my project which increases the size of the payload to the browser. I need to import the list and edit components to my router instead of just the page for every single object I have a page for. The syntax for this new shared component is totally different from the template syntax all the other pages use. It puts all of my page creation into the router/index.js file instead of just layed out as files in src/views which I am used to in Vue.
What is the idiomatic way to accomplish this? Am I on the right track here? I'm happy to do this, it's working, if this really is how we do this in Vue. But I would love to explore alternatives if the Vue community does something differently. I guess I'm mostly looking for the idiomatic Vue way to accomplish this. Thanks a bunch.
How about this:
DogsPage.vue
<template>
<CrudView
:editComponent="DogsEdit"
:listComponent="DogsList"
></CrudView>
</template>
<script>
import DogsEdit from '#/components/DogsEdit.vue'
import DogsList from '#/components/DogsList.vue'
import CrudView from '#/components/CrudView.vue'
export default {
components: { CrudView },
data() {
return { DogsEdit, DogsList }
}
}
</script>
CrudView.vue
<template>
<div>
<component :is="listComponent"></component>
<component :is="editComponent" v-model="obj"></component>
</div>
</template>
<script>
export default {
props: {
editComponent: Object,
listComponent: Object
},
data() {
return {
obj: {}
}
}
}
</script>

defining global variables with vite development

Now I am using vite build tool for my vue SFC app. I read the documentation of vite with the link below:
vite config link
If I am not wrong, the define option in config could be used for defining global constants. What I want to do is to define for example the name of my App in a variable inside this option and then use it in my Vue components. But unfortunately there is no example of code in the documentation about this option.
I tried this code in my vite.config.js file:
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
define: {
global: {
appName: "my-custom-name"
}
},
plugins: [vue()]
})
I am not sure that the syntax and code is correct! And also if it is correct I don't know how to call (use) this constant in my vue app components (.vue files). For example I want to use it in template or script part of this component:
<template>
<div class="bgNow">
<p class="color1">
{{ use here }}
</p>
</template>
<script>
export default {
data() {
return {
name: use here
};
},
methods: {
nameMethod() {
console.log(use here);
}
} // end of method
} // end of export
</script>
<style scoped></style>
I declared the places that want with "use here" in the code. And also if there is any other way that I could define some global constants and variables in my vite vue app, I very much appreciate your help to tell me about that.
define is a config that tells Vite how to perform a search-and-replace. It can only replace one string for another (objects cannot be used as a replacement).
For example, to replace all instances of appName with "my-custom-name", use the following config. Note JSON.stringify() is used (per the recommendation in the docs) to ensure the literal string replacement is properly quoted.
export default defineConfig({
define: {
appName: JSON.stringify('my-custom-name')
}
})
If App.vue contained:
<script setup>
console.log('appName', appName)
</script>
It would be transformed to:
<script setup>
console.log("appName", "my-custom-name")
</script>
demo

Vue: When to register component in main.js vs in the .vue file

Say I have a basic vue project
main.js:
import { createApp } from 'vue';
import App from './App.vue';
import SomeComponent from './components/SomeComponent.vue';
const app = createApp(App);
app.component('some-component', SomeComponent);
app.mount('#app');
components/App.vue:
<template>
<some-component></some-component>
</template>
<script>
import SomeComponent from "./SomeComponent.vue";
export default {
components: { SomeComponent },
};
</script>
components/SomeComponent.vue:
<template>
<div></div>
</template>
<script>
export default {};
</script>
Note that in main.js, I call app.component('some-component', SomeComponent); and in SomeComponent.vue I also specify the same with components: { SomeComponent },. Only one of the two ways is needed (though specifying both doesn't seem to cause errors).
My question is this: When would you specify components in main.js instead of in the component that will actually use it?
It seems that I could create an entire storefront and list all of the components in main.js without ever using components: {} inside a single one of my components and it would work. But it seems more logical to me to list the used sub-components inside each component that will use them for the encapsulation and reusability it brings. But that's because I have an object oriented mindset.

What are the differences between vue-hooks and vue-property-decorator?

I want to separate my logic from the component in vue.
What I have in mind is this:
One file for HTML template, the data pass as props.
Another file that has a lot of functions and getters that gets the data from the store/API.
So after I search a lot I understand I need something like "hooks in reacts".
What I find is u3u/vue-hooks.
My question is what are the benefits to use this idea/library? cause it seems like I do the same with and without vue-hooks.
for example:
foo.ts:
import { computed, defineComponent } from '#vue/composition-api';
export default defineComponent({
name: 'foo',
props: {
icon: {
type: String,
default: '',
},
},
setup(props) {
const iconName = computed(() => props.icon);
return {
iconName,
};
},
});
and foo.vue:
<template>
<div>{{iconName}}</div>
</template>
<script lang="ts" src="./foo.ts">
</script>
<style lang="scss">
</style>
So until here, I separate the logic from the component and I can choose which file to attach the view.
But I can do it with the class as well.
just to change the foo.ts file to:
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Foo extends Vue {}
I would like it if anyone explains to me - if hooks are the way to separate the logic from the UI? and should I use vue-hooks to do it?