It seems to be simple, but somehow I fail to complete.
I want to preload images in my nuxt app, like in this Codepen but not as a directive, cause I only use it in one component.
It works fine, like this in a picture tag.
<img
v-img-preloader="require(`#/assets/img/placeholder.png`)"
v-if="lastImage"
v-lazy="longUrlWithStuff"
:alt="altTitle"
:class="classNameInner"
/>
(The directive v-img-preloader is on every as well.)
And the directive itselft is:
Vue.directive('img-preloader', {
bind: function (el, binding) {
let img = new Image();
img.src = binding.value;
img.onload = function () {
el.src = binding.value;
}
}
})
But now, I do not know, how to transform this, into a method or mounted hook. I would prefer a method, but do not know, how and what to pass an "el" for this element.
I saw, that this fiddle and tried it like there, with a mounted hook, but stuck ...
mounted() {
let img = new Image();
img.src = require(`#/assets/img/placeholder.png`);
img.onload = () => {
this.src = require(`#/assets/img/placeholder.png`);
};
},
I appreciate, if anybody with a clue, sees, what I am doing wrong.
Thanks in advance.
Related
So I have implemented Echarts with a Vue application, on one of the charts, I am trying to get the item clicked and pass it back to the parent component that way I can do specific calculations to it.
The 'on click' method works and I can console.log('params') easily, however, trying to reach any other functions outside of it is not possible for some reason...
here is my code...
data() {
return {
myChart: null,
selectedState: {}
}
}.
mounted() {
this.myChart = echarts.init(document.getElementById("geoMap"))
this.myChart.on('click', function(params){
// It will run the console.log with correct info, but the
// method is not reachable...
console.log(params)
this.setSelectedState(params)
})
},
// Inside my vue script this is just a method to set the data for now...
methods: {
setSelectedState(params){
this.selectedState = params
},
}
any help would be nice!! thanks!
You're not in the Vue component context when listening to the chart event, so you have to change your callback function to an arrow one to access the component's this :
this.myChart.on('click', params => {
this.setSelectedState(params)
});
methods: {
setSelectedState(params) {
console.log(params);
this.selectedState = params
}
}
By the way, you should use ref instead of getting your div with document.getElementById to attach your chart :
<div ref="geoMap"></div>
this.myChart = echarts.init(this.$refs.geoMap);
I'm trying to integrate a simple image zooming library. Basically when clicking on an image, it's expanded so that user can see the image better. Something like this: https://kingdido999.github.io/zooming/
My attempt is to create a plugin that inserts the script to the body.
After some playaround, this is what I ended up with:
/**
* options.selector: css path to the image tags (e.g '.markdown img')
*/
module.exports = function (context, options) {
let selector = options.selector;
let postBodyString = `
<script src="https://unpkg.com/zooming/build/zooming.min.js"></script>
<script>
// Listen to images after DOM content is fully loaded
let initImageZoom = function() {
setTimeout(function() {
new Zooming({
customSize: '100%',
bgOpacity: 0.95,
scaleBase: 0.8,
}).listen('${selector}');
}, 100);
};
let monitorURLChange = function() {
// capture URL change in SPA
let url = location.href;
document.body.addEventListener('click', () => {
requestAnimationFrame( () => {
if (url !== location.href) {
initImageZoom();
}
url = location.href;
});
}, true);
}
document.addEventListener('DOMContentLoaded', initImageZoom);
document.addEventListener('popstate', initImageZoom);
monitorURLChange();
</script>
`;
return {
name: 'docusaurus-image-zoom',
injectHtmlTags() {
return {
headTags: [],
postBodyTags: [postBodyString],
};
},
};
};
However, this solution feels very hackish since I have to listen to events like popstate or do manual monitoring of browser's URLs.
I imagine there should be a proper way of doing this. What should be the proper way of doing this?
It doesn't seem like you need to listen to the React Router events as this is not really related to routing.
I think the right solution here is to create MDX plugins for the img syntax/tag.
I have a Vue.js SPA with some pages that display data from a backend. When I navigate the pages via the navbar, everything works fine, components and data are loaded.
When I'm on the page, e.g. localhost:8080/#/mypage and press F5, the data doesn't get loaded / rendered. Same goes for when I directly navigate to the page via the address bar.
The data gets loaded in this function:
async beforeMount() {
await this.initializeData();
}
I've tried to call the method in every lifecycle hook, i.e. created, beforeCreated, mounted etc...
In the mounted lifecycle hook I'm setting a boolean property to true, so that the table is only rendered when the component is loaded (done with v-if).
mounted() {
this.componentLoaded = true;
}
Not sure if this is important, but I've tried it with or without and it doesn't work.
I would really appreciate it if somebody knew whats happening here.
EDIT:
this.applications is a prop and contains multiple applications which contain instances. I want to add some variables from the backend to each application.
console.log(1) gets printed
console.log(2) does not
initializeData: function () {
let warn = 0;
console.log("1");
this.applications.forEach(async application => {
const instance = application.instances[0];
console.log("2");
let myData = null;
try {
const response = await instance.axios.get('url/myData');
myData = response.data;
} catch (err) {
}
let tmpCount = 0;
let tmpFulfilled = 0;
myData.forEach(ba => {
if(!ba.fulfilled){
warn++;
application.baAllFulfilled = false;
}else {
tmpFulfilled++;
}
tmpCount++;
})
console.log("3");
// Assign values
this.baTotalWarnings = warn;
application.baAnzahl = tmpCount;
application.baFulfilled = tmpFulfilled;
this.componentLoaded = true;
}
Try removing the async and await keywords from your beforeMount, and remove this.componentLoaded from mounted. Set it instead in the then block (or after await) in your initializeData method. I'm not sure Vue supports the async keyword in its lifecycle methods.
Something like this:
beforeMount() {
this.initializeData(); // start processing the method
}
methods: {
initializeData() {
callToBackend().then(() => {
this.componentLoaded = true // backend call ready, can now show the table
})
}
}
I have a function in my Vue app which takes some time (about 2-3 seconds) to complete. It is not an AJAX call.
I would like to include a loading indicator while this code executes, but I am having trouble accomplishing it. I thought I could do the following...
<div v-on:click="doThings()">{{stuff}}</div>
methods: {
doThings: function () {
this.loading = true
console.log(this.loading)
longFunction()
this.loading = false
console.log(this.loading)
}...
}
...but that doesn't work. doThings() seems to not execute anything until longFunction() is done. I even tried making a separate function and changing my button to perform two functions like this...
<div v-on:click="doLoading(); doThings();">{{stuff}}</div>
...but this is also doesn't work. Is what I'm trying to do possible?
Use async code for longFunction() and set this.loading to false after the Promise is resolved.
<div v-on:click="doThings()">{{stuff}}</div>
methods: {
doThings: function () {
this.loading = true
longFunction().then(() => {
this.loading = false
})
}
}
var longFunction = function() {
return new Promise((resolve, reject) => {
window.setTimeout(()=>{ // force a new (pseudo)thread
// do stuff, then
resolve()
},100); // ...some reasonably short interval. One millisecond is working for me when testing locally, but it might be better to include some extra buffer, to ensure Vue is in its next tick
});
}
Alternatively, you could pass an object reference to longFunction that your Vue component can watch for changes on, and use that as the signal back to the component that it can set loading to false.
I have a webcomponent that creates a shadow DOM and adds some html to its shadowRoot.
class SomeThing extends HTMLElement {
attachedCallback () {
this.el = this.createShadowRoot();
this.render();
}
render () {
this.el.innerHTML = '<h1>Hello</h1>';
}
}
export default SomeThing;
And I am compiling it with the help of webpack and its babel-core and babel-preset-es2015 plugins.
Also I am using Karma and Jasmine to write my Unit Test. This is what it looks like.
describe('some-thing', function () {
var someElement;
beforeEach(function () {
someElement = document.createElement('some-thing');
});
it('created element should match string representation', function () {
var expectedEl = '<some-thing></some-thing>';
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.innerHTML).toBe(expectedEl);
});
it('created element should have shadow root', function () {
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.querySelector('some-thing').shadowRoot).not.toBeNull();
})
});
I want to see if there is something in the shadowRoot of my element, and want to write test cases for the HTML and events created inside the shadowRoot. But the second test is failing. It is not able to add shadowRoot to the some-element DOM.
If anyone can help me out, that would be helpful.
I am also uploading the full test working project on Github. You can access it via this link https://github.com/prateekjadhwani/unit-tests-for-shadow-dom-webcomponents
Thanks in advance
I had a similar problem testing a web component but in my case I am using lit-element from polymer/lit-element. Lit-element provides life cycle hooks, template rendering using lit-html library (documentation).
So this is my problem and how I solved. I noticed that the component was added and the class executed constructor and I had access to public methods using:
const element = document.querySelector('my-component-name')
element.METHOD();
element.VARIABLE
But it never reached the hook firstUpdated, so I thought the problem was the speed the test executes vs the speed component is created. So I used the promised provided by lit-element API (updateComplete):
Note: I use mocha/chai instead of Jasmine
class MyComponent extends LitElement {
render() {
return html`<h1>Hello</h1>`
}
}
customElements.define('my-component', TodoApp);
let element;
describe('main', () => {
beforeEach(() => {
element = document.createElement("my-component");
document.querySelector('body').appendChild(element);
});
describe('test', () => {
it('Checks that header tag was added to shadowRoot', (done) => {
(async () => {
const res = await element.updateComplete;
const header = element.shadowRoot.querySelector('h1');
assert.notEqual(header, null);
done();
})();
});
});
});
So, my advice is create a promise and resolve it when the render function is executed, use the promise to sync the creation of the component with tests.
I am using this repository to test concepts
https://github.com/correju/polymer-playground