(using laravel 5.4 and vue.js 2x)
I use router.beforeEach() to handle authorization in my app.
But, my function router.beforeEach() is only loading on the moment after login. If I refresh the page the function isn't called again.
Here is my code:
import router from './routes.js';
require('./bootstrap');
const app = new Vue({
el: '#app',
router,
});
router.beforeEach((to,from,next) => {
if(to.meta.requiresAuth){
authUser = '';
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
if(authUser && authUser.access_token){
next()
}else{
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
}
next()
})
The full project: https://github.com/jrpikong/wingding
The way to check if the current route requires authentication is documented here: https://router.vuejs.org/en/advanced/meta.html
Change your beforeEach method to this:
router.beforeEach((to,from,next) => {
if(to.matched.some(record => record.meta.requiresAuth)){
// do something
}
next();
}
As mentioned in the Documentation,
All route records matched by a route are exposed on the $route
object (and also route objects in navigation guards) as the
$route.matched Array. Therefore, we will need to iterate over
$route.matched to check for meta fields in route records.
router.beforeEach((to,from,next) => {
if(to.matched.some(record => record.meta.requiresAuth)){
authUser = '';
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
if(authUser && authUser.access_token){
next()
}else{
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
}
next()
})
I just switched the position of router.beforeEach to be before
const app = new Vue({
el: '#app',
router,
});
So, in full, the code looks like this:
import router from './routes.js';
require('./bootstrap');
router.beforeEach((to,from,next) => {
if(to.matched.some(record => record.meta.requiresAuth)){
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
if(authUser && authUser.access_token){
next()
}else{
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
}
next()
})
const app = new Vue({
el: '#app',
router,
});
Related
I'm having trouble with GraphQL Subscriptions in React-Apollo. The issue is that when I create a subscription it gets stuck on loading forever. Despite that, the GraphQL Subscriptions work in gql-playground so it seems like a front-end issue. I've followed the documentation closely to match it, but the subscription still returns undefined in the console and is loading.
Here is how the WebSockets with Subscriptions is set up in index.js
// Link for HTTP Requests
const httpLink = new HttpLink({
uri: 'http://localhost:8080/api'
});
// Link for Websocket Links
const wsLink = new WebSocketLink({
uri: 'ws://localhost:8080/api',
options: {
reconnect: true
}
});
// Split Function takes the operation to execute, and reuturns the Websocket Link or HTTP Link depending on a boolean value
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
cosnt
const client = new ApolloClient({
connectToDevTools: true,
cache: new InMemoryCache(),
link: splitLink,
})
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);
and this is the WristbandSubscriptions component I created to test the subscriptions
const WristbandSubscriptions = () => {
const NEW_WRISTBAND_ADDED = gql`
subscription {
newWristbandAdded {
id
tic
active
name
dateOfBirth
onOxygen
pregnant
child
key
department
}
}`
;
const { data, loading, error } = useSubscription(NEW_WRISTBAND_ADDED);
useEffect(()=> {
console.log(data);
},[data])
console.log(error);
console.log("Loading: " + loading);
return (
<div>
<h1>{data}</h1>
</div>
)
}
I wrote a Sapper app with session management following the RealWorld example:
polka()
.use(bodyParser.json())
.use(session({
name: 'kidways-app',
secret: 'conduit',
resave: false,
saveUninitialized: true,
cookie: {
maxAge: 31536000
},
store: new FileStore({
path: 'data/sessions',
})
}))
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
pdfMiddleware,
sapper.middleware({
session: req => ({
token: req.session && req.session.token
})
})
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
Then on my _layout.sevlte:
<script context="module">
export async function preload({ query }, session) {
console.log('preload', session)
return {
// ...
};
}
</script>
<script>
import { onMount, createEventDispatcher } from 'svelte';
import { Splash } from 'project-components';
import * as sapper from '#sapper/app';
import { user } from '../stores';
import client from '../feathers';
const { session } = sapper.stores();
onMount(async () => {
try {
await client.reAuthenticate();
const auth = await client.get('authentication');
user.set(auth.user);
$session.token = 'test';
} catch (e) {
} finally {
loaded = true;
}
});
console.log($session)
</script>
<h1>{$session.token}</h1>
This work on client side rendering, but the token is still undefined on preload, making my SSR template rendering broken.
What did I missed?
When a page renders, session is populated according to the return value of the function you specified here:
sapper.middleware({
session: req => ({
token: req.session && req.session.token
})
})
So while the client may have an up-to-date token, it won't take effect on page reload unless you somehow persist the token to the server in such a way that the session middleware knows about it.
Typically you'd achieve this by having a server route, like routes/auth/token.js or something...
export function post(req, res) {
req.session.token = req.body.token;
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end();
}
...and posting the token from the client:
onMount(async () => {
try {
await client.reAuthenticate();
const auth = await client.get('authentication');
user.set(auth.user);
await fetch(`auth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
});
// writing to the session store on the client means
// it's immediately available to the rest of the app,
// without needing to reload the page
$session.token = 'test';
} catch (e) {
} finally {
loaded = true;
}
});
I have a class with a variable in the constructor, I try to make a ajax request and update a variable. But the variable is not in my scope. what can I do?
import Axios from "axios";
class ajaxTest{
constructor() {
this.resultOfAjax=" Mein Test";
}
set(id){
return new Promise(function(resolve, reject) {
Axios.get('/api/v1/ajax/'+id)
.then(function (response) {
this.resultOfAjax=response.data;
resolve(200);
console.log("Test");
});
})
}
}
export default ajaxTest;
Also I try to update my loadingCircle variable, but it is not working. I think it is the same mistake. Is this right?
const app = new Vue({
el: '#app',
data: {
loadingCircle: true,
ajaxTest: new ajaxTest()
},
methods: {
handleClick: function(id) {
console.log("Das ist ein Test die ID ist:"+id);
this.ajaxTest.set(id).then( function(status){
console.log("Status "+status);
this.loadingCircle=false;
});
}
},
components: {
examplecomponent
}
});
If you use a function, then the this inside it is not the same as the this outside. The solution is to use the fat arrow notation instead.
const app = new Vue({
el: '#app',
data: {
loadingCircle: true,
ajaxTest: new ajaxTest()
},
methods: {
handleClick: (id) => {
console.log("Das ist ein Test die ID ist:"+id);
this.ajaxTest.set(id).then( (status) => {
console.log("Status "+status);
this.loadingCircle=false;
});
}
},
components: {
examplecomponent
}
});
I'm utilising the following API for a World Cup Laravel app - http://api.football-data.org/docs/v1/index.html#_fixture
This information brings me back today's fixture's as I'm using this code (config just holds my API key):
const todaysMatches = new Vue({
el: '#todaysMatches',
data: {
todaysMatches: [],
flags: []
},
methods: {
loadData: function () {
axios.get("http://api.football-data.org/v1/competitions/467/fixtures/?timeFrame=p1", config)
.then(response => {this.todaysMatches = response.data});
}
},
mounted: function () {
this.loadData();
}
});
This brings back the following data sctructure:
Inside each fixture you get an array of _links which you can see in the below screenshot:
Now, what I would like to do is query both the awayTeam api and the homeTeam api because they each have an endpoint of crestUrl which returns the country's flag.
You can see that inside my data I've set an array prop called flags so I was thinking of running additional calls inside my loadData method and populate that array for each fixture, but I don't think that's a clean way of doing it.
Can anyone suggest the best way to approach this?
I have used async/await pattern to achieve your requirement as below:
loadData: async function() {
const response = await axios.get(
"http://api.football-data.org/v1/competitions/467/fixtures/?timeFrame=p1",
config
);
this.todaysMatches = response.data;
let arr = this.todaysMatches.fixtures.map(fixture => {
const _links = fixture._links;
return [
axios.get(_links.awayTeam.href, config),
axios.get(_links.homeTeam.href, config)
];
});
arr.forEach(async item => {
const away = await item[0];
const home = await item[1];
this.flags.push({
awayFlag: away.data.crestUrl,
homeFlag: home.data.crestUrl
});
});
}
Explaination:
After fetching todaysMatches a new array arr is created which consists of promises returned by get request to the team's url [[getAwayTeamInfo, getHomeTeamInfo], [getAwayTeamInfo, getHomeTeamInfo], [getAwayTeamInfo, getHomeTeamInfo],...]
We loop through this and await on the promise to get the crestUrl
This crestUrl is pushed into flags array as an object
{
awayFlag: away.data.crestUrl,
homeFlag: home.data.crestUrl
}
Update
Adding the flag urls directly to the this.todaysMatches.fixtures array
loadData: async function() {
const response = await axios.get(
"http://api.football-data.org/v1/competitions/467/fixtures/?timeFrame=p1",
config
);
this.todaysMatches = response.data;
const fixtures = this.todaysMatches.fixtures;
let arr = fixtures.map(fixture => {
const _links = fixture._links;
return [
axios.get(_links.awayTeam.href, config),
axios.get(_links.homeTeam.href, config)
];
});
arr.forEach(async (item, index) => {
const away = await item[0];
const home = await item[1];
this.$set(fixtures, index, {
...fixtures[index],
awayFlag: away.data.crestUrl,
homeFlag: home.data.crestUrl
});
});
}
I need to pass an arguments in methods using ajax axios.
var app = new Vue({
el: '#app',
data: {
urlAdmission:
admissions: [
{ name : 'asdf'},
{ name : 'sd'}
]
},
mounted: function(){
this.allAdmissions()
},
methods: {
allAdmissions: _.debounce( function(){
var app = this
axios.get('http://localhost/school/api/hello')
.then( function(response ){
app.admissions = response.data.admissions
})
.catch( function(error){
console.log(error)
})
})
}
});
As you can see in mounted I call the methods this.allAdmissions() I need to pass an argument so that I can reuse the function. For example this.allAdmissions('http://localhost/school/api/hello'). Then use it in axios.get('url'). Thanks
It looks like what you're trying to do is make a function that can accept a url and bind the results of the url to a variable value in your data. Here is how you might do that.
methods: {
allAdmissions: _.debounce(function(url, value){
axios.get(url)
.then(function(response){
this[value] = response.data.admissions
}.bind(this))
.catch(function(error){
console.log(error)
})
})
}
Then, if you call that method like this,
this.allAdmissions('http://localhost/school/api/admissions', "admissions")
allAdmissions will set the admissions property on your data to the result of your call. This works if you always want to use response.data.admissions because you hardcoded that. If you wanted that to be variable as well, you might pass in a third value like so
methods: {
getSomeData: _.debounce(function(url, value, responseValue){
axios.get(url)
.then(function(response){
this[value] = response.data[responseValue]
}.bind(this))
.catch(function(error){
console.log(error)
})
})
}
In case some will need multiple ajax request. Here is an example.
var app = new Vue({
el: '#app',
data: {
value: '',
admissions: [],
schoolyear: []
},
created: function(){
this.ajaxAll()
},
methods: {
ajaxAll: _.debounce( function(){
var app = this
var admissions = 'admissions'
var schoolYear = 'schoolyear'
axios.all([this.getAllData('http://localhost/school/api/admissions', 'admissions'), this.getAllData('http://localhost/school/api/schoolyear', 'schoolyear')]);
}),
getAllData: function(url, value){
var app = this
return axios.get(url)
.then(function(response){
app[value] = response.data[value]
console.log(response.data.admissions)
})
}
}
})
Credit to #Bert Evans.