I'm getting crazy on this.
This is the code, is a component I'm using inside a form to show the preview of a video when I paste an url into an input:
<template>
<div class="row">
{{embedData}}
<input v-model="embedData" name="content[body]" id="content_body">
<div class="col-md-6">
<div class="form-group">
<div class="form-group url optional content_video_url form-group-valid">
<label for="content_video_url" class="url optional">Url del video</label>
<input #change="forceRerender" v-model="url" type="url" value="" name="content[video_url]" id="content_video_url" class="form-control is-valid string url optional">
</div>
</div>
</div>
<div class="col-md-6">
<div class="video-responsive"
<o-embed ref="embed" :url="url" :key="componentKey"></o-embed>
</div>
</div>
</div>
</template>
<script>
import oEmbed from './oEmbed'
import EventBus from '../utils/eventBus'
export default {
components: {
oEmbed
},
props: {
video_url: String,
video_caption: String
},
created: function() {
this.url = this.video_url;
this.caption = this.video_caption;
},
mounted: function() {
EventBus.$on('HTML', function (payLoad) {
this.embedData = payLoad
console.log('payLoad:' + this.embedData);
console.log('arrived');
});
},
data: function() {
return {
url: '',
caption: '',
componentKey: 0,
embedData: ''
}
},
methods: {
forceRerender () {
this.componentKey = this.componentKey + 1;
}
}
}
</script>
o-embed is a component, and I've added a simple bus emit function when the component is update:
mounted: function() {
EventBus.$on('HTML', function (payLoad) {
this.embedData = payLoad
console.log('payLoad:' + this.embedData);
});
}
If I check the console log I have this
payLoad: <iframe src="https://player.vimeo.com/video/596287904?app_id=122963&h=d77f5dc57c" width="426" height="240" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="Raritan Bay Cruisers-Hopelawn New Jersey CruisebNight 8-31-2021.wmv"></iframe>
Everythink is working, this.embedData looks ok, I have the log, but when I render embedData in my view it's empty.
I give an additional info: I'm forcing the re-rendering of the embed component, but I don't think it's related.
Any ideas?
You're using an anonymous function.
this inside the anonymous function does not provide the component's context.
Try using an arrow function instead:
EventBus.$on('HTML', (payLoad) => {
this.embedData = payLoad
console.log('payLoad:' + this.embedData);
});
Mythos found the issue (at least one of them). The mustache template (double curly braces) interpret contents as plain text, not html. If you want to inject raw html into your page you should do something like
<div v-html="embedData"></div>
instead (https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML)
Related
I have a pretty simple view that displays the icons of all characters from a certain game. If I were to visit the URL that displays that view through a router-link, everything works fine and I see the icons, however, if I then refresh the page, the icons disappear.
They also do not render at all if I manually type www.example.com/champions. Why is this happening.
My component:
<template>
<div class='wrapper'>
<div class="champions-container">
<div v-for='champion in champions' class="champion">
<img class='responsive-image' :src="'http://ddragon.leagueoflegends.com/cdn/' + $store.getters.version + '/img/champion/' + champion.image.full" alt="">
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
champions: this.$store.state.fullChampions
}
}
}
</script>
And my Vuex store where the champions are stored:
export default new Vuex.Store({
state: {
version: null,
fullChampions: null
},
mutations: {
version(state, data){
state.version = data.version
},
fullChampions(state, data){
state.fullChampions = data.fullChampions
}
},
actions: {
getVersion({commit}){
return axios.get("http://ddragon.leagueoflegends.com/api/versions.json")
.then((response) => {
commit('version', {
version: response.data[0]
})
})
.catch(function (error) {
console.log(error);
})
},
getFullChampions({commit, state}){
return axios.get("https://ddragon.leagueoflegends.com/cdn/" + state.version + "/data/en_US/championFull.json")
.then((response) => {
commit('fullChampions', {
fullChampions: Object.values(response.data.data)
})
})
.catch(function (error) {
console.log(error);
})
},
These might be because of these issues you encountered.
First: that component is not the one that dispatched your getFullChampions function in your vuex, might be in other component.
Second is that, you are already assigning the value of champions wherein the state fullChampions is not updated.
this.champions: this.$store.state.fullChampions // state.fullChampions might not yet updated.
Try this one might help you
watch: {
'$store.state.fullChampions': function() {
this.champions = this.$store.state.fullChampions
},
}
Last is to to do first a condition above your v-for to prevent the element
<div class="champions-container" v-if=""$store.getters.version>
<div v-for='champion in champions' class="champion">
<img class='responsive-image' :src="'http://ddragon.leagueoflegends.com/cdn/' + $store.getters.version + '/img/champion/' + champion.image.full" alt="">
</div>
</div>
Can you try to add this:
watch: {
$route: function(val, OldVal){
this.champions = this.$store.state.fullChampions;
}
},
after yuor data?
Upd.
If you are calling getFullChampions() action, then you can call it within watcher of my example instead of assigning to this.champions.
I have a array of strings in which I want to linkify certain words like "User object", "Promise", etc like this:
var strings = ['This returns a promise containing a User Object that has the id', 'next string']
This needs to be rendered like this
<div class="wrapper">
<div class="item" v-for="str in strings" v-html="str"></div>
</div>
The problem is I want to replace words like "User object", "Promise" and bind them to a #click event that my app can handle.
So if it were rendered like I want it to be, it would be something like this (the same v-for loop above rendered manually)
<div class="wrapper">
<div class="item">This returns a promise containing a User object that has the id</div>
<div class="item">next string</div>
</div>
I tried doing this but it doesn't bind the #click event
methods: {
linkify(str) {
return str.replace(/user object/, 'User object');
}
}
Any ideas?
Here's an example of a component that takes in a string for the full message and a string for the text to replace with a link and renders a span with that message with the link text wrapped in a <a> tag:
Vue.component('linkify', {
template: '#linkify-template',
props: {
value: { type: String },
linkText: { type: String }
},
computed: {
before() {
return this.value.split(this.linkText)[0];
},
after() {
return this.value.split(this.linkText)[1];
}
}
});
new Vue({
el: '#app',
data() {
return {
message: 'This returns a promise containing a User Object that has the id',
}
},
methods: {
foo() {
console.log('clicked')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script type="text/x-template" id="linkify-template">
<span>
{{ before }}
<a href="#" #click.prevent="$emit('click')">
<code>{{ linkText }}</code>
</a>
{{ after }}
</span>
</script>
<div id="app">
<linkify link-text="User Object" :value="message" #click="foo"></linkify>
</div>
Okay figured it out. If somebody has a better way to do it please answer too!
Vue.component('linkify', {
props: ['value', 'words'],
template: `<span :is="html"></span>`,
data() {
return {
html: Vue.compile('<span>' + this.value.replace(new RegExp('(' + this.words.join('|') + ')', 'g'), `<code>$1</code>`) + '</span>'),
}
}
});
Now all I need to do in the main app is this:
<div class="wrapper">
<div class="item" v-for="str in strings">
<linkify :value="str" :words="['user object', 'promise']" #click="help"></linkify>
</div>
</div>
Unfortunately this only works with full version of Vue (which has the compile function)
I am working on an app with cryptocurrencies, so right now I have 2 components:
MyCoins:
Vue.component('mycoins',{
data() {
return {
coins: [],
}
},
template: `
<ul class="coins">
<coin v-for="(coin, key) in coins" :coin="coin.coin_name" :key="coin.coin_name"></coin>
</ul>
`,
methods: {
getStats() {
self = this;
axios.get('api/user/coins').then(function (response) {
console.log(response.data.coins);
self.coins = response.data.coins;
})
.catch(function (error) {
console.log(error);
});
}
},
mounted() {
this.getStats();
}
})
On url 'api/user/coins' I get this data:
{"coins":
[{"id":1,"coin_name":"ethereum","user_id":1,"buy_price_usd":"341.44000","buy_price_btc":"0.14400","created_at":"2017-09-25 20:40:20","updated_at":"2017-09-25 20:40:20"},
{"id":2,"coin_name":"bitcoin","user_id":1,"buy_price_usd":"12.00000","buy_price_btc":"14.00000","created_at":"2017-09-25 21:29:18","updated_at":"2017-09-25 21:29:18"},
{"id":3,"coin_name":"ethereum-classic","user_id":1,"buy_price_usd":"33.45000","buy_price_btc":"3.00000","created_at":"2017-09-25 21:49:50","updated_at":"2017-09-25 21:49:50"},{"id":4,"coin_name":"lisk","user_id":1,"buy_price_usd":"23.33000","buy_price_btc":"0.50000","created_at":"2017-09-25 21:51:26","updated_at":"2017-09-25 21:51:26"}]}
Then I have this component: Coin:
Vue.component('coin',{
data() {
return {
thisCoin: this.coin,
coinData: {
name: "",
slug: "",
image: "https://files.coinmarketcap.com/static/img/coins/32x32/.png",
symbol: "",
price_eur: "",
price_usd: "",
}
}
},
props: ['coin'],
template: `
<li class="coin">
<div class="row">
<div class="col col-lg-2 branding">
<img :src="this.coinData.image">
<small>{{this.coinData.name}}</small>
</div>
<div class="col col-lg-8 holdings">
<p>11.34 <span>{{this.coinData.symbol}}</span></p>
<p>$ {{this.coinData.price_usd * 3}}</p>
</div>
<div class="col col-lg-2 price">
<p>{{this.coinData.price_usd}}</p>
</div>
<div class="edit col-lg-2">
<ul>
<li>
<p>Edit</p>
</li>
<li>
<p>Delete</p>
</li>
</ul>
</div>
</div>
</li>
`,
methods: {
getCoin: function(name) {
self = this;
axios.get('api/coin/' + name).then(function (response) {
console.log(response);
self.coinData.name = response.data.coin.name;
self.coinData.price_usd = response.data.coin.price_usd;
self.coinData.price_eur = response.data.coin.pride_eur;
})
.catch(function (error) {
console.log(error);
});
}
},
mounted() {
this.getCoin(this.coin);
}
})
On url 'api/coin/{name}' I get this data:
{"coin":{"slug":"lisk","name":"Lisk","symbol":"LSK","rank":14,"price_usd":"6.15510","price_btc":"0.00156","24h_volume_usd":null,"market_cap_usd":"99999.99999","available_supply":"99999.99999","total_supply":"99999.99999","percent_change_1h":"0.10000","percent_change_24h":"6.78000","percent_change_7d":"-5.64000","last_updated":1506385152,"price_eur":"5.19166","24h_volume_eur":null,"market_cap_eur":"99999.99999","created_at":"2017-09-25 00:06:27","updated_at":"2017-09-26 00:23:02"}}
But as of right now, only the last component gets the details filled (name, price_usd, price_eur, etc.), but not the first 3 ones.
Here is the video of loading the page: https://gfycat.com/gifs/detail/BreakableSlimHammerkop - You can see it goes trough all the coins it should pass to the first three components. What am I doing wrong?
The problem is because the self variables you've declared in your getStats() and getCoin() methods are bound to the window object, and not properly scoped to each method. A simple fix would be to replace each self = this; statement with var self = this;.
Let me explain.
When you declare a new variable without using the var, let or const keywords, the variable is added to the global scope object, which in this case is the window object. You can find more info about this JavaScript behaviour here https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/ or check the var docs on MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)
You shouldn't directly reassign data value because Vue cannot detect property additions and rerender view. You should do it via Vue.set or this.$set like this:
self.$set(self.coinData, 'name', response.data.coin.name)
...
I have a component where I am uploading a video file, everything works fine on my local machine, and it used to work fine on the production server, Namechap is where I host the project, until only recently I have done some work and made changes, that I saw it doesn't work on the production server anymore.
I am using Vue v. 1.0.28, and this is the upload component, where in the fileInputChange() method I am posting form data, to the /upload endpoint, which on the production server for some reason I can't read in the backend:
<template>
<div class="card-content col-md-10 col-md-offset-1">
<div v-if="!uploading">
<div class="col-md-12 Image-input__input-wrapper">
Upload video
<input type="file" name="video" id="video" #change="fileInputChange" class="Image-input__input" accept="video/*">
</div>
</div>
<div class="alert alert-danger video-upload-alert" v-if="failed">Something went wrong. Please check the video format and try again. If you need any help please contact our <a>support service.</a></div>
<div id="video-form">
<div class="alert alert-info" v-if="uploading && !failed && !uploadingComplete">
Please do not navigate away from this page, until the video has finished uploading. Your video will be available at {{ $root.url }}/videos/{{ uid }}, once uploaded.
</div>
<div class="alert alert-success" v-if="uploading && !failed && uploadingComplete">
Upload complete. Video is now processing. Go to your videos.
</div>
<div class="progress" v-if="uploading && !failed && !uploadingComplete">
<div class="progress-bar" v-bind:style="{ width: fileProgress + '%' }"></div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<label for="title" class="control-label">Title</label>
<input type="text" class="form-control" v-model="title">
</div>
<!--
<div class="col-md-12 form-group">
<label for="visibility" class="control-label">Visibility</label>
<select class="form-control" v-model="visibility">
<option value="private">Private</option>
<option value="unlisted">Unlisted</option>
<option value="public">Public</option>
</select>
</div>
-->
</div>
<div class="row">
<div class="col-md-12 form-group">
<label for="description" class="control-label">Description</label>
<textarea class="form-control" v-model="description"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<button type="submit" class="btn btn-submit" #click.prevent="update">Save</button>
</div>
</div>
<div class="row">
<div class="col-md-12 form-group">
<span class="help-block pull-right">{{ saveStatus }}</span>
</div>
</div>
</div>
</template>
<script>
function initialState (){
return {
uid: null,
uploading: false,
uploadingComplete: false,
failed: false,
title: null,
link: null,
description: null,
visibility: 'private',
saveStatus: null,
fileProgress: 0
}
}
export default {
data: function (){
return initialState();
},
methods: {
fileInputChange() {
this.uploading = true;
this.failed = false;
this.file = document.getElementById('video').files[0];
var isVideo = this.isVideo(this.file.name.split('.').pop());
if (isVideo) {
this.store().then(() => {
var form = new FormData();
form.append('video', this.file);
form.append('uid', this.uid);
this.$http.post('/upload', form, {
progress: (e) => {
if (e.lengthComputable) {
this.updateProgress(e)
}
}
}).then(() => {
this.uploadingComplete = true
this.uploading = false
}, () => {
this.failed = true
this.uploading = false
});
}, () => {
this.failed = true
this.uploading = false
})
}
else {
this.failed = true
this.uploading = false
}
},
isVideo(extension) {
switch (extension.toLowerCase()) {
case 'm4v':
case 'avi':
case 'mpg':
case 'mp4':
case 'mp3':
case 'mov':
case 'wmv':
case 'flv':
return true;
}
return false;
},
store() {
return this.$http.post('/videos', {
title: this.title,
description: this.description,
visibility: this.visibility,
extension: this.file.name.split('.').pop()
}).then((response) => {
this.uid = response.json().data.uid;
});
},
update() {
this.saveStatus = 'Saving changes.';
return this.$http.put('/videos/' + this.uid, {
link: this.link,
title: this.title,
description: this.description,
visibility: this.visibility
}).then((response) => {
this.saveStatus = 'Changes saved.';
setTimeout(() => {
this.saveStatus = null
}, 3000)
}, () => {
this.saveStatus = 'Failed to save changes.';
});
},
updateProgress(e) {
e.percent = (e.loaded / e.total) * 100;
this.fileProgress = e.percent;
},
}
}
</script>
The problem is that on upload in my controller in the store function the request object is empty on the production server, which I got when I did dd($request->all()). Then it fails to find the video, in the network inspector I get a 404 error, which is returned by firstOrFail() method, because it can't find the Video model, since it is missing $request->uid.
No query results for model [App\Video].
This is the controller:
class VideoUploadController extends Controller
{
public function index()
{
return view('video.upload');
}
public function store(Request $request)
{
$player = $request->user()->player()->first();
$video = $player->videos()->where('uid', $request->uid)->firstOrFail();
$request->file('video')->move(storage_path() . '/uploads', $video->video_filename);
$this->dispatch(new UploadVideo(
$video->video_filename
));
return response()->json(null, 200);
}
}
I am not sure what is going on, since on inspecting the network tab in the console I am sending a request payload that looks like this:
------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="video"; filename="Football Match Play.mp4" Content-Type: video/mp4
------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="uid"
159920a7878cb2
------WebKitFormBoundarywNIkEqplUzfumo0A--
It is really frustrating since I have no idea how to fix this, when everything is fine on my local machine, not sure what is wrong on the production server and how to fix it?
Update
It started working again on its own, since I have created a ticket in the Namecheap support service, all of sudden after one day, it started working again, I haven't done any changes, and on asking the Namecheap support I have got an answer from them that they haven't done any changes either, so I have no idea what went wrong, which is still a bit frustrating since I would like to avoid that in the future, but at least everything is working now again as it should.
Laravel's $request->all() method only pulls from the input, thus in the store() method of VideoUploadController $request->all() return empty object.
In your script, it calls this.store() after checked the isVideo is true while file changes. As a result, there isn't a uid parameter or video parameter.
Controller (snippet, as its very large):
function loadImages() {
if (Authentication.loggedIn()) {
Images.get(function(s) {
$scope.images = s.images;
},
function(f) {} );
}
}
$scope.loginOnSubmit = function() {
Authentication.login($scope.model.login).then(
function(okResponse) {
WizardHandler.wizard().next();
loadImages()
},
function(failureResponse) {
// TODO: Alert use with the failure
}
);
};
loadImages();
View:
<wz-step title="{{ strings.images.bullet }}">
<div class="row">
<div class="col-md-10">
<h1 ng-bind="strings.images.title"></h1>
<p ng-bind="strings.images.description"></p>
<div ui-grid="{ data: images }" class="imagesGrid"></div>
</div>
</div>
</wz-step>
using angular-wizard wizard plugin (wz-step directive..).
I've looked over the web, thought its related to the $digest, etc.
I've tried using $timeout(...) to overcomes this, no go.
what happens is, the data doesnt show up in the view, but when I refresh the page, then its there, or when I even re-size the page, it appears.