vue.js : click a button, look through dom, find all classes with a specific class name and append a new class to that existing class list - vue.js

So, i'm using Axios to pull an html file, and then take everything in that html file past the <body> tag and appending it to {{htmlData}} inside of a div in my template
looks like this:
<template>
<somebutton> I click on</somebutton>
<div id="some-container-name" v-html="htmlData">
{{ htmlData }}
</div>
</template>
data: function() {
return {
htmlData:''
};
},
methods: {
pullView: function(html) {
this.axios.get('http://someUrl.html').then(response => {
let corsHTML = response.data;
let htmlDoc = (new DOMParser()).parseFromString(corsHTML, "text/html");
this.htmlData = htmlDoc.documentElement.getElementsByTagName('body')[0].innerHTML;
})
},
The user has an option to click on a button - the code then searches through the dom and then appends a classname to every existing you-can-edit-me class name from the html document that is pulled in via axios.
Does this make sense?
Because of how I'm pulling this content in, I don't really have the chance to bind anything to this content using Vue's :bind directive. My Google-fu has failed me and need some suggestions. Is it possible to edit this.htmlData and make the transformation in that object and then i guess re-render the data??
I don't want to pollute my work with jQuery and wondering if anyone else has done something like this?

Since you already have a parsed htmlDoc:
for (const element of htmlDoc.querySelectorAll('.you-can-edit-me')) {
element.classList.add('additional-css-class');
}
inside your pullView method, before assigning it to this.htmlData.

Related

Unable to add elements using the setAttribute

I am using the VUE JS code and trying to add the setAttribute to some of the tags.
Here is the code I am using :
changetab() {
const demoClasses = document.querySelectorAll(".delCon__select");
demoClasses.forEach(button => {
button.setAttribute("tabindex", "0");
});
return true;
},
but when I view in the code inspector, It does not show added to it, I have added the above function in computed.
template is like this :
<template>
<el-container class="orders"></el-download>
</template>
You need to make this type of request in Vue's Lifecycles, like: created or mounted.
Something like:
mounted() {
this.changetab()
}
Computed would not be the most appropriate place for this type of action.

Can I get Vue.js component as an instance?

I try to get the template of Vue.js component since I need it for another function.
First,
I have a function that needs a Vue.js component template because I want the data in the template to dynamically change and not just some static HTML.
Secondly,
I have a problem to get a template from Vue.js component. Maybe they are not allowed to do that but I am not sure. I am new to Vue.js.
But from my understanding of JS, maybe this can happen.
I am tried to do something like this:
let vmComponent = Vue.component('VueComponent', {
data() {
return {
title: 'I am a Vue.js component manually.'
}
},
template: `
<div>
<h2>Template Exchange</h2>
<h3>{{ title }}</h3>
</div>`,
});
console.log(vmComponent.template);
I hope to get the HTML string when I run vmComponent.template
If this way of doing things is not possible. Is there any other ways I can get Vue.js component template as HTML String.
For example, maybe in a new.Vue({})
It's possible to access component template with vmComponent.options.template.
A template can be declared as a string for reuse:
export const template = `...`;
const vmComponent = Vue.component('VueComponent', {
template,
...
});
This may not work with pre-compiled templates.

vuejs where in the code/lifecycle should data retrieval be triggered

I am working on a vuejs SPA.
I have a view that shows a list of items and another view that shows details for a specific Item.
when I click the item I switch views using:
this.$router.push('/item/' + event.ItemId );
The data is managed using vuex modules.
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
Where would be the appropriate place (code/lifecycle) to trigger the (async) retrieval of the data required for rendering the item details view?
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
One way to achieve this, is to define a state variable, named e.g. isLoading, in the data context of the Vue component. This variable would then be true while the data is retrieved asynchronously. In the template, you can use v-if to display a spinner while loading, and displaying the content after that.
If you are retrieving the data multiple times (refreshing the view), I would move the retrieving code into a method, e.g. called loadData. In the mounted section of the Vue component you then can just initially call this method once.
Here is some example code:
<template>
<div>
<button #click="loadData" :disabled="isLoading">Refresh</button>
<div class="item" v-if="!isLoading">
{{ item }}
</div>
<div class="spinner" v-else>
Loading...
</div>
</div>
</template>
<script>
import HttpService from '#/services/HttpService';
export default {
name: 'item-details',
data () {
return {
isLoading: false,
item: {}
};
},
methods: {
loadData () {
this.isLoading = true;
HttpService.loadData().then(response => {
this.item = response.data;
this.isLoading = false;
}, () => {
this.item = {};
this.isLoading = false;
});
}
},
mounted () {
this.loadData();
}
};
</script>
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
This issue you mentioned occurs if you are not using the HTML5 history mode, but an anchor (#) in the URL instead. If you are just changing the part after the anchor in the URL, the page is not actually refreshed by the browser. The Vue component won't be reloaded in this case and the state is still old. There are basically two ways around this:
You are switching from anchors in the URL to a real URL with the HTML5 history mode, supported by the Vue Router. This requires some back-end configuration, though. The browser then does not have this faulty behavior, because there is no anchor. It will reload the page on every manual URL change.
You can watch the $route object to get notified on every route change. Depending on if the user is changing the part after the anchor, or before, the behavior is different (it also depends where the cursor is, when you hit enter). If the part after the anchor is changed (your actual Vue route), only the component is notified. Otherwise, a full page refresh is made. Here's some example code:
// ...inside a Vue component
watch: {
'$route' (to, from) {
this.loadData();
}
}

Lazy loading images in Vue/Laravel

I am trying to use jQuery Lazy Loading in my Laravel/Vue project but I am struggling to get an image to appear in my Vue component. I have the following img block which I thought would work:
<img v-if="vehicle.photo_path != null" :data-original="'/storage/vehicles/' + vehicle.photo_path" class="lazy" height="180" width="150"/>
I did find this other question on here - Static image src in Vue.js template - however when I try that method I get this: Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead.
So I switched back to the v-bind method but all I am getting is a white box with a grey border - no image. If I v-bind on the src attribute however I can see the image correctly.
I know I have implemented the Lazy Loading plugin correctly as I can successfully call it elsewhere on my site (such as on a blade view), but I'm not sure where I am going wrong. Thank you.
Try moving the call to $("img.lazy").lazyload() into the mounted() method on your Vue instance. I just had a similar issue with Vue and jQuery Lazyload and that solved it for me. For example:
var app = new Vue({
el: ...
data() ...
computed: ...
methods: ...
mounted() {
$("img.lazy").lazyload()
}
})
I found many modules on the internet, but I like to avoid modules when I can. So I came up with something else, I don't know if it's the best, but it works and I didn't see any performances loss, I lazy load all the images when the page loads. You may prefer on scroll if you have lots of them, but I guess you'll figure this out if my answer fits your needs.
You'll need vueX for this, but I'll avoid the set up as this is not replying to your question.
If, like me, you have some kind of Index.vue component which main usage is to initiate all the child components (I use to do that for vue-router for instance), place a mounted() function in it :
mounted(){
const ctx = this;
// You could use this method, but if you reload the page, the cache of the browser won't allow your JS to trigger .onload method, so better use what's after.
/*window.onload = function(){
console.log('page loaded, can load images');
ctx.$store.dispatch('setPageLoaded', true);
}*/
let interval = setInterval(function() {
if(document.readyState === 'complete') {
clearInterval(interval);
ctx.$store.dispatch('setPageLoaded', true);
}
}, 100);
}
=> On the page load, I just set a page_load variable in the store to true.
Then, in any component you'd like to lazy load the image, just use 2 computeds (I used a mixin that I include in my components so I avoid repeating some code) :
computed: {
page_loaded(){
return this.$store.getters.getPageLoaded;
},
image(){
if(this.page_loaded){
console.log('starting loading image');
if(this.product.picture){
return resizedPicture(this.product.picture, this.width, this.height);
}else if(this.product.hasOwnProperty('additional_pictures') && this.product.additional_pictures.length){
return resizedPicture(this.product.additional_pictures[0], this.width, this.height);
}
return '';
}
}
}
page_loaded() goal is to return the store page_loaded variable I talk about.
image() goal is to actually load the image.
I used that for inline CSS background-image property, so my HTML looks like this :
<span v-if="page_loaded" class="image" :style="'background-image:url('+image+')'" :width="1200" :height="900"></span>
I hope it'll help, but as I said, feel free guys to tell me if it's not optimized.

Multiple instances of a vue.js component and a hidden input file

I am experiencing a weird behavior using Vue.js 2.
I have a component that I reference twice in a single html page.
This component contains an input file control called attachment_file. I hide it using the Bootstrap class hidden and I open the file selection using another button. When a file is selected, I put in a variable called attachment_filename a certain string just like so:
<template>
<div>
<button #click="selectAttachement"><span class='glyphicon glyphicon-upload'></span></button>
<input id="attachment_file" type="file" class="hidden" #change="attachmentSelected">
{{attachment_filename}}
</div>
</template>
<script>
export default {
data () {
return: {
attachment_filename: null,
}
},
methods: {
selectAttachement () {
$('#attachment_file').click();
},
attachmentSelected () {
this.attachment_filename = 'some file here';
},
}
}
</script>
Problem With the class hidden and when a file is selected from the 2nd instance of the component, the value of this.attachment_filename is updated but in the data of 1st instance of the component!
If I remove the class hidden, it updates the value in the correct instance.
Possible solution use css opacity or width instead of the class hidden.
But is there a reason for this behavior?
Not sure why it is not working specifically with .hidden, but you have an inherent problem in the code that i think is the cause of the problem.
You are selecting the input using jquery with an id, that creates a couple of problems:
When you use the component twice or more, all these inputs generated by these components will have the same id, which is not what you want since id should be unique
Even if you change it to a class instead of id, it won't work properly since you are selecting the element using jquery, and that will select all the elements with this class, while you want to select just the input in that component.
The solution is to use refs:
<input id="attachment_file" type="file" class="hidden" #change="attachmentSelected" ref="fileInput">
selectAttachement () {
$(this.$refs.fileInput).click();
},