I'm working on a Vue project on a static environment with no Node or Vue-cli,
We're importing Vue, Vuetify and vue-i18n using CDNs
We need to have the Vuetify components translated using the Vue-i18n like shown here
Here is a codepen of an attempt i've made, trying to translate the pagination part at the bottom.
I've tried using Vue.use() but couldn't get it to work, no errors in the console and no translation on the page.
import App from '../components/App.vue.js';
import i18n from '../lang/languages.js';
import store from './store/store.js';
Vue.filter('toUpperCase', function(value) {
return value.toUpperCase();
});
Vue.config.devtools = true;
Vue.use(Vuetify, {
lang: {
t: (key, ...params) => i18n.t(key, params)
}
});
new Vue({
i18n,
store,
el: '#app',
render: (h) => h(App)
});
lang/languages.js:
import { russian } from './languages/russian.js';
import { chineseSimple } from './languages/chinese-simple.js';
import { german } from './languages/german.js';
import { portuguese} from './languages/portuguese.js';
const languages = {
'ru': russian,
'zh-Hans': chineseSimple,
'de': german,
'pt': portuguese,
};
const i18n = new VueI18n({
locale: 'en',
messages: languages
});
export default i18n;
What you are looking for is not available in CDN distributions. You might ask why?
Look at this code:
const Vuetify: VuetifyPlugin = {
install (Vue: VueConstructor, args?: VuetifyUseOptions): void {
Vue.use(VuetifyComponent, {
components,
directives,
...args
})
},
version: __VUETIFY_VERSION__
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(Vuetify)
}
Especially this part:
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(Vuetify)
}
It automatically installs the Vuetify without any configurations such as language and etc, so your Vue.use(Vuetify, {..}) won't get called because Vue won't install plugins twice!
What you could do?
Clone the Vuetify repo and change this part of the code and build a new dist for your self.
Save as the file vuetify.dist.js and change that part of the code
Use some hacky tricky workarounds which I don't recommend, but I created a sample for you.
Here is a code pen example, What I actually do:
Load Vue.js file with scripts tag
Use fetch api to download content of vuetify.dist.min.js
Replace window.Vue to something else to make sure vuetify won't install itself automatically!
Eval the changed code
I DON'T RECOMMEND THIS APPROACH AT ALL
fetch("https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.14/vuetify.min.js")
.then(res => res.text())
.then(vutify => {
eval(vutify.replace("window.Vue", "window.Vue2"));
Vue.use(Vuetify, {
lang: {
t: (key, ...params) => i18n.t(key, params)
}
});
const App = Vue.component("app", {
template: `
Related
I have some problems with React and Advertisement.
Wanna use 'Coupang' Advertisement, but they support the script library only.
I can add it to 'index.html' in the public directory, but cannot customize the location.
here is the code,
<script src="https://ads-partners.coupang.com/g.js"></script>
<script>
new PartnersCoupang.G({"id":23232,"subId":null});
</script>
It's a dynamic advertisement.
How can I add it to the React functional component??
MB this will be helpful.
useScript will help you add script to your code dynamically.
and you custom hook (just create PartnersCoupang);
const usePartnersCoupang = () => {
const const [loaded, error] = useScript('https://ads-partners.coupang.com/g.js');
React.useEffect(() => {
if (loaded) {
new PartnersCoupang.G({"id":23232,"subId":null});
}
}, [loaded]);
React.useEffect(() => {
if (error) {
console.error('PartnersCoupang Failed.');
}
}, [error]);
}
Actually, you should eject from your create-react-app project by this command:
$ yarn eject
or:
$ npm run eject
Then you can see a folder that name is config, in this folder you can see all configuration of your project, especially your Webpack configs, then you should add your external library to the webpack as external key on the configuration of Webpack:
// webpack.config.js
...
module.exports = {
...
externals: {
PartnersCoupang: '[path-to]/g.js',
},
...
};
Then in your component import it easily:
import React, { Component } from 'react';
import PartnersCoupang from 'PartnersCoupang';
class YourComponent extends Component {
componentDidMount() {
new PartnersCoupang.G({"id":23232,"subId":null});
}
}
I have to display dynamic meta descriptions for my articles and I am kind of struggling to achieve that with the async function for my head object. This is what I have so far:
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ArticleContent extends Vue {
article: any | null = null;
articlelist = example.data;
async asyncData({params: any }) { <----- Not sure how I could put in my articlelist here
return this.articlelist;
}
head(): object {
return {
title: this.articlelist.productId.productNames['en'],
meta: [
{
hid: this.articlelist._id,
name: this.articlelist.productNames['en'],
content: this.articlelist.metaDescription['en'],
},
],
};
}
}
</script>
articlelist is what I am using in the head() object for my meta description. Would appreciate some help!
Both the head() and asyncData() properties are not part of the core of vue,
to use head() you need to install this as a plugin
to use asyncData() you have to use nuxt
If your spa has a strong need for seo I suggest you use nuxt, which natively includes seo and the conversion from vue to nuxt is very easy
If you already using nuxt this is the correct way to get
async asyncData({params: any }) {
const articlelist = await axios.get('some.url'); //get the data
return { articlelist }; //this object is merged with the data of the istance
}
I'm working on an app I'm migrate to VueJS so some parts are using old jQuery code.
So I'm trying to append an VueJS component using jQuery, so I made
import copyToClipboard from '../components/_base/VCopyToClipboard';
const CopyToClipboard = Vue.extend(copyToClipboard);
$(event.currentTarget).find('.dns-challenge-row').each((index, element) => {
const component = new CopyToClipboard({
propsData: {
targetId: $(element).find('code').attr('id'),
},
}).$mount();
$(element).append(component.$el);
});
Everything is working BUT when I go on the page where this component is appended, i18n return an error
Cannot translate the value of keypath 'tooltip.default'. Use the value of keypath as default.
FYI my translation messages are directly defined inside my SFC using the i18n keyword
i18n: {
messages: {
en: {
tooltip: {
default: 'Copy content',
success: 'Copied',
},
},
fr: {
tooltip: {
default: 'Copier le contenu',
success: 'Copié',
},
},
},
},
and I use then directly inside the SFC using this.$t('tooltip.default')
My i18n is import like the docs say but is loaded after the vue.js I use to create my component.
import {
Vue,
} from './vue';
import VueI18n from 'vue-i18n';
import en from '../../translations/en';
import fr from '../../translations/fr';
Vue.use(VueI18n);
export default new VueI18n({
locale: document.getElementsByTagName('html')[0].getAttribute('lang'),
messages: {
en,
fr,
},
});
The vue.js file is the the file where I put all my Vue.use() definitions, my routern, other stuff and is used to create the Vue instance inside another file
vueSetup(new Vue({
el: '#app',
components: {
...
},
i18n: i18n,
router: router,
store: store,
}));
Do you have an idea to solve this?
I tried to load i18n before the vue component without success and I saw a lot of GitHub issues with this error but not like my case.
Just import and add i18n instance to the new component instance
const CopyToClipboard = Vue.extend(copyToClipboard);
$(event.currentTarget).find('.dns-challenge-row').each((index, element) => {
const component = new CopyToClipboard({
i18n: i18n,
propsData: {
targetId: $(element).find('code').attr('id'),
},
}).$mount();
$(element).append(component.$el);
});
Is there a way for a .vue file to be responsible for creating its own Vue instance in a Single File Component pattern?
Here's the Vue File.
// MyComponent.vue
<template><div>Hello {{ name }}!</div></template>
<script>
const Vue = require('vue');
// what would usually be exports default
const componentConfig = {
name: "my-component",
props: {
name: String,
},
};
function create(el, props) {
const vm = new Vue({
el,
render(h) {
return h(componentConfig, { props });
});
vm.$mount();
return vm;
}
module.exports = { create };
</script>
and then the usage in some JS file:
// index.js
const MyComponent = require('./MyComponent.vue');
const el = '.container';
const props = {
name: 'Jess',
};
MyComponent.create(el, props);
</script>
When I do the above, I get errors about not being able to find the template.
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <MyComponent>
<Root>
Like instinctually, I don't understand how the Vue compiler would be able to magically deduce (from within the script tags) that I want to reference the template declared above... so.. yeah. Is there an explanation for why I can't do this, or thoughts on how I could get it to work?
What you are describing is done in a pre-compilation step through Webpack and Vue Loader. The Vue compiler doesn't actually parse Single File Components. What the Vue compiler can parse is templates provided in a component’s options object. So if you provide a template option in your componentConfig object your example will work. Otherwise you'll have to go through the pre-compilation step with Webpack and Vue Loader to parse a Single File Component's template. To do that you'll have to conform to the SFC structure defined in the spec. Here's an excerpt ..
Template
Each *.vue file can contain at most one <template> block at a
time.
Contents will be extracted and passed on to vue-template-compiler and
pre-compiled into JavaScript render functions, and finally injected
into the exported component in the <script> section.
Script
Each *.vue file can contain at most one block at a time.
The script is executed as an ES Module.
The default export should be a Vue.js component options object.
Exporting an extended constructor created by Vue.extend() is also
supported, but a plain object is preferred.
Any webpack rules that match against .js files (or the extension
specified by the lang attribute) will be applied to contents in the
<script> block as well.
To make your specific example work You can re-write main.js file like this ..
const MyComponent = require("./MyComponent.vue");
const el = ".container";
const data = {
name: "Jess"
};
MyComponent.create(el, data);
And your MyComponent.vue file (This could just as well be a js file as #Ferrybig mentioned below) ..
<script>
const Vue = require('vue');
function create(el, data) {
const componentConfig = {
el,
name: "my-component",
data,
template: `<div>Hello {{ name }}!</div>`
};
const vm = new Vue(componentConfig);
return vm;
}
module.exports = { create };
</script>
See this CodeSandbox
Or if you prefer render functions your MyComponent.vue will look like this ..
<script>
const Vue = require('vue');
function create(el, data) {
const componentConfig = {
el,
name: "my-component",
data,
render(h) { return h('div', 'Hello ' + data.name) }
};
const vm = new Vue(componentConfig);
return vm;
}
module.exports = { create };
</script>
CodeSandbox
One last thing to keep in mind: In any component you can use either a template or a render function, but not both like you do in your example. This is because one of them will override the other. For example, see the JSFiddle Vue boilerplate and notice how when you add a render function the template gets overridden. This would explain that error you were getting. The render function took precedence, but you fed it a component's options object that provides no template to be rendered.
You are really close to a working solution, but you are missing just some "glue" parts to combine everything together:
<template>
<div>Hello {{ name }}!</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
import Vue from "vue";
const Component = {
// Add data here that normally goes into the "export default" section
name: "my-component",
props: {
name: String,
},
data() {
return {
};
},
};
Component.create = function(el, props) {
const vm = new Vue({
el,
render(h) {
return h(Component, { props });
},
});
vm.$mount();
return vm;
};
export default Component;
</script>
This can then be used as follows in other files:
import App from "./App";
App.create("#app", {
name: 'Hello World!',
});
Example on codesandbox: https://codesandbox.io/s/m61klzlwy
A vue application I am working on currently has lots of code redundancies relating to date functions. In an effort to reduce these redundancies, I'd like to create a utility class as shown below, import it and set it to a Vue data property within the component, so I can call the date functions within it.
I am not certain on the best way to implement this. The current implementation results in an error saying TypeError: this.dates is undefined and my goal is not only to resolve this error but create/utilize the class in the Vue environment using best standards.
Importing utility class
import Dates from "./utility/Dates";
...
Component
const contactEditView = Vue.component('contact-edit-view', {
data() {
return {
contact: this.myContact
dates: Dates
}
},
...
Dates.js
export default {
dateSmall(date) {
return moment(date).format('L');
},
dateMedium(date) {
return moment(date).format('lll');
},
dateLarge(date) {
return moment(date).format('LLL');
}
};
View
Date of Birth: {{ dates.dateMedium(contact.dob) }}
My suggestion for this is to use a plugin option in Vue. About Vue plugin
So you will crate a new folder called services, add file yourCustomDateFormater.js:
const dateFormater = {}
dateFormater.install = function (Vue, options) {
Vue.prototype.$dateSmall = (value) => {
return moment(date).format('L')
}
Vue.prototype.$dateMedium = (value) => {
return moment(date).format('lll')
}
}
In main.js:
import YourCustomDateFormater from './services/yourCustomDateFormater'
Vue.use(YourCustomDateFormater)
And you can use it anywhere, like this:
this.$dateSmall(yourValue)
Or, if you want to use mixin. Read more about mixin
Create a new file dateFormater.js
export default {
methods: {
callMethod () {
console.log('my method')
}
}
}
Your component:
import dateFormater from '../services/dateFormater'
export default {
mixins: [dateFormater],
mounted () {
this.callMethod() // Call your function
}
}
Note: "Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application." - Vue documentation
dateUtilsjs
import moment from 'moment-timezone'
function formatDateTime(date) {
return moment.utc(date).format("M/D/yyyy h:mm A")
}
export { formatDateTime }
Component JS
...
import { formatDateTime } from '../utils/dateUtils'
...
methods: {
formatDateTime,
}
Used within component
{{ formatDateTime(date) }}