I have this get Request
router.get('/', async (req, res) => {
//I connect to the DB and i return the collection with Posts
const posts = await loadPostsCollection();
res.send(await posts.find({ "tmima": { "$eq": 'andriko' } }).toArray());
});
Everything works Fine as it is.. the problem is that i want to find the posts dynamicaly and not having this 'andriko' there..
I tried this { "tmima": { "$eq": req.body.params } } but its now working... the weird thing is that with req.body.params it shows all the other posts except those that have tmima: 'andriko'
This 'tmima' value comes from the dynamic route so thats why i need this req.body.params or something similar, to show the posts based on the routing.
Thx in advance!
EDIT!! FronteEnd section included!!
router.js
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/katigories/:tmima",
name: "katigories",
props: true,
component: () => import("../views/katigories.vue"),
children: [
{ path: 'anakoinoseis', name: 'anakoinoseis', props: true, component: () => import("../views/Ypokatigories/Anakoinoseis.vue")},
{ path: 'roster', name: 'roster', props: true, component: () => import("../views/Ypokatigories/RosterView.vue")},
{ path: 'vathmologia', name: 'vathmologia', props: true, component: () => import("../views/Ypokatigories/Vathmologia.vue")}
]
}
axios.js
import axios from 'axios'
const url = 'http://localhost:5000/api/posts/';
class AnnouncementsService {
//GET Annaouncements
static getPosts() {
return new Promise ((resolve,reject) => {
axios.get(url).then((res) => {
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
);
})
.catch((err)=> {
reject(err);
})
});
}
Anakoinoseis.vue
<script>
import AnnouncementsService from '../../store/axios'
export default {
name: 'Announcements',
props: ['tmima'],
data() {
return {
posts: [],
text: '',
title: ''
}
},
async created() {
try {
this.posts = await AnnouncementsService.getPosts();
await console.log(this.tmima)
}catch(err){
this.error = err.message;
}
},
</script>
Related
I have to following situation with vue2/vuex; Let's say I have a users module where I store all users I fetched from my api.
I use this module to populate, for example, dropdown lists containing all users.
Now I also have a users page, but this page has the option to filter, paginate users etc. This happens serverside, so the module will be updated with the new list of (filtered) users.
Should I create two separate modules for both usecases (usersOptions and usersView)? To me it would seem more logical to create two instances of the user store, but apparently that's not possible with Vuex. How would you handle a situation like this?
Here is an example of the my users module:
import UserRepository from '#/repositories/UserRepository';
export default {
namespaced: true,
state: {
loading: false,
users: [],
},
getters: {
isLoading(state) {
return state.loading;
},
data(state) {
return state.users;
},
options: (state) => (value = 'id', label = 'name') => state.users.map(
(user) => ({ value: user[value], label: user[label] }),
),
},
mutations: {
SET_LOADING(state, payload) {
state.loading = payload;
},
SET_DATA(state, payload) {
state.users = payload;
},
},
actions: {
fetch({ commit }) {
return new Promise((resolve, reject) => {
commit('SET_LOADING', true);
UserRepository.index({ limit: 0 })
.then((response) => {
const users = response.data.data;
commit('SET_DATA', users);
resolve(response);
})
.catch((error) => {
reject(error);
})
.finally(() => {
commit('SET_LOADING', false);
});
});
},
},
};
Intuitively, I'd do something like that. Haven't tested it but it's probably not ready to use yet.
import UserRepository from '#/repositories/UserRepository';
export default {
namespaced: true,
state: {
loading: false,
users: [],
},
getters: {
isLoading(state) {
return state.loading;
},
data(state) {
return state.users;
},
usersView() {
return state.users.view;
},
usersOptions() {
return state.users.options;
},
options: (state) => (value = 'id', label = 'name') => state.users.map(
(user) => ({ value: user[value], label: user[label] }),
),
},
mutations: {
SET_LOADING(state, payload) {
state.loading = payload;
},
SET_DATA(state, key, payload) {
state.users[key] = payload;
},
},
actions: {
fetch({ commit }, params) {
return new Promise((resolve, reject) => {
commit('SET_LOADING', true);
UserRepository.index(params)
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
reject(error);
})
.finally(() => {
commit('SET_LOADING', false);
});
});
},
fetchOptions() {
this.dispatch('fetch', { limit: 0 }).then((users) {
commit('SET_DATA', 'options', users);
})
},
fetchView() {
this.dispatch('fetch', { limit: 15, page: 1 }).then((users) {
commit('SET_DATA', 'view', users);
})
},
},
};
Two stores its never the soultion in my opinion,
try to seperate to 2 modules.
find more here: https://vuex.vuejs.org/guide/modules.html
I try to console.log the isAuthenticated getter state and its returning false even though in dev tools Vuex tab the state value is true.
Below is the store.js:
import axios from 'axios'
export default {
namespaced: true,
state: {
bearerToken: null,
userData: null,
},
getters: {
isAuthenticated(state) {
if (state.bearerToken && state.userData) {
return true
}
return false
},
userData(state) {
return state.userData
},
},
actions: {
async login({ dispatch }, credentials) {
const response = await axios({
url: 'api/auth/login',
data: credentials,
method: 'POST',
})
dispatch('attempt', response.data.bearerToken)
},
async attempt({ commit, state }, bearerToken) {
if (bearerToken) {
commit('SET_BEARER_TOKEN', bearerToken)
}
// ? If bearerToken is null or invalid stop here
if (!state.bearerToken) {
return
}
// ? If bearerToken is valid get user data
try {
const response = await axios({
url: 'api/auth/me',
method: 'GET',
})
commit('SET_USER_DATA', response.data)
} catch (error) {
commit('SET_BEARER_TOKEN', null)
commit('SET_USER_DATA', null)
}
},
logout({ commit }) {
return axios({
url: 'api/auth/logout',
method: 'POST',
})
.then(() => {
commit('SET_BEARER_TOKEN', null)
commit('SET_USER_DATA', null)
})
},
},
mutations: {
SET_BEARER_TOKEN(state, bearerToken) {
state.bearerToken = bearerToken
},
SET_USER_DATA(state, userData) {
state.userData = userData
},
},
}
Below is the router.js:
import store from '#/store'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const ifNotAuthenticated = (to, from, next) => {
console.log(store.getters['authentication/isAuthenticated']) // <- this returns false
console.log(store.getters.isAuthenticated) // <- this returns undefined
if (!store.getters['authentication/isAuthenticated']) {
next()
return
}
next({ name: 'dashboard' })
}
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior() {
return { x: 0, y: 0 }
},
routes: [
{
path: '/',
name: 'dashboard',
component: () => import('#/views/Dashboard.vue'),
meta: {
pageTitle: 'Dashboard',
breadcrumb: [
{
text: 'Dashboard',
active: true,
},
],
},
},
{
path: '/login',
name: 'login',
component: () => import('#/views/Login.vue'),
beforeEnter: ifNotAuthenticated,
meta: {
layout: 'full',
},
},
{
path: '/error-404',
name: 'error-404',
component: () => import('#/views/error/Error404.vue'),
meta: {
layout: 'full',
},
},
{
path: '*',
redirect: 'error-404',
},
],
})
// ? For splash screen
// Remove afterEach hook if you are not using splash screen
router.afterEach(() => {
// Remove initial loading
const appLoading = document.getElementById('loading-bg')
if (appLoading) {
appLoading.style.display = 'none'
}
})
export default router
I can't seem to pass data to my Vuex action. I always get the error message TypeError: can't access property x, data is undefined. I have done a few different approaches on passing property value but I couldn't make it work. Can you look at my code, what am I doing wrong?
Store:
import axios from 'axios'
export default {
namespaced: true,
state: {
announcements: []
},
getters: {},
actions: {
createAnnouncement({ commit }, courseId, data) {
return new Promise((resolve, reject) => {
axios
.post(`teacher/courses/announcements/create/${courseId}`, { title: data.title, message: data.message })
.then((response) => {
commit('ADD_ANNOUNCEMENT', response.data.data)
resolve(response)
})
.catch((error) => { reject(error) })
})
}
},
mutations: {
ADD_ANNOUNCEMENT(state, newAnnouncement) {
state.announcements.push(newAnnouncement)
}
}
}
Component:
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data() {
return {
title: '',
message: '',
}
},
computed: {
...mapGetters('courses', ['getCourseData'])
},
methods: {
...mapActions('announcements', ['createAnnouncement']),
onSubmit() {
const { announcementTitle, announcementMessage } = this
const courseId = this.getCourseData.id
this.createAnnouncement(courseId, { title: announcementTitle, message: announcementMessage })
}
}
}
</script>
Actions only accept two arguments, the context and an optional payload.
Try changing your action to
createAnnouncement({ commit }, { courseId, data }) {
//...
}
and dispatch it with
const payload = {
courseId: this.getCourseData.id,
data: {
title: this.announcementTitle,
message: this.announcementMessage
}
}
this.createAnnouncement(payload)
See https://vuex.vuejs.org/api/#actions
I've created simple VueCLI auth module using axios and Vuex.
In store.js I've got all logic for tokens using api from session.js:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import sessionSerivce from '#/services/session.js'
Vue.use(Vuex)
Vue.use(require('vue-cookies'))
export const store = new Vuex.Store({
state: {
status: '',
accessToken: $cookies.get('accessToken') || '',
refreshToken: $cookies.get('refreshToken') || '',
user: $cookies.get('user') || '',
},
actions: {
login({ commit }, data) {
return new Promise((resolve, reject) => {
commit('auth_request')
sessionSerivce
.logIn(data)
.then((resp) => {
const commitData = {
accessToken: resp.data.access_token,
refreshToken: resp.data.refresh_token,
user: resp.data.user,
}
$cookies.set('accessToken', commitData.accessToken)
$cookies.set('refreshToken', commitData.refreshToken)
$cookies.set('user', JSON.stringify(commitData.user))
axios.defaults.headers.common['Authorization'] =
commitData.accessToken
commit('auth_success', commitData)
resolve(resp)
})
.catch((err) => {
commit('auth_error')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
reject(err)
})
})
},
verifyToken({ commit, state }) {},
register({ commit }, data) {
return new Promise((resolve, reject) => {
commit('auth_request')
sessionSerivce
.register(data)
.then((resp) => {
const commitData = {
accessToken: resp.data.access_token,
refreshToken: resp.data.refresh_token,
user: resp.data.user,
}
$cookies.set('accessToken', commitData.accessToken)
$cookies.set('refreshToken', commitData.refreshToken)
$cookies.set('user', JSON.stringify(commitData.user))
axios.defaults.headers.common['Authorization'] =
commitData.accessToken
commit('auth_success', commitData)
resolve(resp)
})
.catch((err) => {
commit('auth_error')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
reject(err)
})
})
},
logout({ commit }) {
return new Promise((resolve, reject) => {
commit('logout')
$cookies.remove('accessToken')
$cookies.remove('refreshToken')
$cookies.remove('user')
delete axios.defaults.headers.common['Authorization']
resolve()
})
},
},
mutations: {
auth_request(state) {
state.status = 'loading'
},
auth_success(state, commitData) {
state.status = 'success'
state.accessToken = commitData.accessToken
state.refreshToken = commitData.refreshToken
state.user = commitData.user
},
auth_error(state) {
state.status = 'error'
},
refresh_token(state, accessToken) {
state.accessToken = accessToken
},
logout(state) {
state.status = ''
state.accessToken = ''
state.refreshToken = ''
state.user = ''
},
},
getters: {
isLoggedIn: (state) => {
return !!state.accessToken
},
authStatus: (state) => state.status,
},
})
In main.js I use this function to check:
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (store.getters.isLoggedIn) {
next()
return
}
next('/login')
} else next()
})
The problem is that code above checks only if access token exists in Vuex. I want to verify using api before any route, that requires auth and if it's not successfully I want to refresh It with api using refresh token. If both are unsuccessful(access and refresh tokens are both invalid) user gonna log out.
Example route which requires auth:
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
},
I've tried code like this:
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (store.state.accessToken) {
await store.dispatch('verifyToken')
if (store.getters.isLoggedIn) {
next()
return
}
}
next('/login')
} else next()
})
Action in Vuex:
verifyToken({ commit, state }) {
const accessToken = state.accessToken
const refreshToken = state.accessToken
sessionSerivce
.verifyToken(accessToken)
.then((resp) => {})
.catch((err) => {
sessionSerivce
.refreshToken(refreshToken)
.then((resp) => {
console.log('Refreshuje token')
const accessToken = resp.data.access_token
localStorage.setItem('accessToken', accessToken)
axios.defaults.headers.common['Authorization'] = accessToken
commit('refresh_token', accessToken)
})
.catch((err) => {
commit('logout')
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
delete axios.defaults.headers.common['Authorization']
})
})
},
Note that in code above i used localstorage but i've changed my mind and I'm using cookie, as You can see in previous code.
Unfortunately this code didn't work as expected - if (store.getters.isLoggedIn) { next(); return; } is starting to execute before await store.dispatch('verifyToken') ends, which is bad.
Any ideas?
I want to test the data I receive from my service.
I read angular documentation and I followed observable example but I don't receive any data when I subscribed to the observable.
Console.log() isn't working inside the subscribe.
The service is working properly and get the right data in a real environment.
I tried to use async and doneFn but they didn't work both of them got time out.
Service File
export class BackService {
URL = '********'; // I removed url for security.
constructor(private httpClient: HttpClient) { }
getAllForms(): Observable<Array<object>> {
return this.httpClient.get<Array<object>>(`${this.URL}/allForms`);
}
getFormById(formId): Observable<Array<object>> {
return this.httpClient.get<Array<object>>(`${this.URL}/form/${formId}`);
}
}
Test Service File
import { TestBed } from '#angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { BackService } from './back.service';
describe('BackService', () => {
let httpMock: HttpTestingController;
let backService: BackService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [BackService]
});
backService = TestBed.get(BackService);
httpMock = TestBed.get(HttpTestingController);
});
it('should be created', () => {
expect(backService).toBeTruthy();
});
describe('Methods', () => {
describe('All Forms', () => {
it('should use GET method', () => {
backService.getAllForms().subscribe();
const req = httpMock.expectOne(`${backService.URL}/allForms`);
expect(req.request.method).toBe('GET');
});
it('should use the right url', () => {
backService.getAllForms().subscribe();
const req = httpMock.expectOne(`${backService.URL}/allForms`);
expect(req.request.url).toBe(`${backService.URL}/allForms`);
});
it('should return the right data', () => {
const mockData = [{'_id': 435345345, '_type': 'window'}]
backService.getAllForms().subscribe(data => {
expect(data).toEqual(mockData);
});
});
});
});
First 2 tests look ok, for third test to receive data that you can test on, you have to trigger that "httpMock" by calling its flush() method with necessary object you want your httpClient to return.
This should work for third test:
it('should return the right data', () => {
const mockData = [{'_id': 435345345, '_type': 'window'}]
backService.getAllForms().subscribe(data => {
expect(data).toEqual(mockData);
});
const req = httpMock.expectOne(`${backService.URL}/allForms`);
req.flush(mockData);
});
we do pact tests because you have to mock a lot which didn't feel real for us.
Example
import {PactWeb} from '#pact-foundation/pact-web';
describe('FooService', () => {
let provider: PactWeb;
beforeAll(async () => {
await provider.addInteraction({
state: 'I have an foo',
uponReceiving: 'a request to create an bar',
withRequest: {
method: 'POST',
path: '/url/bars',
body: {
foo: '123456',
bar: 'abcd'
}
},
willRespondWith: {
status: 200,
headers: {'Content-Type': 'application/json'},
body: {
foo: '123456',
bar: 'abcd'
}
}
});
});
it('should create one bar and respond with that bar', async () => {
const service: FooService = TestBed.get(FooService);
(service as any).apiBasePath = provider.mockService.baseUrl + 'url';
const result = await service.createBar({
foo: '123456',
bar: 'abcd'
}).toPromise();
expect(result.id).toEqual('some-random-uuid');
});
afterAll(function (done) {
provider.finalize()
.then(function () {
done();
}, function (err) {
done.fail(err);
});
});
});
I assume, that you have a service called "createBar" that you want to test.
State
Is just to know what you are doing, so it is the state of the provider. He has a foo. And uponReceiving of the request he should create a bar
withRequest
Shows how the request should look like
willRespondWith
Shows the response.
Can you try with MockBackend instead?
import { TestBed, inject } from "#angular/core/testing";
import {
HttpModule,
Http,
Response,
ResponseOptions,
XHRBackend
} from "#angular/http";
import { MockBackend } from "#angular/http/testing";
import { BackService } from "./back.service";
describe("BackService", () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [BackService, { provide: XHRBackend, useClass: MockBackend }]
});
});
describe("getAllForms()", () => {
it("should return an Observable<Array<Form>>", inject(
[BackService, XHRBackend],
(backService, mockBackend) => {
const mockResponse = {
data: [
{ id: 0, form: "Form 0" },
{ id: 1, form: "Form 1" },
{ id: 2, form: "Form 2" },
{ id: 3, form: "Form 3" }
]
};
mockBackend.connections.subscribe(connection => {
connection.mockRespond(
new Response(
new ResponseOptions({
body: JSON.stringify(mockResponse)
})
)
);
});
backService.getAllForms().subscribe(forms => {
expect(forms.length).toBe(4);
expect(forms[0].form).toEqual("Form 0");
expect(forms[1].form).toEqual("Form 1");
expect(forms[2].form).toEqual("Form 2");
expect(forms[3].form).toEqual("Form 3");
});
}
));
});
});