I'm quite new in vue.js world and I'm lost with a certain case.
I did a little caching system using resource-loader, it preloads my images and videos and puts the data in a array. Everything works fine but now, I don't know how to create my video element and so play it.
I can't do a v-for because sometimes it's an image, and other time it's a video...
I don't know what to do !
Here is my App.vue
<template>
<div>
<div class="loader" v-show="progress < 100">
<h1>{{ progress }}%</h1>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
data () {
return {
progress: 0,
cache: {}
}
},
// Static manifest
manifest: [
'./static/img/intro/test.jpg',
'./static/video/intro/bg.mp4',
'./static/video/intro/bg.webm',
'./static/video/home/scroll.mp4',
'./static/video/home/scroll.webm',
'./static/sound/home/sanik.mp3',
],
ready: function() {
let Loader = require('resource-loader');
let loader = new Loader();
let manifest = this.$options.manifest;
manifest.forEach(function(file) {
loader.add(file);
});
let that = this;
loader.on('progress', function(event, resource){
that.progress = Math.round(event.progress);
console.log('progress', this.progress);
let sequence = [];
sequence = resource.url.split('/');
sequence = sequence[sequence.length - 2];
if(that.cache[sequence] === undefined) that.cache[sequence] = {};
that.cache[sequence][resource.name] =
{
url: resource.url,
data: resource.data
}
;
});
loader.on('complete', function(event, resources){
that.$broadcast('cache-loaded', 'that.cache');
that.$route.router.go('/intro');
});
loader.load();
}
}
And this is my intro.vue
<template>
<h1>INTRO</h1>
<a v-link="{ path: '/home' }">Continue to Home</a>
</template>
<script>
export default {
name: 'Intro',
components: {},
events: {
'cache-loaded' : function(cached){
console.log(this.$root.cache['intro']['./static/img/intro/test.jpg']);
// ok it shows my element and what's next ??
}
}
}
</script>
EDIT : I found the solution to not append the HTML raw element, instead I use the vue's native data binding.
App.vue
<template>
<div class="">
<div class="loader" v-show="progress < 100">
<h1>{{ progress }}%</h1>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
data () {
return {
progress: 0,
img_cache: [],
vid_cache: []
}
},
// Static manifest
manifest: [
'./static/intro/img/test.jpg',
'./static/intro/video/bg.mp4',
'./static/intro/video/bg.webm',
'./static/home/video/scroll.mp4',
'./static/home/video/scroll.webm'
],
ready: function() {
let Loader = require('resource-loader');
let loader = new Loader();
let that = this;
let manifest = that.$options.manifest;
manifest.forEach(function(file) {
loader.add(file, file);
});
loader.on('progress', function(event, resource){
that.progress = Math.round(event.progress);
console.log('progress', this.progress);
if(resource.url.match(/\.(jpe?g|png|gif|bmp)$/i)){
that.img_cache.push({
'name': resource.name,
'src': resource.url
});
}else {
that.vid_cache.push({
'name': resource.name,
'src': resource.url
})
}
});
loader.on('complete', function(event, resources){
console.log('COMPLETE');
that.$route.router.go('/intro');
});
loader.load();
}
}
</script>
Intro.vue
<template>
<h1>INTRO</h1>
<a v-link="{ path: '/home' }">Continue to Home</a>
<div class="" v-for="itm in $root.vid_cache">
<video v-bind:src="itm.src" autoplay loop>
</video>
</div>
</template>
<script>
import Loader from './Loader'
export default {
name: 'Intro',
components: {Loader},
ready: function(){
console.log('READY');
}
}
</script>
I checked in the network inspector, the ressources load only once.
I found the solution to not append the HTML raw element, instead I use the vue's native data binding.
App.vue
<template>
<div class="">
<div class="loader" v-show="progress < 100">
<h1>{{ progress }}%</h1>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
data () {
return {
progress: 0,
img_cache: [],
vid_cache: []
}
},
// Static manifest
manifest: [
'./static/intro/img/test.jpg',
'./static/intro/video/bg.mp4',
'./static/intro/video/bg.webm',
'./static/home/video/scroll.mp4',
'./static/home/video/scroll.webm'
],
ready: function() {
let Loader = require('resource-loader');
let loader = new Loader();
let that = this;
let manifest = that.$options.manifest;
manifest.forEach(function(file) {
loader.add(file, file);
});
loader.on('progress', function(event, resource){
that.progress = Math.round(event.progress);
console.log('progress', this.progress);
if(resource.url.match(/\.(jpe?g|png|gif|bmp)$/i)){
that.img_cache.push({
'name': resource.name,
'src': resource.url
});
}else {
that.vid_cache.push({
'name': resource.name,
'src': resource.url
})
}
});
loader.on('complete', function(event, resources){
console.log('COMPLETE');
that.$route.router.go('/intro');
});
loader.load();
}
}
</script>
Intro.vue
<template>
<h1>INTRO</h1>
<a v-link="{ path: '/home' }">Continue to Home</a>
<div class="" v-for="itm in $root.vid_cache">
<video v-bind:src="itm.src" autoplay loop>
</video>
</div>
</template>
<script>
import Loader from './Loader'
export default {
name: 'Intro',
components: {Loader},
ready: function(){
console.log('READY');
}
}
</script>
I checked in the network inspector, the ressources load only once.
Related
I am trying to integrate a third party js application to my vue.js project. It is a molecular editor that will load on my page as a component.
Link to the js application - https://jsme-editor.github.io/dist/index.html
And this is what I want to achive -
<html><head>
<script type="text/javascript" language="javascript" src="jsme/jsme.nocache.js"></script>
<script>
//this function will be called after the JavaScriptApplet code has been loaded.
function jsmeOnLoad() {
jsmeApplet = new JSApplet.JSME("jsme_container", "380px", "340px");
jsmeApplet.setAfterStructureModifiedCallback(showEvent);
document.getElementById("log").value = "";
}
function showEvent(event) {
var log = document.getElementById("log");
log.value = event.action + " at: " + event.atom + " b: " + event.bond + " m: " + event.molecule + " smiles:" + event.src.smiles() + "\n" + log.value;
}
</script></head>
<body>
<div id="jsme_container"></div>
<textarea id="log" rows="5" cols="50"> </textarea>
</body>
</html>
What I have done so far is I have declared it as component as seen below -
<template>
<div class="hello">
<button #click="notify">Notify</button>
<div id="jsme-container" ref="jsmeContainer"></div>
</div>
</template>
<script>
export default {
name: 'newcomp',
data() {
return {
jsmeIsLoaded : false,
div_select : null
}
},
props: {
txt: String,
height: {
type: String,
required: true
},
width: {
type: String,
required: true
},
smiles: String,
options: String,
onChange: Function
},
mounted() {
let script = document.createElement('script')
script.src = '/home/rathore/Desktop/test_component/src/assets/JSME_2020-12-26/jsme/jsme.nocache.js'
document.head.appendChild(script)
this.$nextTick(
window.jsmeOnLoad = () => {this.jsmeIsLoaded = true;}
)
},
watch: {
jsmeIsLoaded(){
this.div_select = this.$refs.jsmeContainer.id
const div_loader = document.createElement('script')
const applet_txt = 'jsmeApplet = new JSApplet.JSME("'+this.div_select+'","300px","400px");'
//console.log(applet_txt,this.div_select)
const div_applet = document.createTextNode(applet_txt);
div_loader.appendChild(div_applet);
document.head.appendChild(div_loader);
}
},
methods: {
//Unused method
handleJsmeLoad(){
if (this.props.options){
this.jsmeApplet = new window.JSApplet.JSME(this.div_select, this.props.width, this.props.height, {options: this.props.options});
}
else {
this.jsmeApplet = new window.JSApplet.JSME(this.div_select, this.props.width, this.props.height);
}
this.jsmeApplet.setCallBack("AfterStructureModified", this.handleChange);
this.jsmeApplet.readGenericMolecularInput(this.props.smiles)
},
handleChange(jsmeEvent){
if (this.props.onChange) {
this.props.onChange(jsmeEvent.src.smiles())
}
},
notify(){
if (this.jsmeIsLoaded){
alert("JSME LOADED!!");
}
}
},
}
</script>
and called the component -
App.vue
template>
<div id="app">
<newcomp :height="'300px'" :width="'400px'" :onChange="logsmile" />
</div>
</template>
<script>
import newcomp from './components/NewComponenet.vue'
export default {
name: 'App',
components: {
newcomp
},
methods: {
logsmile(smiles){
console.log(smiles)
}
},
}
</script>
Here I have tried injecting <script> tag in the <head>. It is injected as I expected, but it doesn't work it give me error -
Uncaught ReferenceError: JSApplet is not defined. and it doesn't render the editor.
but actually I want it render using handleJSMELoad() method. Please suggest me the resolution.
I have a snippet below which is essentially my entire code block at this point, and essentially it creates a div and when you click "add another zone" it will clone that div. This allows the user to enter multiple lines of info and each have their own result and image.
The issue is that I'm successfully cloning everything with it's own unique identity thanks to my card setup. However, dropzone is not replicating. The first file dropzone form will work perfectly, but when I clone the div and have 2 or more dropzone insnstances on the page they don't work (they don't show the upload image text or anything)
How can I successfully apply my same logic to the dropzone instance here?
new Vue({
components: {},
el: "#commonNameDiv",
data() {
return {
searchString: [''],
results: [],
savedAttributes: [],
cards: [],
showList: false,
zoneNumber:[],
imageZoneNames: [] }
},
methods: {
autoComplete(ev, card) {
this.results = [];
console.log(this.searchString);
if (ev.target.value.length > 2) {
axios.get('/product/parts/components/search', {
params: {
searchString: ev.target.value
}
}).then(response => {
card.results = response.data;
this.showList = true;
console.log(this.results);
console.log(this.searchString);
});
}
},
saveAttribute(result, card) {
card.value = result.attribute_value;
card.results = [];
card.zone = this.zoneNumber;
this.showList = false;
},
addCard: function() {
this.cards.push({
index: "",
value: "",
zoneNumber: "",
results: [],
componentImage:""
});
console.log(this.cards);
},
hideDropdown() {
this.showList = false;
},
},
created() {
this.addCard();
let instance = this;
Dropzone.options = {
maxFilesize: 12,
renameFile: function (file) {
var dt = new Date();
var time = dt.getTime();
return time + file.name;
},
acceptedFiles: ".jpeg,.jpg,.png,.gif",
addRemoveLinks: true,
timeout: 50000,
removedfile: function (file) {
console.log(file.upload.filename);
var name = file.upload.filename;
var fileRef;
return (fileRef = file.previewElement) != null ?
fileRef.parentNode.removeChild(file.previewElement) : void 0;
},
init: function() {
this.on("addedfile",
function(file) {
instance.imageZoneNames.push({name: file.upload.filename, desc: 'Line Drawing'});
console.log(file);
console.log(instance.imageZoneNames);
});
}
};
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.0/dropzone.js"></script>
<div id="commonNameDiv">
<div class="uk-grid" v-for="(card, i) in cards" :key="i">
<div class="uk-width-1-10" >
<input v-model=" card.zoneNumber" size="4" type="text" name="mapNumber">
</div>
<div class="uk-width-6-10">
<input
style="width:100%"
placeholder="what are you looking for?"
v-model="card.value"
v-on:keyup="autoComplete($event, card)"
>
<div v-if="showList" class="panel-footer componentList" v-if="card.results.length">
<ul>
<li v-for="(result, i) in card.results" :key="i">
<a v-on:click="saveAttribute(result, card)">#{{ result.attribute_value }}</a>
</li>
</ul>
</div>
</div>
<div class="uk-width-3-10">
<form method="post" action="{{url('product/parts/upload/store')}}" enctype="multipart/form-data"
class="dropzone">
</form>
</div>
</div>
<div style="height: 35px;">
</div>
<div>
<a v-on:click="addCard">Add another zone</a>
</div>
</div>
When you instantiate the Dropzone class, it automatically looks for elements to transform in dropzones (by default, elements with the .dropzone class).
It looks like you want to dynamically add elements that are dropzones. Then you need to trigger the dropzone transformation yourself.
I would suggest you disable the autoDiscover option, and manually designates each element you want to transform into dropzones :
addCard() {
this.cards.push({
...
});
let cardIndex = this.cards.length - 1;
// Waiting for the element #dropzone-X to exist in DOM
Vue.nextTick(function () {
new Dropzone("#dropzone-"+cardIndex, {
...
});
});
},
created() {
...
Dropzone.autoDiscover = false
// no new Dropzone()
...
// Starting setup
this.addCard();
},
<form ... class="dropzone" v-bind:id="'dropzone-'+i">
Working jsbin
There are several ways to select the element to transform ($refs, ids, classes), here I'm suggesting ids.
See the doc on programmatically creating dropzones
Actually it is being created, but the Dropzone is not being reconstructed.
I think you have to create a new instance of the Dropzone.
if you try to insert:
created() {
this.addCard();
var myDropzone = new Dropzone('.dropzone')
let instance = this;
Dropzone.options.myDropzone = {
or even add the options to the addCard method or set a setupDropzones method and add it to the addCard method.
I am creating a simple SPA using vue.js and axioz as a scripts (not cli, etc)
So far I am able to pull data from a json then render and paginate the list,and when an item is clicked I am able to console log the data for the specific entry.
HTML
<!--app-->
<div id="app">
<!--articles-->
<div class="row" style="background: #111; padding: 8em 0; width: 50%;">
<div class="ctr">
<div class="row articles page_content" style="padding: 0;">
<ul>
<li v-for="(post) in displayedPosts" #click="getSingle(post.id)">
<router-link :to="{ path: '/post/'+ post.id}" class="flex" >
<div class="row article_thumb">
<img :src="post.url" :alt="post.title"/>
</div>
<div class="row article_excerpt">
<h3 class="title">{{post.title }}</h3>
</div>
</router-link>
</li>
</ul>
</div>
<div class="row pagination">
<button type="button" v-if="page != 1" #click="page--"> << </button>
<button type="button" v-for="pageNumber in pages.slice(page-1, page+5)" #click="page = pageNumber"> {{pageNumber}} </button>
<button type="button" #click="page++" v-if="page < pages.length"> >> </button>
</div>
</div>
</div>
<!--articles-->
<div class="row" style="background: #000; padding: 8em 0; width: 50%;">
<div class="flex router">
<router-view></router-view>
</div>
</div>
</div>
<!--app-->
VUE.JS
const Home = {
template: "<div><h1>Click an article to update this view</h1></div>"
};
//post
var Post = {
template:
'<div class="row"><h1>Display data for Post ID # {{$route.params.id}} here</h1><p style="color: red;">This is where I am stuck, cant display the post data, see example below.</p><p>{{title}}</p></div>',
//post methods
methods: {
//get single post
getSingle: function(id) {
var self = this;
this.id = this.$route.params.id;
this.title = this.title;
axios
.get(this.baseUrl, {
params: {
id: this.id,
}
})
.then(response => {
this.post = response.data;
this.title = response.data.title;
console.log(this.title);
console.log(this.post);
console.log("You clicked post ID #" + this.id);
})
.catch(response => {
console.log(error);
});
}
},
//post methods
//post data
data() {
return {
baseUrl: "https://jsonplaceholder.typicode.com/photos",
posts: [],
title: this.title
};
},
//post created
created() {
this.getSingle(this.$route.params.id);
},
watch: {
"$route.params": {
handler(newValue) {
const { id } = newValue;
this.getSingle(id);
},
immediate: true
}
}
};
//post
//router
const router = new VueRouter({
routes: [
{ path: "/", component: Home },
{ path: "/post/:id", component: Post }
]
});
//initial state
var paginationApp = new Vue({
el: "#app",
router: router,
data: {
posts: [],
baseUrl: "https://jsonplaceholder.typicode.com/photos",
page: 1,
perPage: 2,
pages: []
},
//initial state methods
methods: {
//get single
getSingle() {},
//get posts
getPosts() {
axios
.get(this.baseUrl)
.then(response => {
this.posts = response.data;
})
.catch(response => {
console.log(response);
});
},
//set pages
setPages() {
let numberOfPages = Math.ceil(this.posts.length / this.perPage);
for (let index = 1; index <= numberOfPages; index++) {
this.pages.push(index);
}
},
//paginate
paginate(posts) {
let page = this.page;
let perPage = this.perPage;
let from = page * perPage - perPage;
let to = page * perPage;
return posts.slice(from, to);
}
},
//created
created() {
this.getPosts();
},
//watch
watch: {
posts() {
this.setPages();
}
},
//computed
computed: {
displayedPosts() {
return this.paginate(this.posts);
}
}
});
//initial state
Or see this codepen for full example https://codepen.io/flashvenom/pen/YozyMx and be sure to checkout the console log.
My problem is i cannot console log the title or any internal fields of the data object, as I want to be able to add the title etc into the view area.
Any help or pointers would be much appreciated.
The response is in array form and you cannot access array object element without looping array.
If you wish to get title of first post, then you can do as shown below,
this.title = response.data[0].title
To access all posts title, you can use v-for loop in you vue template. Here is little example on how you can accomplish that,
<div v-for="post in posts">
<span>{{ post.title }}</span>
</div>
I'm new to Vue js - the following is not updating:
<div id="error" class="col s12 red center">
<span v-if="seen">
Error fetching readings: {{ msg }}
</span>
</div>
Vue:
var error = new Vue({
el: '#error',
data: {
msg: '',
seen: false
},
methods: {
show: function(message) {
this.msg = message;
this.seen = true;
},
hide: function() {
this.seen = false;
}
}
});
Post fetch:
fetch( ... )
.then(...)
.catch(err => {
error.show( err );
loader.hide();
});
error.show() displays the previously hidden div, but displays:
Error fetching readings: {}
Why?
i created a CodeSandbox sample based upon your code, you need to have computed property to have the Vue reactivity
Sample can be found, check code in HelloWorld.vue in components folder
https://codesandbox.io/s/x2klzr59wo
<template>
<div id="error" class="col s12 red center">
{{ seen }}
<hr />
<span v-if="computedSeen"> Error fetching readings: {{ msg }} </span>
<hr />
<button #click="show('effe');">SHOW</button>
<button #click="hide();">HIDE</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
msg: "",
seen: false
};
},
methods: {
show: function(message) {
this.msg = message;
this.seen = true;
},
hide: function() {
this.seen = false;
}
},
computed: {
computedSeen: function() {
// `this` points to the vm instance
return this.seen;
}
}
};
</script>
Oops, problem was err from the fetch is an object, and I should have used err.message.
In my code I had a console.log('Error: %s', err) which appears to format the err object into text. Which is what threw me :(
Sorry.
I am relatively new to Vue, so forgive me if this is obvious (or obviously impossible).
I have a set of JSON data (fetched from a RESTful API via vue-resource):
{content: "This is content. <a href='/blog'> Link to blog </a>"}
Right now, the link triggers a page reload. If it were a vue-router v-link, that would not be an issue. However, this doesn't work (quotes are escaped in the data, of course):
{content: "This is content. <a v-link="{ path: '/blog' }"> Link to blog </a>"}
At this point, the template is already parsed, and Vue won't create a v-link anymore (it will just show up as a v-link in the rendered html).
My final result would ideally mean that I could include links in my CMS, either in HTML or Vue format, and have Vue route them correctly as v-links.
Is there something I can do to make Vue interpret the link in the JSON data?
I've answered the question on Vue Chat, and writing it here in case any other people facing similar problem
Simplified example on Codepen
HTML
<div id="app">
<div>
<a v-link= "{path:'/home'}">Go to home</a>
</div>
<router-view></router-view>
</div>
<template id="home">
<div>
<div>
Fetched Content:
</div>
<div>
{{{ fetchedContent }}}
</div>
</div>
</template>
<template id="route1">
<div>
Route1 view
</div>
</template>
<template id="route2">
<div>
Route2 view, this is different from Route1
</div>
</template>
javascript
function getContent (callback) {
var content = 'Click this: Go to route1 and Go to route2'
setTimeout(function () { callback(content) }, 1000)
}
var Home = Vue.component('home',{
template:'#home',
data: function () {
return {
fetchedContent: 'Loading...'
};
},
ready: function () {
var self = this
var router = this.$router
getContent( function (result) {
self.fetchedContent = result;
Vue.nextTick(function () {
var hyperLinks = self.$el.getElementsByTagName('a')
Array.prototype.forEach.call(hyperLinks, function (a) {
a.onclick = function (e) {
e.preventDefault()
router.go({ path: a.getAttribute("href") })
}
})
})
})
}
});
var Route1 = Vue.component('route1', {
template: '#route1'
});
var Route2 = Vue.component('route2', {
template: "#route2"
});
var router = new VueRouter({
hashbang:false,
history:true
});
router.map({
'/home':{
component:Home
},
'/route1':{
component:Route1
},
'/route2':{
component:Route2
}
});
router.start({
}, '#app');
I had a similar solution here: question using a custom dataset in my JSON code and a click listener to process it:
mounted() {
window.addEventListener('click', event => {
let target = event.target
if (target && target.href && target.dataset.url) {
event.preventDefault();
console.log(target.dataset.url);
const url = JSON.parse(target.dataset.url);
console.log(url.name)
this.$router.push(url.name);
}
});
},