Async data change when using server side render in Vue - javascript

I want to use Vue in server side rendering, but the content data inside template have to request from the other CMS server.
<template>
<h1>{{ content.heading }}</h1>
</template>
<script>
export default {
data() {
return {
content: {
heading: ''
}
}
},
created() {
axios
.get(CONTENT_RESOURCE)
.then(content => this.content = content);
}
}
</script>
Due to axios.get is an async request, server will send empty content before request complete.
Use curl to request content:
curl 'URL';
# It got <h1></h1>,
# but I want <h1>Something here</h1>
How do I make sure it can render with CMS content data in server side?

According to vue-hackernews-2.0 example, src/server-entry.js will detect preFetch function in current route component.
So, just add a preFetch function in current route component and save the data to Vuex store.
<template>
<h1>{{ content.heading }}</h1>
</template>
<script>
const fetchContent = store =>
axios
.get(CONTENT_RESOURCE)
.then(content => store.dispatch('SAVE_CONTENT', content));
export default {
computed: {
content() {
return this.$store.YOUR_CONTENT_KEY_NAME
}
},
preFetch: fetchContent, // For server side render
beforeCreate() { // For client side render
fetchContent(this.$store);
}
}
</script>

You have to make following changes in the code:
var demo = new Vue({
el: '#demo',
data:{
content : {heading: ""}
},
beforeMount() {
var self = this;
setTimeout(function(){
self.content.heading = "HI"
}, 100)
}
})
Here is a working fiddle.

Related

Calling VueJS Components after login from an external script

I have a VueJS Program where I want to provide a login. Thus this login is used one multiple platforms, it is loaded via an external server (See: mounted). It is loaded, puts elements into the mainlogin-div creating a login view and when the user clicks Login it performs a http login request to another server. When the login is successful, the external script calls done(result) or error(result) whether the login was a success or not.
But now of course how can I go back to calling Vue-stuff from there? I cant just do like this.$router.push("/coolPath") or call a defined method like loggedIn to make a toast or what not... How is this implemented?
<template>
<div id="main">
<div id="mainlogin"></div>
<script type="application/javascript">
function done(result){
console.log(result);
loggedIn(result, true)
}
function error(result) {
console.log(result)
loggedIn(result, false)
}
</script>
</div>
</template>
<script>
const Toast = require("#/toast/toast")
export default {
name: "Login",
components: {},
data() {
return {
isAuthenticated: false
}
},
beforeMount() {
let loginScript = document.createElement('script')
loginScript .setAttribute('src', 'http://**.***.***.***/index_krass.js')
document.body.appendChild(loginScript )
},
methods: {
loggedIn: function (result, success){
Toast.toast(result.message, success)
}
}
}
</script>
<style>
...
</style>
Error is always "this." is not defined or loggedIn(...) is not defined...
Maybe its some race condition where VueJS needs to be loaded first or the other way around?
Instead of the <script> block in the template, just set the global functions up on window as arrow functions to capture the Vue instance context:
export default {
beforeMount() {
window.done = result => this.loggedIn(result, true)
window.error = result => this.loggedIn(result, false)
let loginScript = document.createElement("script")
loginScript.setAttribute("src", ".../index_krass.js")
document.body.appendChild(loginScript)
},
destroyed() {
window.done = window.error = undefined
},
}
demo

VUE JS Dynamic background image after axios request

I'm trying to display a background image that it's path needs to be loaded through an API.
The plan is: From a main grid of links, click one and display a background image according to the one clicked.
As of now I am using axios to query my API which sends the data I need. I have the following script part on my component.
<script>
import axios from 'axios'
const lhost = require("#/config/global").host;
let championData;
export default {
name: 'IndividualChampion',
props: {
},
data: () => ({
champions: [],
verPersonagem: mdiMovieOpen,
}),
computed: {
},
created: async function() {
try {
let champion = this.$route.fullPath.split('/')[2];
let response = await axios.get(lhost + "/champion/" + champion + '/full');
championData = response.data
console.log(championData)
let background = '#/assets' + championData['skins']['0']['splash'].replace('.png','.jpg')
}
catch (e) {
return e;
}
},
methods: {
}
}
</script>
And this is my HTML
<template>
<div :style="{ backgroundImage: `url(${require(background)})` }">
</div>
</template>
I have searched but can't seem to find a solution in which the background image is loaded and, when loaded, is presented.
Can someone help?
Judging from your use of '#/assets', you seem to be using webpack with a resolve alias. The expression require(background) is not enough for webpack to determine what files it needs to add to your bundle.
You can help Webpack by specifying the directory that you want to load your file from. All you have to do is take out '#/assets/' from the background variable and use it directly in the require call so that Webpack can see it.
<template>
<div v-if="background" :style="{ backgroundImage: `url(${require('#/assets/' + background)})` }">
</div>
</template>
<script>
import axios from 'axios'
const lhost = require("#/config/global").host;
let championData;
export default {
name: 'IndividualChampion',
props: {
},
data: () => ({
champions: [],
verPersonagem: mdiMovieOpen,
background: ''
}),
computed: {
},
created: async function() {
try {
let champion = this.$route.fullPath.split('/')[2];
let response = await axios.get(lhost + "/champion/" + champion + '/full');
championData = response.data
console.log(championData)
this.background = championData['skins']['0']['loading'].replace('.png','.jpg')
}
catch (e) {
return e;
}
},
methods: {
}
}
</script>
It will bundle every possible file inside the directory, though.
You can read more about it here: https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import

Vuejs - Render raw html links to <router-link>

I;m new to VueJS and I'm making some weird experiments. I build a backend service using python/flask and this backend provide me a string of html code with many tags inside, I want to render this inside my Vue app, I have a method for calling the backend that looks like this:
async execute (method, resource, data) {
return client({
method,
url: resource,
data: data
}).then(async req => {
return req.data.html
})
},
callBack (id) {
console.log(id)
return this.execute('post', '/content/', {body: { 'id': id }})
}
And in the .vue file I have:
export default {
data () {
return {
loading: false,
launch: [],
html: 'none',
page: this.$route.params.article
}
},
beforeMount () {
console.log('beforeee')
this.html = api.callBack(this.page)
},
methods: {
async launch () {
this.launch = ''
this.html = await api.callBack(this.page)
}
}
}
so when I call the launch function it populates this.html, and this html variable lives in a v-html.Everything seems to work i get the html and render it in de container but the links are broken, the links should point at the same app something like #/test/linkvalue, but as they are tags, and in vue you have to use they doesn't work.
There is a way to achieve this "dynamic re route" or I'm doing something too weird?
The links are plenty, since they are scraped from the web, so manually parsing is not an option.
JSFiddle
Thanks in advance for your help
Also, you shouldn't return the raw html. Just return the paths for the routes and then loop the paths and create links that way.
You can use the v-html vue directive to output raw html.
https://jsfiddle.net/eywraw8t/66262/
new Vue({
el: "#app",
data: {
rawhtml: "<h1 style='color: red;'>Hi, I am raw html.</h1>"
},
methods: {
}
})
<div id="app">
<div v-html="rawhtml"></div>
</div>

Emitting global events from websocket listener

I want to contribute to a project - it's written in Vue, and I am a beginner in Vue.
I have two components - Setup and MainApp
Both will need to update some state based on different messages from the websocket. Some websocket messages will affect the former, some the latter.
Vue doesn't know services, so I thought I'd just create a custom component, with empty <template>. instantiate the websocket there and then issue an this.emit() every time a new message occurs in the listener.
Both other components would listen to the emits and would be able to react.
Unfortunately, I can't get the websocket component to work.
main.js:
import Ws from './WsService.vue';
//other imports
const routes = [
//routes
]
const router = new VueRouter({
routes // short for `routes: routes`
})
const app = new Vue({
router
}).$mount('#app')
//I thought this to be the way to instantiate my webSocket service:
const WsService = new Vue({
el: '#WsService',
components: { Ws }
});
index.html
<body>
<div id="app">
<div id="WsService"></div>
<router-link to="/setup">Setup</router-link>
<router-link to="/main-app">Main App</router-link>
<router-view></router-view>
</div>
<script src="/dist/demo-app.js"></script>
</body>
the websocket "service":
<template>
</template>
<script>
const PORT_LOCAL = 9988;
var ws = new WebSocket("ws://localhost:" + PORT_LOCAL);
ws.onopen = function() {
ws.send('{"jsonrpc":"2.0","id":"reg","method":"reg","params":null}');
};
ws.onerror = function(e) {
console.log("error in WebSocket connection!");
console.log(e);
};
export default {
data() {
return {
}
},
created() {
var self = this;
ws.onmessage = function(m) {
var msg = JSON.parse(m.data);
switch(msg.id) {
// result for address request
case "reg":
self.$emit("reg_received", msg.result);
break;
case "send":
self.$emit("send_received", msg.result);
break;
case "subscribe":
self.$emit("subscribe_received", msg.result);
break;
default:
console.log(msg);
break;
}
}
},
methods: {
},
send(id, method, params) {
ws.send('{"jsonrpc":"2.0","id":"' + id + '","method":"' + method + '","params":null}');
}
}
}
</script>
Send for example from main app (this seems to work):
import WsSvc from './WsService.vue';
export default {
data() {
//
},
subscribe() {
let jsonrpc = "the jsonrpc string";
WsSvc.send(jsonrpc);
}
}
Listening to emit:
export default {
data() {
//
},
created() {
this.$on("reg_received", function(result){
//do smth with the result
});
}
}
Wit this configuration, the created hook actually never gets called - and thus I'll never hit the onmessage listener. The reason to have a custom component I thought was that I would have access to the emit function.
It feels I am making it more complicated than it should be but I haven't managed yet to get it right. The solution doesn't need to follow this approach.
There's no need for a socket specific component in this case. What I have done in the past on a couple projects is implement an API or store object that handles the socket messages and then import that API or store into the components that need it. Also in a similar answer, I show how to integrate a WebSocket with Vuex.
Here is an example that combines the concept of using Vue as an event emitter with a web socket that can be imported into any component. The component can subscribe and listen to the messages it wants to listen to. Wrapping the socket in this way abstracts the raw socket interface away and allows users to work with $on/$off subscriptions in a more typically Vue fashion.
Socket.js
import Vue from "vue"
const socket = new WebSocket("wss://echo.websocket.org")
const emitter = new Vue({
methods:{
send(message){
if (1 === socket.readyState)
socket.send(message)
}
}
})
socket.onmessage = function(msg){
emitter.$emit("message", msg.data)
}
socket.onerror = function(err){
emitter.$emit("error", err)
}
export default emitter
Here is an example of that code being used in a component.
App.vue
<template>
<ul>
<li v-for="message in messages">
{{message}}
</li>
</ul>
</template>
<script>
import Socket from "./socket"
export default {
name: 'app',
data(){
return {
messages: []
}
},
methods:{
handleMessage(msg){
this.messages.push(msg)
}
},
created(){
Socket.$on("message", this.handleMessage)
},
beforeDestroy(){
Socket.$off("message", this.handleMessage)
}
}
</script>
And here is a working example.
Hey this should work for you better and easy
This my example with .vue file
yourVueFile.Vue
<template>
// key in your template here
</template>
<script>
export default {
//use the created() option to execute after vue instance is created
created() {
let ws = new WebSocket("yourUrl");
ws.onopen = e => {
ws.send(
JSON.stringify({ your json code })
);
ws.onmessage = e => {
let data = JSON.parse(e.data);
// the this.$data get your data() options in your vue instance
this.$data.dom = data;
};
};
},
data() {
return {
dom: core
};
},
methods: {
}
};
</script>

Vue.js - Global Data from AJAX Call

I'm giving Vue.js a try and so far I'm loving it because it's much simpler than angular. I'm currently using vue-router and vue-resource in my single page app, which connects to an API on the back end. I think I've got things mostly working with a the primary app.js, which loads vue-router and vue-resource, and several separate components for each route.
Here's my question: How do I use props to pass global data to the child components when the data is fetched using an asynchronous AJAX call? For example, the list of users can be used in just about any child component, so I would like the primary app.js to fetch the list of users and then allow each child component to have access to that list of users. The reason I would like to have the app.js fetch the list of users is so I only have to make one AJAX call for the entire app. Is there something else I should be considering?
When I use the props in the child components right now, I only get the empty array that the users variable was initialized as, not the data that gets fetched after the AJAX call. Here is some sample code:
Simplified App.js
var Vue = require('vue');
var VueRouter = require('vue-router')
Vue.use(VueRouter);
var router = new VueRouter({
// Options
});
router.map({
'*': {
component: {
template: '<p>Not found!</p>'
}
},
'/' : require('./components/dashboard.js'),
});
Vue.use(require('vue-resource'));
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
users: [],
};
},
methods: {
fetchUsers: function() {
this.$http.get('/api/v1/users/list', function(data, status, response) {
this.users = data;
}).error(function (data, status, request) {
// handle error
});
}
}
});
router.start(App, '#app')
Simplified app.html
<div id="app" v-cloak>
<router-view users = "{{ users }}">
</router-view>
</div>
Simplified dashboard.js
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
},
};
When dashboard.js gets run, it prints an empty array to the console because that's what app.js initializes the users variable as. How can I allow dashboard.js to have access to the users variable from app.js? Thanks in advance for your help!
p.s. I don't want to use the inherit: true option because I don't want ALL the app.js variables to be made available in the child components.
I believe this is actually working and you are being misled by the asynchronous behavior of $http. Because your $http call does not complete immediately, your console.log is executing before the $http call is complete.
Try putting a watch on the component against users and put a console.log in that handler.
Like this:
module.exports = {
component: {
ready: function() {
console.log(this.users);
},
props: ['users'],
watch: {
users: {
handler: function (newValue, oldValue) {
console.log("users is now", this.users);
},
deep: true
}
}
}
};
In the new version of Vue 1.0.0+ you can simply do the following, users inside your component is automatically updated:
<div id="app" v-cloak>
<router-view :users="users"></router-view>
</div>

Categories

Resources