How can I use single file vue component with symfony 4? - symfony

What I want to do is:
1) in app.js to be able to use
import Vue from 'vue'
import Time from './vue/components/Time'
Vue.component('vue-time', {
template: '<span class="foo bar">Hi</span>'
})
new Vue({
el: 'header',
components: { Time }
})
2) in Time.vue:
<template>
<div>current time</div>
</template>
<script>
module.exports = {
}
</script>
<style>
</style>
Is this possible? All is implemented using encore-webpack and symfony4.
Thanks.

in app.js registered 2 components:
Time - registered localy
vue-time - registered global
if you want register Time global add in app.js this:
*do not use name Time https://developer.mozilla.org/uk/docs/Web/HTML/Element/time
Vue.component(`Time`, Time)
in app.js delete this:
components: { Time }
then use tag <time></time> anywhere in your application

Related

How to get current route via useRoute outside of router-view component

How can I get the current route using useRoute for a component that's outside of the <router-view />? Is this possible?
Breadcrumbs.vue
<script setup>
import {useRoute} from 'vue-router'
const route = useRoute()
console.log(route.name) // undefined
</script>
App.vue
<template>
<Breadcrumbs />
<router-view />
</template>
The alternative is that I have to put <Breadcrumbs /> at the top of every single view component, and I was hoping to avoid that and instead just include it once in my App.vue
route.name is undefined in your example because that's the initial value when the component is rendered before the route has resolved.
For the component to reactively update based on the current route, use a watcher (e.g., watch or watchEffect):
import { watchEffect } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
watchEffect(() => {
console.log(route.name)
})
demo
In your main file, try to mount app like this
router.isReady().then(() => {
app.mount('#app');
});
then useRoute() should be ready in your component

Cannont mount twice the same Vue 3 component

Im' mounting a Vue 3 component as a loading animation during a frame loading with :
createApp(App).mount(e);
When the frame is loaded, the component is erased in the html by the frame content (but not by Vue I guess). This behaviour is managed by an external system of Vue.
When I'm trying to reload the component with the same command, the component is not displayed.
If I only mount again the component, I have the following warning in the console and the component is not displayed :
"[Vue warn]: App has already been mounted.
If you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. `const createMyApp = () => createApp(App)`".
I've found also a way to replicate the issue with :
const app = createApp(App)
app.mount(el);
el.innerHtml = '';
app.mount(el);
I also try to unmount the component with no more success :
const app = createApp(App);
app.mount(el);
app.unmount();
app.mount(el);
So what is the correct way to display again the same vue component when it is erased externally ?
To mount/unmount 2 instances of Vue, it is necessary to use render method.
So, for exemple below, this solution works :
var app = createApp({ render: () => h(App) })
app.mount(el);
el.innerHtml = '';
app = createApp({ render: () => h(App) })
app.mount(el);
Don't forget to import h() function with :
import { createApp, h } from 'vue';
An far more better option is to use Vue custom elements.
First, create a classical SFC with ce.vue extension :
//test.ce.vue
<template>
<div class="text-primary">Test</div>
</template>
<script>
export default {
name: 'test',
};
</script>
<style>
.text-primary {
color: red;
}
</style>
And then in the main script :
//app.js
import Test from 'test.ce.vue';
const testElement = defineCustomElement(Test);
customElements.define('test-element', testElement);
document.body.appendChild(document.createElement('test-element'));
The component will be rendered as soon as is detected in the document:
<test-component>
#shadow-root (open)
<style>
.text-primary {
color: red;
}
</style>
<div class="text-primary">Test</div>
</test-component>

Problem creating a WebComponent from a SFC with Vue 3.2.9

I am trying to create a web component in Vue3. For this I use the Vue cli with the target library. Everything works as expected.
The problem is that I can't get any values from the props parameter of the setup function.
If I use the component as a Vue component the props work. As a web component i cant get any value from the props parameter
Its seams that the property wc-Test is not forwarded properly.
Does anyone have any ideas?
The Component Code
<template>
<a class="btn">
<slot></slot>
</a>
</template>
<script>
import {version } from "vue";
export default {
name: 'xn-button',
props: {
variant: {
default: 'normal',
type: String
}
},
setup(props, context) {
console.log(`Vue: ${version}`)
console.log(props)
console.log(props.variant)
},
}
</script>
<style></style>
Usage as Part of Vue Library:
<xn-button variant="Vue-Test">Test1</xn-button>
Usage as WebComponent:
<xn-button variant="wc-Test">Test2</xn-button>
Console output:
image

Symfony Vue JS import App not working without file type

I tried importing my App. vue file inside app.js like this:
import Vue from 'vue';
import App from './App';
new Vue({
el: '#app',
render: h => h(App)
});
My App.vue file:
<template>
<div>
<h2>My Application</h2>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
This doesnt work an gives me this error:
"export 'default' (imported as 'App') was not found in './App'
However, if I add .vue inside my import it suddenly works just fine. In other tutorials I found for Symfony and vue it works fine without having to add .vue. Any reason as to why it's not working for me?

Moment.js with Vuejs

I try to print out date time using like the following in vue-for
{{ moment().format('MMMM Do YYYY, h:mm:ss a') }}
but, it does not appear. It's just a blank. How I can try to use moment in vue?
With your code, the vue.js is trying to access the moment() method from its scope.
Hence you should use a method like this:
methods: {
moment: function () {
return moment();
}
},
If you want to pass a date to the moment.js, I suggest to use filters:
filters: {
moment: function (date) {
return moment(date).format('MMMM Do YYYY, h:mm:ss a');
}
}
<span>{{ date | moment }}</span>
[demo]
If your project is a single page application, (eg project created by vue init webpack myproject),
I found this way is most intuitive and simple:
In main.js
import moment from 'moment'
Vue.prototype.moment = moment
Then in your template, simply use
<span>{{moment(date).format('YYYY-MM-DD')}}</span>
In your package.json in the "dependencies" section add moment:
"dependencies": {
"moment": "^2.15.2",
...
}
In the component where you would like to use moment, import it:
<script>
import moment from 'moment'
...
And in the same component add a computed property:
computed: {
timestamp: function () {
return moment(this.<model>.attributes['created-at']).format('YYYY-MM-DD [at] hh:mm')
}
}
And then in the template of this component:
<p>{{ timestamp }}</p>
I made it work with Vue 2.0 in single file component.
npm install moment in folder where you have vue installed
<template>
<div v-for="meta in order.meta">
{{ getHumanDate(meta.value.date) }}
</div>
</template>
<script>
import moment from 'moment';
export default {
methods: {
getHumanDate : function (date) {
return moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY');
}
}
}
</script>
Here is an example using a 3rd party wrapper library for Vue called vue-moment.
In addition to binding Moment instance into Vue's root scope, this library includes moment and duration filters.
This example includes localization and is using ES6 module imports, an official standard, instead of NodeJS's CommonJS module system requires.
import Vue from 'vue';
import moment from 'moment';
import VueMoment from 'vue-moment';
// Load Locales ('en' comes loaded by default)
require('moment/locale/es');
// Choose Locale
moment.locale('es');
Vue.use(VueMoment, { moment });
Now you can use the Moment instance directly in your Vue templates without any additional markup:
<small>Copyright {{ $moment().year() }}</small>
Or the filters:
<span>{{ 3600000 | duration('humanize') }}</span>
<!-- "an hour" -->
<span>{{ [2, 'years'] | duration('add', 1, 'year') | duration('humanize') }}</span>
<!-- "3 years" -->
// plugins/moment.js
import moment from 'moment';
moment.locale('ru');
export default function install (Vue) {
Object.defineProperties(Vue.prototype, {
$moment: {
get () {
return moment;
}
}
})
}
// main.js
import moment from './plugins/moment.js';
Vue.use(moment);
// use this.$moment in your components
For moment.js at Vue 3
npm install moment --save
Then in any component
import moment from 'moment'
...
export default {
created: function () {
this.moment = moment;
},
...
<div class="comment-line">
{{moment(new Date()).format('DD.MM.YYYY [ ] HH:mm')}}
</div>
Moment.js with Vue3 js
npm install moment --save # npm
yarn add moment # yarn
Main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import moment from 'moment'
const app = createApp(App)
app.config.globalProperties.$moment = moment
app.use(router).mount('#app')
Used moments in vue3 js component
{{ $moment(item.created_at).format("YYYY-MM-DD") }} // 2021-07-03
global members are not available by default in your <template>'s scope. But you can easily pass them on using computed properties.
computed: {
moment: () => moment,
console: () => console,
window: () => window
}
Now you can use any of them in your template. i.e: console.log(moment(), window).
Note this doesn't add any overhead.
vue-moment
very nice plugin for vue project and works very smoothly with the components and existing code.
Enjoy the moments...😍
// in your main.js
Vue.use(require('vue-moment'));
// and use in component
{{'2019-10-03 14:02:22' | moment("calendar")}}
// or like this
{{created_at | moment("calendar")}}
I've read the solutions posted here and it seems to be more complex than my solution so I'm presenting this one, what I do is like this
The thing you need:
import moment from 'moment';
...
data() {
return {
moment: moment, // This is the one line of code that you need
}
}
So this is what it looks like
HTML (Now this works):
<h1>{{moment().format('MMMM Do YYYY, h:mm:ss a')}}</h1>
JS:
import moment from 'moment';
export default {
data() {
return {
moment: moment, // This is the one line of code that you need
}
}
}
I'd simply import the moment module, then use a computed function to handle my moment() logic and return a value that's referenced in the template.
While I have not used this and thus can not speak on it's effectiveness, I did find https://github.com/brockpetrie/vue-moment for an alternate consideration
TESTED
import Vue from 'vue'
Vue.filter('formatYear', (value) => {
if (!value) return ''
return moment(value).format('YYYY')
})
Install the moment module:
npm i moment
In your vue component:
import moment from 'moment';
export default {
data(){
},
methods:{
moment(date){ return moment(date) }
}
}
Inside the template:
<span>{{ moment().format('MMMM Do YYYY, h:mm:ss a') }}</span>

Resources