I am using vue.js as CDN. I need help with a schematic of how I can build an application to display the component in index.html. Currently, I have the following structure:
<div id="app">
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
}
}).mount('#app')
</script>
component.js:
<template>
<div>
Test
</div>
</template>
export default {
data: () => ({
}),
}
You can try to define Vue and use .component
//in other file
const component1 = {
template: `<div> {{ item }} <p>{{ prop }}</p></div>`,
props: ['prop'],
data: () => ({ item: 'test' }),
}
const app = Vue.createApp({
data: () => ({ someData: 'prop' }),
})
app.component('test', component1)
app.mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="app">
<test :prop="someData" />
</div>
component_one.html
<p>component one</p>
component_two.html
<p>component {{two}}</p>
<input type="text" v-model="two"/>
component_three.html
<p>component three</p>
app.html
<router-link to="/">one</router-link> |
<router-link to="/two">two</router-link>
<component_three/>
<router-view />
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.41/vue.global.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.1.6/vue-router.global.js"></script>
<title>test</title>
</head>
<body>
<div id="app"/>
<script type="text/javascript" src="index.js"> </script>
</body>
</html>
index.js
const one = async () => {
let template = await fetch("component_one.html")
template = await template.text()
return ({
template: template,
setup() {/*...*/ }
})
}
const two = async () => {
let template = await fetch("component_two.html")
template = await template.text()
return ({
template: template,
setup() {
return {
two: Vue.ref("TWO"),
}
}
})
}
const three = async () => {
let template = await fetch("component_three.html")
template = await template.text()
return ({
template: template,
setup() {/*...*/ }
})
}
const app = async () => {
let template = await fetch("app.html")
template = await template.text()
return ({
template: template,
components: {
"component_three" : await three(),
},
setup() {/*...*/ }
})
}
const init = async () => {
const index = Vue.createApp(await app());
const routings = VueRouter.createRouter({
history : VueRouter.createWebHashHistory(),
routes : [
{path:'/', component: await one()},
{path:'/two', component: await two()}
]
})
index.use(routings)
index.mount("#app")
}
init()
html files are read as string. maybe put them in backend/database server. For faster loading, use Promise.all([]) to all await components.
working example: www.julven.epizy.com/vuetest
Here is what I have tried with vue router. We can separate the logic to components.
Here is the example in replit
In the index.html, we can use the <script type="module"> so we can use the import the js files.
In the components/home/home.js,
importTemplate function accepts a html file path and return the html file as string.
Other vue properties like methods, data can be put in the export default {}.
If sub-components are needed, we can use app.component('bottomb', BottomBar) like the one in index.html. <bottomb /> can then be used in all other components like the home.html, not just in index.html.
Related
I'm new to Vue, javascript & Web development. Using Vue, I tried to recreate the moviedb app(from Brad's 50 JS Projects in 50 Days course).
I'm getting stuck and can't get the data out of a scope.
I've successfully retrieved data & destructured it.
But how can I get those values out of that scope (out of setMovies function) and use it in the Vue file (html template)?
Here's my code:
I've made the api_key private
<h1>MovieDesk</h1>
<div class="hero">
<!-- Search -->
<div class="search">
<form #submit.prevent="handleSearch">
<input type="text" placeholder="Search here..." />
<button #click="handleSearch">Search</button>
</form>
</div>
</div>
<!-- Movies -->
<div v-if="searchOn">
<SearchedMovies />
</div>
<div v-else>
<MovieList/>
</div>
</template>
<script>
// imports-------------------
import { ref } from "#vue/reactivity";
import MovieList from "../components/MovieList.vue";
import SearchedMovies from "../components/SearchedMovies.vue";
import { onMounted } from "#vue/runtime-core";
export default {
components: { MovieList, SearchedMovies },
setup() {
const searchOn = ref(false);
const api_url = ref(
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=api_key&page=1"
);
const movies = ref([])
// getting the data ------------------------------
onMounted(() => {
fetch(api_url.value)
.then((res) => res.json())
.then((data) => {
console.log(data);
setMovies(data.results);
});
});
function setMovies(movies) {
movies.forEach((movie) => {
const { title, poster_path, vote_average, overview } = movie;
});
}
return { searchOn, setMovies };
},
};
</script> ```
In your setMovies function, You can set the response in the movies variable and then return that variable from your setup.
function setMovies(apiResponse) {
movies.value = apiResponse
}
return { movies };
Live Demo :
const { ref, onMounted } = Vue;
const App = {
setup() {
const movies = ref([])
onMounted(() => {
const apiResponse = [{
id: 1,
name: 'Movie 1'
}, {
id: 2,
name: 'Movie 2'
}, {
id: 3,
name: 'Movie 3'
}];
setMovies(apiResponse);
})
function setMovies(res) {
movies.value = res;
}
return {
movies
};
}
};
Vue.createApp(App).mount("#app");
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<pre>{{ movies }}</pre>
</div>
Add 'movies' to the return statement at the bottom of your code, then you should be able to render it.
I currently have this div:
<div class="center-align" :class="setDisplay()">
<div class="lds-hourglass"></div>
</div>
I need to check if "this" div contains "center-aligh" to execute code in setDisplay:
In my Vue 3 setup I have:
setup() {
const setDisplay = () => {
console.log(this.$el);
}
}
return {
setDisplay
}
this.$el returns as undefined. From what I've read I would return it like so:
this.$el.className based on this answer
But it's not working. Any idea what I'm doing wrong?
You can use ref in setup function and with nextTick get class in mounted hook with value.classList :
const { ref, onMounted, nextTick } = Vue
const app = Vue.createApp({
setup() {
const el = ref(null)
const myClass = ref(null)
const setDisplay = () => {
myClass.value = 'myclass'
}
onMounted(async () => {
await nextTick()
console.log(Array.from(el.value.classList))
Array.from(el.value.classList).includes('center-align') && setDisplay()
})
return { setDisplay, el, myClass }
}
})
app.mount('#demo')
.myclass {
color: red;
}
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div class="center-align xxx" ref="el" :class="myClass">
<div class="lds-hourglass"></div>
class applied
</div>
</div>
I want to be able to create reusable custom elements. With my current implementation, each of the custom elements renders only once. All the elements (all 4) are injected into the DOM, but only the first instance of each is rendered.
I have tried with both using ShadowDOM and not using it. Any ideas?
Screenshot from dev tools:
index.html (extract from <body>):
<body>
<funky-header></funky-header>
<funky-content></funky-content>
<funky-header></funky-header>
<funky-content></funky-content>
<script src="index.js" defer></script>
</body>
I have created a generic script to create custom elements from a .html file:
index.js:
const elements = [
{ name: 'funky-header', shadowDom: false },
{ name: 'funky-content', shadowDom: false }
]
async function registerCustomElement(elementName, shadowDom) {
console.log(`Registering ${elementName}`)
await fetch(`./${elementName}.html`)
.then(stream => stream.text())
.then(async markup => {
const doc = new DOMParser().parseFromString(markup, 'text/html');
const template = doc.querySelector('template[alpine]')
const templateContent = template.content
const styles = doc.querySelector('style[scoped]')
const styleContent = styles.textContent.toString()
const elements = templateContent.querySelectorAll('[class]')
class CustomElement extends HTMLElement {
constructor() {
super()
}
connectedCallback() {
console.log(`inserting ${elementName}`)
if (shadowDom) {
const shadowRoot = this.attachShadow({ mode: 'closed' })
shadowRoot.appendChild(template.content)
} else {
this.setAttribute(`data-x-${elementName}`, '')
elements.forEach(element => {
element.setAttribute(`data-x-${elementName}`, '')
})
const scopedStyles = styleContent.replaceAll(' {', '{').replaceAll('{', `[data-x-${elementName}] {`)
const styleTag = document.createElement('style')
styleTag.type = 'text/css'
styleTag.append(document.createTextNode(scopedStyles))
this.append(styleTag)
this.append(templateContent)
}
}
}
customElements.define(elementName, CustomElement)
})
.catch(err => {
console.log('ERROR:', err)
})
}
elements.forEach(element => registerCustomElement(element.name, element.shadowDom))
An example template file:
funky-header.html:
<template alpine>
<h1 class="font-black text-blue-800">This is my header</h1>
<p class="font-thin text-xs text-blue-600 my-text">This is a paragraph with a longer text, to simualte a descritpion.</p>
</template>
<style scoped>
.my-text {
color: purple;
}
</style>
The solution was as simple as replacing the this.append(templateContent) with this.innerHTML = template.innerHTML.
I have this app to make I am dealing with the CRUD and only the "U(pdate)" is posing problems
I have :
<template>
<EditQuestion
v-show="showEditQ"
:questToEdit="questToEdit"
/>
</template>
<script>
import EditQuestion from '../components/EditQuestion'
export default {
name: 'Home',
components: {
EditQuestion
},
data() {
return {
questToEdit:{},
}
},
methods:{
async getQuestion(_id){
const questToEdit = await this.fetchQuestion(_id)
return questToEdit
},
async fetchQuestion(_id) {
const res = await fetch(`http://localhost:3000/api/questions/${_id}`)
const data = await res.json()
return data
},
}
</script>
and in the component side I have
<template>
<div>
<p>test</p>
<p>{{questToEdit.question}}</p>
<li
v-for="(proposition,index) in questToEdit.propositions"
:key="index"
>{{proposition.props}}
</li>
</div>
</template>
<script>
export default {
name: 'EditQuestion',
props: {
questToEdit:Object
},
data(){
return {
}
},
}
</script>
I feel like I can't access questToEdit in the component (it's doing nothing ) or its never called on home ? thx for your time in advence.
Apologies if the question is confusing. Basically I have this html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A Gentle Introduction</title>
<script
src="https://rawgit.com/flatiron/director/master/build/director.min.js">
</script>
<script>
var author = function () { console.log("author"); };
var books = function () { console.log("books"); };
var viewBook = function (bookId) {
console.log("viewBook: bookId is populated: " + bookId);
};
var routes = {
'/author': author,
'/books': [books, function() {
console.log("An inline route handler.");
}],
'/books/view/:bookId': viewBook
};
var router = Router(routes);
router.init();
</script>
</head>
<body>
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
</ul>
</body>
</html>
which is clearly in a .html file. I want to change this to a .js file so that I can put html within the js so that when the different links are clicked, what is routed/returned is different.
I dont really know how to directly put this into a javascript file and then get the router to work. This is where the html file came from https://github.com/flatiron/director#client-side-routing and I am trying to use this flatiron/director router.
Any help would be great!
I was able to make it work with react and jsx and the routing code outside react itself.
Written with es6/es2015
app.js
const author = () => { console.log("author"); };
const books = () => { console.log("books"); };
const viewBook = (bookId) => { console.log("viewBook: bookId is populated: " + bookId); };
const routes = {
'/author': author,
'/books': [books, () => { console.log("An inline route handler."); }],
'/books/view/:bookId': viewBook
};
const router = Router(routes);
router.init();
class SampleRouting extends React.Component {
render() {
return (
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
</ul>
)
}
}
React.render( <SampleRouting/> , document.getElementById('root'));
index.html
<div id="root"></div>
sample: http://s.codepen.io/oobgam/debug/vNoogO
_edited app.js to reflect the updating of state and page header
class App extends React.Component {
constructor(props) {
super(props);
this.state = { currentPage: 'author' }
}
componentDidMount() {
const author = () => { this.setState({currentPage: 'author'}) };
const books = () => { this.setState({currentPage: 'Books'}); };
const viewBook = (bookId) => { this.setState({currentPage: 'Book ' + bookId }); };
const routes = {
'/author': author,
'/books': books,
'/books/view/:bookId': viewBook
};
const router = Router(routes);
router.init();
}
render() {
return (
<div>
<h1>{ this.state.currentPage }</h1>
<SampleRouting />
</div>
);
}
}
// stateless function
const SampleRouting = () => {
return (
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
< /ul>
)
}