Vue3 Add html to existing project - vuejs3

I’ve built my first project and have run the build process. I have my index.html file and it works if opened directly.
I’ve copied the code into an existing html page and the initial page load is fine. However, when props get updated, binding (v-if statements) no longer works.
Any help would be great
Edit with code example
<script>
import { ref } from "vue";
import Determining from './Determining.vue'
import Ready from './Ready.vue'
export default {
components: {
'Determining': Determining,
'Ready': Ready,
},
setup() {
let checkout = ref({
state: 'determining',
});
return {
checkout,
};
},
created() {
this.checkout.state = 'ready';
console.log("I am getting here");
}
}
</script>
<template>
<Determining v-if="checkout.state == 'determining'" />
<Ready v-if="checkout.state == 'ready'" />
</template>
The determining state is shown when the page first loads. The console log is firing in setup, but Ready component is not showing
Progress
I've narrowed it down to other javascript running on the page.
Any javascript, even just
<script>console.log("hello");</script>
Is enough to break it.
Other than adding additional javascript to Vue, is there anyway around it?

if I'm not wrong, you can't access composition api or setup() variables in options api (such as created, mounted, data, methods etc). You can use beforeMount as #Thomas commented or onMounted by importing it from vue, for the example:
<script>
import { ref, beforeCreate } from "vue";
import Determining from './Determining.vue'
import Ready from './Ready.vue'
export default {
components: {
'Determining': Determining,
'Ready': Ready,
},
setup() {
beforeCreate(()=> {
checkout.state.value = 'ready';
console.log("I am getting here");
})
let checkout = ref({
state: 'determining',
});
return {
checkout,
};
}
}
</script>
<template>
<Determining v-if="checkout.state == 'determining'" />
<Ready v-if="checkout.state == 'ready'" />
</template>
if you want it more simple, you can use script sugar setup, this way you don't need to return in stup(). It can make your code simpler but if you want to define props, the approach is different
<script setup>
import { ref, beforeCreate } from "vue";
import Determining from './Determining.vue'
import Ready from './Ready.vue'
beforeCreate(()=> {
checkout.state.value = 'ready';
console.log("I am getting here");
})
let checkout = ref({
state: 'determining',
});
</script>
<template>
<Determining v-if="checkout.state == 'determining'" />
<Ready v-if="checkout.state == 'ready'" />
</template>

Related

Retrieve a Font Awesome or Bootstrap Icon (or React Component) from MongoDB into React

I'm building a database of hot sauce products in MongoDB Atlas for my React App. One attribute of the sauces is heat intensity. I'd like to use a bootstrap or font awesome icon to illustrate this. One flame, two flames, three flames...etc. I figured out a handful of ways to code it if I were storing the products statically in my React app, but since I want to have a dynamic inventory stock number attached to each product, I'm storing all the product descriptions in the database.
My question is how can I store the icon, in any of it's formats (bootstrap icon for example):
Unicode: U+F7F6
CSS: \F7F6
JS: \uF7F6
HTML: &#xF7F6
and then retrieve it.
Everything I've tried just errors or gives me a plain string in my div.
I'd also be all ears if you have a way to store and retrieve react components, as I can go the svg imported as a component route.
Like if I have
import { ReactComponent as Pep5 } from "../assets/pep5.svg";
import { ReactComponent as Pep4 } from "../assets/pep4.svg";
import { ReactComponent as Pep3 } from "../assets/pep3.svg";
and can somehow store and retrieve
<Pep4 className="heatScale"/>
My code for getting all the records in my db is:
recordRoutes.route("/sauce").get(function (req, res) {
let db_connect = dbo.getDb("products");
db_connect
.collection("sauces")
.find({})
.toArray(function (err, result) {
if (err) throw err;
res.json(result);
});
});
module.exports = recordRoutes;
All the other parts of my "product" are working great and coming across perfectly.
I figured out the boneheaded method I was trying to accomplish the svg version.
TL;DR I don't need to store the React Component code in the database, only an identifier I can later use to conditionally return the React Component I want from a function.
Imports:
import { ReactComponent as Pep5 } from "../assets/pep5.svg";
import { ReactComponent as Pep4 } from "../assets/pep4.svg";
import { ReactComponent as Pep3 } from "../assets/pep3.svg";
import { ReactComponent as Pep2 } from "../assets/pep2.svg";
import { ReactComponent as Pep1 } from "../assets/pep1.svg";
Function:
function PepEval(heat) {
const value = JSON.stringify(heat);
console.log(value);
if (value.includes("1")) {
return <Pep1 className="peppers" />;
} else if (value.includes("2")) {
return <Pep2 className="peppers" />;
} else if (value.includes("3")) {
return <Pep3 className="peppers" />;
} else if (value.includes("4")) {
return <Pep4 className="peppers" />;
} else if (value.includes("5")) {
return <Pep5 className="peppers" />;
}
return "Something Broke";
}
Div that calls the function:
<div className="itemHeat">
<PepEval value={{ heat }} />
</div>
I'm sure JS has a Select Case type thing instead of all the else ifs but I wanted to get it working.
If someone has a better method for evaluating the JSON object that I'm bringing in, I'm all ears.

Why isn't my t() texts refreshing in localhost/en but refreshing in localhost/fr on i18n.changeLanguage()?

Hi
I just made a website with a darkmode and multilanguage support to test around but I ran into an issue.
the code
I got rid of all things that aren't an issue
portfolio/src/pages/index.tsx
import { useTranslation } from 'react-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export default () => {
const { t,i18n } = useTranslation('common')
return <div onClick={()=>i18n.changeLanguage(i18n.language=='fr'?'en':'fr')}>
<div>{i18n.language}</div>
<span>{t('debug')}</span>
</div>
}
export async function getStaticProps({ locale }:any) {
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
// Will be passed to the page component as props
},
};
}
portfolio/src/public/locales/en/common.js
{"debug":"english"}
portfolio/src/public/locales/fr/common.js
{"debug":"français"}
portfolio/next-i18next.config.js
const path = require("path");
module.exports = {
debug: false,
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
localePath: path.resolve('./src/public/locales'),
};
portfolio/src/pages/_app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import {appWithTranslation} from 'next-i18next'
export default appWithTranslation(({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />
})
The issue
When I do npm run dev and go to http://localhost:3000/fr, the page defaults to french and works good I can swap between languages without problems but when i go to http://localhost:3000/en the t('debug') doesn't translate when the i18n.language changes as intended.
Found what I wanted
So basicaly I need to use a next Link that will change the local and the link
Code application
index.js
//...
export default () => {
const { t,i18n } = useTranslation('common')
return (
<div>
<Link
href={i18n.language=='fr'?'/en':'/fr'}
locale={i18n.language=='fr'?'en':'fr'}
>{i18n.language}</Link>
<div>{t('debug')}</div>
</div>
)
}
//...
result
Now the text changes as intended both in the /fr and /en because it switches between the 2 however the result is far from smooth. It reloads the page and i'd like to avoid that because I use some animations on it.
Found what i wanted part 2
Browsing through the next-i18next documentation I found what I wanted.
solution
I needed to load the props using getStaticProps and in the serverSideTranslation function i needed to pass as argument the array off ALL the language necessary to load the page ['en','fr'] because i switched between the 2

Vue3 Wait till Child Component is Mounted

I want to wait for a child component to mount before rendering a tooltip. While waiting, I will have a placeholder to display to the user.
An example component would be:
<template>
<div v-if="!mounted">Loading...</div>
<child-component></child-component>
</template>
<script>
export default defineComponent({
setup() {
const mounted = ref(false);
return {
mounted
}
}
});
</script>
After doing research, it looks like Vue2 supports life-cycle hooks on the components, which would change the above code to:
<template>
<div v-if="!mounted">Loading...</div>
<child-component #hook:mounted="mountedCheck"></child-component>
</template>
<script>
export default {
export default defineComponent({
setup() {
const mounted = ref(false);
const mountedCheck = () = {
mounted.value = true;
}
return {
mounted, mountedCheck
}
}
});
}
However, I cant seem to get the #hook:mounted to work. Is there something similar in Vue3, or am I missing something?
The syntax has changed in Vue3. Now you need to use #vue:mounted instead of #hook:mounted.
See Vue 3 Migration Guide - VNode Lifecycle Events for details
Also, keep in mind that some event names like destroyed and beforeDestroy have been renamed to unmounted and beforeUnmount respectively in Vue3
The child component needs to emit that hook
child:
<script>
export default defineComponent({
setup() {
onMounted(() => {
const { emit } = getCurrentInstance();
emit('mounted');
})
return {};
}
});
</script>
parent
<template>
<div v-if="!mounted">Loading...</div>
<child-component #mounted="mountedCheck"></child-component>
</template>

CodeMirror on Vue3 has a problem when setValue is kicked

I'm trying to use CodeMirror on Vue3 and the problem occurs when I call doc.setValue().
The Problem is following:
Cursor position is broken when doc.setValue() is called
CodeMirror throws an exception when continuing editing
The exception is here.
Uncaught TypeError: Cannot read property 'height' of undefined
at lineLength (codemirror.js:1653)
at codemirror.js:5459
at LeafChunk.iterN (codemirror.js:5623)
at Doc.iterN (codemirror.js:5725)
at Doc.iter (codemirror.js:6111)
at makeChangeSingleDocInEditor (codemirror.js:5458)
at makeChangeSingleDoc (codemirror.js:5428)
at makeChangeInner (codemirror.js:5297)
at makeChange (codemirror.js:5288)
at replaceRange (codemirror.js:5502)
How should I solve this?
~~~
Versions are CodeMirror: 5.61.1, Vue.js: 3.0.11
My code is following:
index.html
<div id="app"></div>
<script src="./index.js"></script>
index.js
import { createApp } from 'vue';
import App from './App';
const app = createApp(App);
app.mount('#app');
App.vue
<template>
<div>
<button #click="click">Push Me</button>
<textarea id="codemirror"></textarea>
</div>
</template>
<script>
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';
// import codemirror resources
import 'codemirror/addon/mode/overlay.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/gfm/gfm.js';
export default {
data () {
return {
cm: null
}
},
mounted () {
this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
mode: 'gfm',
lineNumbers: true,
});
},
methods: {
click (event) {
this.cm.getDoc().setValue('foo\nbar');
}
}
}
</script>
Thanks.
UPDATES
First, this problem also occurs when I used replaceRange() with multiline.
Unfortunately, I couldn't find any solution. So I tried to find another way.
My solution is recreating Codemirror instance with a textarea that has new content.
It works well.
// Remove old editor
this.cm.toTextArea();
// Get textarea
const textarea = document.getElementById('codemirror');
// Set new content
textarea.value = 'foo\nbar';
// Create new editor
this.cm = CodeMirror.fromTextArea(textarea, { /** options */ });
I found a method, you can use toRaw to get the original Object from Proxy,and this method can be also used in monaco-editor
import { toRaw } from 'vue'
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';
// import codemirror resources
import 'codemirror/addon/mode/overlay.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/gfm/gfm.js';
export default {
data () {
return {
cm: null
}
},
mounted () {
this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
mode: 'gfm',
lineNumbers: true,
});
},
methods: {
click (event) {
toRaw(this.cm).setValue('foo\nbar');
}
}
}
Another way,you don't have to define cm in data, just use this.cm
data () {
return {
//cm: null
}
},

Meteor 1.5 : Dynamic Import for Blaze

I am having two questions:
1) I want to use Meteor 1.5 Dynamic Import for Blaze but all the examples and tutorials are given for React. So I am confused how exactly it can be used . Can anyone give examples of it.
2) I am using packages from atmospherejs.com like amcharts which I only need at Admin Dashboard side. How to dynamically import them?
Thanks in Advance!
UPDATE(Solution):
Below is homepage.html (parent template)
<template name="homepage">
Homepage Content
{{> Template.dynamic template=content}}
</template>
login.html (child template)
<template name="login">
You're logged in!
</template>
login.js
import '../homepage/homepage.js';
import './login.html';
API = function () {
BlazeLayout.render("homepage",{content: 'login'});
}
export { API }
main.js
LoadLogin = function () {
import('/imports/modules/common/login/login.js').then(function (api) {
api.API();
})
}
/lib/route.js
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
FlowRouter.route('/', {
name: 'homepage',
action() {
LoadLogin();
}
});
I am developing my own admin panel, Meteor Candy, to be driven by dynamic imports, so I am happy to share how I got it working.
First, we have the view.html:
<template name="admin">
Admin
</template>
Second, we have our JS logic:
import { Template } from 'meteor/templating';
import { Meteor } from 'meteor/meteor';
import { Blaze } from 'meteor/blaze';
import './view.html';
API = {}
API.render = function () {
Blaze.render(Template.admin, document.body);
}
export { API }
Finally, we just need to import that code and trigger our Template to be rendered into the page:
openAdmin = function () {
import('./imports/admins').then(function (api) {
api.render()
})
}
Once something runs the openAdmin() function, the templates will be imported from the server and the render function will be called.
The basic technique for dynamically importing modules in Meteor 1.5 using Blaze is as follows:
Template.theTemplate.events({
'click button'(event, instance) {
import("foo").then(Foo => {
console.log(Foo);
});
}
});
Make sure you take a close look at how your module is then imported, because apparently some refactoring may be needed when calling it in your code. For example, using "zxcvbn":
WAS
const result = zxcvbn(pwd);
IS
const result = zxcvbn.default(pwd);
It is pretty straight forward using example link https://github.com/thesaucecode/meteor-amcharts-example/blob/master/client/example2.js, you just have to write the code inside Template.MYTEMPLATE.onRendered(function(){});
On top of that you can use var chart as reactive-var.

Resources