Can't get jsonp callback to work inside a Vue component - vue.js

I'm trying to take this Flickr jsonp Vue example (https://codepen.io/tomascherry/pen/GrgbzQ) and turn it into a component. However, I cannot figure out how to get jsonFlickrFeed() to map to this.jsonFlickrFeed() (once that function is place inside the component's methods: {}).
Code as follows:
<template>
<!-- HTML HERE -->
</template>
<script>
let callApiTimeout = null
export default {
name: 'Flickr',
filters: {
splitTags: function(value) {
// showing only first 5 tags
return value.split(' ').slice(0, 5)
}
},
directives: {
/* VueJs utilites */
img: {
inserted: function(el, binding) {
this.lazyload(el, binding)
},
update: function(el, binding) {
this.lazyload(el, binding)
}
}
},
data() {
return {
images: [],
query: ''
}
},
watch: {
query: function(value) {
clearTimeout(callApiTimeout)
callApiTimeout = setTimeout(
function() {
const reqURL =
'https://api.flickr.com/services/feeds/photos_public.gne'
const options = {
params: {
format: 'json',
tags: this.query,
jsoncallback: 'this.jsonFlickrFeed'
}
}
this.$http.jsonp(reqURL, options)
}.bind(this),
250
)
}
},
methods: {
/* JSONP callback function */
jsonFlickrFeed(response) {
this.$data.images = response.items
},
/* General utility functions */
lazyload(el, binding) {
const img = new Image()
img.src = binding.value
img.onload = function() {
el.src = binding.value
}
}
}
}
</script>
<style lang="less">
/* STYLE HERE */
</style>
I tried adding the jsoncallback: 'this.jsonFlickrFeed' parameter but that doesn't help.

To make it simpler, just pass the parameter nojsoncallback=1 and it will return the JSON object directly.

Related

How pass data from component to external js file

I want to use my component's data within my external JavaScript file, containing my Dropzone configuration. I tried unsuccessfully to use Function.prototype.bind:
export const dropzoneConfig = {
url: api.http.baseUrl + '/files',
thumbnailWidth: 150,
maxFilesize: 5,
acceptedFiles: 'image/*',
addRemoveLinks: true,
sending: function (file, xhr, formData) {
formData.append('type', 'photo');
},
success: function (file, xhr) {
file.id = xhr.data.id;
if (this.entry.files === undefined) {
this.entry.files = [];
}
this.entry.files.push(xhr.data);
this.saveNote();
}.bind(this),
headers: api.http.authHeaders().headers
};
In the code above, this.entry and this.saveNote are unavailable because they're from my Vue component. How do I make them accessible to the external file?
A more general solution would be for the component to pass in a success-event handler that has access to the component's data and methods, as shown below. This solution decouples the configuration from the component's internals.
dropzoneConfig.js:
export const dropzoneConfig = ({ onSuccess }) => ({
//...
success(file, xhr) {
//...
onSuccess(xhr.data)
}
})
App.vue:
<script>
import Dropzone from 'dropzone'
import { dropzoneConfig } from './dropzoneConfig'
export default {
data() {
return {
entry: {
files: []
}
}
},
created() {
Dropzone.options.myComponent = dropzoneConfig({
onSuccess: fileData => this.onDropzoneSuccess(fileData)
})
},
methods: {
saveNote() {
//...
},
onDropzoneSuccess(fileData) {
this.entry.files.push(fileData)
this.saveNote()
}
}
}
</script>

How to destroy vuejs component from outside of component

I have created one of component in vuejs something like this
var tree_data = Vue.extend({
template: '#tree_data_template',
props: [
'groupmodal',
'search-name',
'tree-id'
], // props in single quotes
data: function () {
return {
shared: d2d.store
};
}
});
And use this component in another template like this.
var template_data = Vue.extend({
template: '#template_data',
created: function () {
var self = this;
self.shared.setCurrentRoute('templates');
},
components: {
'tree-data': tree_data
},
data: function () {
return {
id: this.$route.params.id,
shared: d2d.store,
};
},
methods: {
destroyComponent: function () {
//Need to code for destroy tree-data component
}
}
});
Blade file code
<tree-data
groupmodal="false"
search-name="user_search"
tree-id="user_tree"
>
</tree-data>
So finally how can i destroy my "tree-data" component through the "destroyComponent()" method
As cobaltway said you can use v-if
Setting v-if initially to false will render(generate) the component.
Then in your method setting v-if to true will destroy the component.
html
<div id="template_data">
<tree-data v-if="destroyComponent"></tree-data>
</div>
script
var template_data = Vue.extend({
template: '#template_data',
created: function () {
var self = this;
self.shared.setCurrentRoute('templates');
},
components: {
'tree-data': tree_data
},
data: function () {
return {
id: this.$route.params.id,
shared: d2d.store,
destroyComponent:true
};
},
methods: {
destroyComponent: function () {
//Need to code for destroy tree-data component
this.destroyComponent = false;
}
}
});
Here is the fiddle

VueJS: Setting data initially based on http response

So I have a template .vue file:
<template>
<div id="app">
<textarea v-model="input" :value="input" #input="update"></textarea>
<div v-html="compiledMarkdown"></div>
</div>
</template>
<script>
var markdown = require('markdown').markdown;
export default {
name: 'app',
data() {
return {
input: '# Some default data'
}
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
}) })
},
computed: {
compiledMarkdown: function() {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
});
return markdown.toHTML(this.input);
}
},
methods: {
update: function(e) {
this.input = e.target.value
}
}
}
</script>
In the mounted function I am trying to set input equal to the response of an HTTP request, but when you view this file this.input is still the same as it was initially declared. How can I change this.input inside the compiledMarkdown function to be this.input in the mounted function. What other approaches might I take?
You can not call a async method from a computed property, you can use method or watcher to run asynchronous code, from docs
This is most useful when you want to perform asynchronous or expensive operations in response to changing data.
You have to ran that relevant code when input changes, like following:
var app = new Vue({
el: '#app',
data: {
input: '# Some default data',
markdown : ''
},
methods: {
fetchSchoolData: function (schoolId) {
var url = this.buildApiUrl('/api/school-detail?schoolId=' + schoolId);
this.$http.get(url).then(response => {
this.schoolsListData = response.data;
}).catch(function (error) {
console.log(error);
});
},
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
})
})
},
watch: {
// whenever input changes, this function will run
input: function (newInput) {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
this.markdown = markdown.toHTML(this.input);
});
}
},
Have a look at my similar answer here.

multiple watchers same function vue.js

I have several watchers that should be calling the same function, is there way to list them all in once statement?
watch: {
'param.a' (nv) {
this.calc();
}
,'param.b' (nv) {
this.calc();
}
,'param.c' (nv) {
this.calc();
}
}
something along the lines of 'param.a,param.b,param.c' (nv) {...} ?
Edit: I should have clarified, this isn't the actual code, but I can't use a computed property.
Not sure why you can't use a computed property but you could add the watcher in the created hook like in the demo below or this fiddle.
I think a watch for array is not implemented in vue. Also there is a similar question at SO, just with Vue 1.x syntax. (There the watch is added in the mounted hook (previously ready) but I think you don't have to wait for DOM ready to add the watch. Anyway that would also work.)
My code is inspired by this github issue. Only changed to a mixin and to ES6 arrow function.
Vue.mixin({
methods: {
watchCollection(arr, cb) {
arr.forEach((val) => this.$watch(val, cb))
}
}
})
new Vue({
el: '#app',
/*watch: {
'param.a' (nv) {
this.calc();
}
,'param.b' (nv) {
this.calc();
}
,'param.c' (nv) {
this.calc();
}
},*/
/* // not suported
watch: {
['param.a', 'param.b', 'param.c'] : (nv) {
this.calc();
}
},*/
created() {
this.watchCollection(
['param.a', 'param.b', 'param.c'],
this.calc)
},
computed: {
resultComputed() {
return this.calc();
}
},
methods: {
calc() {
let a = parseInt(this.param.a);
let b = parseInt(this.param.b);
let c = parseInt(this.param.c);
this.result = a + b + c;
return this.result;
}
},
data() {
return {
param: {
a: 0,
b: 0,
c: 0
},
// msg: 'hello',
result: 0
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<input v-model="param.a" />
<input v-model="param.b" />
<input v-model="param.c" />{{result}} {{resultComputed}}
<!--{{msg}}-->
</div>

VueJS Extends Methods and Data

I would like to extend methods. Example:
Vue.extend({
data: function () {
return {
fetcData: 'Test',
}
},
methods: function(){
return {
modal: function(){ alert('Modal') },
}
}
});
Vue.extend({
....
});
I use multiple extends.
// VueJS Instance
new Vue({
el: 'html',
data: {
effect: true
},
methods: {
xhrGet: function () {
alert(this.fetcData); // Undefined
this.modal(); // Undefined
},
xhrPost: function (event) {
alert('xhrPost')
}
}
});
Error Code:
this.fetchData is undefined.
this.modal is not a function
Vue.extend returns a new Vue component definition that can be later on instantiated using new keyword, it doesn't change the global Vue object. So if you want to create a component hierarchy try:
var BaseComponent = Vue.extend({
methods: {
foo: function() { console.log('foo') }
}
})
var MyComponent = BaseComponent.extend({
methods: {
bar: function() {
this.foo()
console.log('bar')
}
}
})
let myComponentInstance = new MyComponent()
Check out that fiddle for a live example.