Computed property depends on vuex store. How to update the cached value? - vue.js

The value of this.$store.state.Auth.loginToken is modified by one of its child components. The initial value of this.$store.state.Auth.loginToken is undefined. But still, the update in its value has no effect in the cached value of navItems thus it always returns the second value.
computed: {
navItems () {
return this.$store.state.Auth.loginToken != undefined ?
this.items.concat([
{ icon: 'add', title: 'Add new journal entry', to: '/' },
{ icon: 'power_settings_new', title: 'Logout', to: '/logout'}
]) :
this.items.concat([
{ icon: 'play_arrow', title: 'Login', to: '/login' }
])
}
}
Is there a better way to keep a watch on this.$store.state.Auth.loginToken so that it can be used same as navItems?

I created a basic example of how you can use vuex getters and Auth token (codepen):
const mapGetters = Vuex.mapGetters;
var store = new Vuex.Store({
state: {
Auth: {
loginToken: ''
},
menuItems: [
{ icon: 'home', title: 'Home', to: '/' },
{ icon: 'about', title: 'About', to: '/about' },
{ icon: 'contact', title: 'Contact', to: '/contact' }
]
},
mutations: {
SET_LOGIN_TOKEN(state, data) {
state.Auth.loginToken = 1
}
},
getters: {
menuItems(state, getters) {
if(state.Auth.loginToken !== '') {
return state.menuItems.concat([{
icon: 'profile', title: 'Profile', to: '/profile'
}])
}
return state.menuItems
},
loggedIn(state) {
return state.Auth.loginToken !== ''
}
},
actions: {
doLogin({commit}) {
commit('SET_LOGIN_TOKEN', 1)
}
}
});
new Vue({
el: '#app',
store,
data: function() {
return {
newTodoText: "",
doneFilter: false
}
},
methods: {
login() {
this.$store.dispatch('doLogin')
}
},
computed: {
...mapGetters(['menuItems', 'loggedIn'])
}
})
This is just an example so you can ignore the actual login action. Also, the store should be a directory, the getters, mutations and actions should have their own files which are then imported in the index.js in the store like in this example

Related

Data not being passed from Child Data to Parent Props

I have a Request Form Component, and within this request form Component I have a Dropdown Menu Component, which I will link both below. All values in my table are pushed into an object upon hitting the Submit Button. However my dropdown selection is only being picked up by my console.log and not being pushed into the Object.
I'm not so familiar with Vue, so I'm not sure what direction to go in for fixing this. I'll attach the relevant (?) pieces of code below.
Parent Component:
<SelectComponent :selected="this.selected" #change="updateSelectedValue" />
export default {
fullScreen: true,
name: 'CcRequestForm',
mixins: [BaseForm],
name: "App",
components: {
SelectComponent,
},
data() {
return {
selected: "A",
};
},
props: {
modelName: {
default: 'CcRequest',
},
parentId: {
type: Number,
default: null,
},
},
mounted() {
this.formFields.requester.value = this.currentRequesterSlug;
},
destroyed() {
if (!this.modelId) return;
let request = this.currentCcRequest;
request.params = request.params.filter(p => p.id)
},
computed: {
...mapGetters(['ccTypesForRequests', 'currentRequesterSlug', 'currentCcRequest']),
ccTypesCollection() {
return this.ccTypesForRequests.map((x)=>[x.slug, this.t(`cc_types.${x.slug}`)]);
}
},
methods: {
addParam() {
this.addFormFields(['params'], {
slug: '',
name: '',
isRequired: true,
description: '',
typeSlug: '',
selected: ''
});
},
deleteParam(idx){
this.removeFormFields(['params', idx]);
},
restoreParam(idx){
this.restoreFormFields(['params', idx])
},
$newObject() {
return {
slug: '',
name: '',
isAbstract: false,
requester: '',
description: '',
status: 'inactive',
params: [],
selected: ''
};
},
$extraPrams() {
return {
parentId: this.parentId,
};
},
updateSelectedValue: function (newValue) {
this.selected = newValue;
},
},
watch: {
selected: function (val) {
console.log("value changed", val);
},
},
};
Child Component:
<script>
export default {
name: "SelectComponent",
props: {
selected: String,
},
computed: {
mutableItem: {
get: function () {
return this.selected;
},
set: function (newValue) {
this.$emit("change", newValue);
},
},
},
};
You have to define the emit property in the parent component, or else it won't know what to expect. That would look like:
<SelectComponent :selected="this.selected" #update-selected-value="updateSelectedValue" />
Check out this tutorial for more information: https://www.telerik.com/blogs/how-to-emit-data-in-vue-beyond-the-vuejs-documentation
To update selected property inside the object, in this constellation, you need to update object property manually upon receiving an event, inside of updateSelectedValue method. Other way could be creating a computed property, since it's reactive, wrapping "selected" property.
computed: {
selectedValue () {
return this.selected
}
}
And inside of object, use selectedValue instead of selected:
return {
...
selected: selectedValue
}

Get params of route Shopware 6

I try to extend the product detail page and get the productId out of the routes.
Shopware.Module.register('sw-product-content-element', {
routeMiddleware(next, currentRoute) {
if (currentRoute.name === 'sw.product.detail') {
currentRoute.children.push({
name: 'sw.product.detail.content-element',
path: '/sw/product/detail/:id/content-element',
component: 'sw-product-detail-content-element',
meta: {
parentPath: "sw.product.index"
},
props: {
default(route) {
console.log(route)
return {
productId: route.params.id
};
}
}
});
}
next(currentRoute);
}
});
I try to pass my productId as props to my component, but in my component productId is undefined.
Component.register('sw-product-detail-content-element', {
template,
props: {
productId: {
required: true,
type: String
},
},
data() {
return {
product: null
};
},
created() {
console.log(this.productId)
},
....
I found a solution. I do not need to pass my value as props, I can just use this.$route.params.id
Component.register('sw-product-detail-content-element', {
template,
props: {
productId: {
required: true,
type: String
},
},
data() {
return {
product: null
};
},
created() {
this.productId = this.$route.params.id
console.log(this.productId)
},
....

How to track changes in a property stored in Vuex(store) and perform some method based on the value?

I'm trying to change the links based on the variable user_role which is stored in Vuex(store). I'm not able to find an appropriate way to track the change and based on its value I want to perform some method. Any suggestions on how to do it?
------------------------------store.js-------------------------------
export default new Vuex.Store({
state: {
user_role: "User"
},
mutations: {},
actions: {},
modules: {}
});
-----------------------------------component.vue---------------------------
export default {
name: "Navbar",
data() {
return {
links: [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "", route: "" },
{ text: "Resources", route: "/resources" }
],
pers_actions: ["Profile", "LogOut"],
};
},
watch: {
user_role: {
if (user_role === "PM") {
this.links[2] = {
text: "Allocations",
route: "/allocations"
};
} else if (user_role === "PMO") {
this.links[2] = {
text: "Reports",
route: "/reports"
};
} else if (user_role === "User") {
this.links = [
{
text: "Allocations",
route: "/allocations"
}
];
}
}
},
Rather than explicitly mutating your local data in response to some state change, it is better to compute your links within a computed property because it will automatically update whenever some dependent data has changed. It'll "just work".
computed: {
links() {
switch (this.$store.state.user_role) {
case: "PM": return [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "Allocations", route: "/allocations" },
{ text: "Resources", route: "/resources" },
];
case: "PMO": return [
{ text: "Projects", route: "/projects" },
{ text: "Requests", route: "/requests" },
{ text: "Reports", route: "/reports" },
{ text: "Resources", route: "/resources" },
];
// For any other role
default: return [
{ text: "Allocations", route: "/allocations" },
];
}
}
}

vue-router returns 'function%20%' in url instead of param

So I'm showing some bread-crumbs like so..
<router-link to="/" class="breadcrumb-item">Home</router-link>
<router-link :to="{name: 'provider-dashboard', params: { id: provider_id }}" class="breadcrumb-item">Provider Dashboard</router-link>
<router-link :to="{name: 'provider-account-dash', params: { provider_id: provider_id, id: account_id }}" class="breadcrumb-item">Account Dashboard</router-link>
<router-link :to="{name: 'resident-profile', params: { account_id: account_id, id: resident_id }}" class="breadcrumb-item">Resident Profile</router-link>
I'm setting the param values with computed props that look like so..
account_id: {
get() {
return this.$store.getters['AssessmentPlanForm/getAccountId'];
},
set(value) {
this.$store.dispatch('AssessmentPlanForm/setAccountId', value);
},
},
provider_id: {
get() {
return this.$store.getters['AssessmentPlanForm/getProviderId'];
},
set(value) {
this.$store.dispatch('AssessmentPlanForm/setProviderId', value);
}
},
resident_id: {
get() {
return this.$store.getters['AssessmentPlanForm/getResidentId'];
},
set(value) {
this.$store.dispatch('AssessmentPlanForm/setResidentId', value);
},
},
I have confirmed that the values of the computed properties are correct, however when I click the router-link breadcrumb to go to desired location, the url shows users/function%20Number() instead of say users/18.
Why is this occurring and how can I get vue-router to properly render the parameter set by computed-prop?
Update from 1st comment
Here are the getters & no I'm not doing that for these attributes.
getId: (state) => {
return state.id;
},
getProviderId: (state) => {
return state.provider_id;
},
getEmployeeId: (state) => {
return state.employee_id;
},
getAccountId: (state) => {
return state.account_id;
},
getResidentId: (state) => {
return state.resident_id;
},
getSlug: (state) => {
return state.slug;
},
Update from 2nd comment
Vue.use(Router);
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [{
path: "/",
name: "home",
component: Splash,
prop: true
},
{
path: "/about",
name: "about",
component: About,
prop: true,
},
{
path: "/contact",
name: "contact",
component: ContactUs,
prop: true,
},
{
path: "/pricing",
name: "pricing",
component: Pricing,
prop: true,
},
{
path: "/faq",
name: "faq",
component: Faq,
prop: true
},
{
path: "/polls",
name: "polls",
component: Polls,
prop: true
},
{
path: "/login",
name: "login",
component: Login,
prop: true
},
{
path: "/provider-signup",
name: "provider-signup",
component: ProviderSignup,
prop: true
},
{
path: "/provider-dashboard/:id",
name: "provider-dashboard",
component: ProviderDash,
prop: true
},
{
path: "/providers/:id/edit",
name: "edit-provider",
component: EditProvider,
prop: true
},
{
path: "/provider/:id/employee-invitation",
name: "employee-invitation",
component: ProviderEmployeeInvite,
prop: true
},
{
path: "/employee-signup",
name: "employee-signup",
component: EmployeeSignup,
prop: true
},
{
path: "/employee-dashboard/:id",
name: "employee-dashboard",
component: EmployeeDash,
prop: true
},
{
path: "/employees/:id/edit",
name: "edit-employee",
component: EditEmployee,
prop: true
},
{
path: "/provider/:provider_id/employees",
name: "employees",
component: Employees,
prop: true
},
{
path: "/provider/:provider_id/accounts/new",
name: "provider-account-signup",
component: ProviderAccountSignup,
prop: true
},
{
path: "/providers/:provider_id/accounts/:id",
name: "provider-account-dash",
component: ProviderAccountDash,
prop: true
},
{
path: "/providers/:provider_id/accounts/:account_id/edit",
name: "edit-provider-account",
component: EditProviderAccount,
prop: true
},
.
.
.
]
});
So the answer was to fix a User error on my part. I forgot to assign the values of those attributes in a page I was working on.
The answer was to load the values of these attributes #created
retrieve(context, record_id) {
let resident_id = router.currentRoute.params.resident_id;
Axios.get(`/residents/${resident_id}/assessment_plan_forms/${record_id}`, {
headers: {
'Authorization': 'Bearer ' + window.$cookies.get('access_token'),
'x-amz-acl': 'public-read'
}
})
.then((response) => {
// let current_user = response.data.locals.current_user;
let provider = response.data.locals.provider;
let resident = response.data.locals.resident;
let account = response.data.locals.account;
let pdf_url = response.data.locals.pdf_url;
let date_of_record = response.data.locals.date_of_record;
let assessment_plan_form = response.data.locals.assessment_plan_form;
context.dispatch('AssessmentPlanForm/setId', assessment_plan_form.id, {
root: true
})
context.dispatch('AssessmentPlanForm/setProviderId', provider.id, {
root: true
})
context.dispatch('AssessmentPlanForm/setAccountId', account.id, {
root: true
})
context.dispatch('AssessmentPlanForm/setResidentId', resident.id, {
root: true
});
context.dispatch('AssessmentPlanForm/setPdfUrl', pdf_url, {
root: true
});
context.dispatch('AssessmentPlanForm/setDateOfRecord', date_of_record, {
root: true
});
context.dispatch('AssessmentPlanForm/setResidentSignature', resident.full_name, {
root: true
});
// redirect to show page
router.push({
name: 'show-assessment-plan',
params: {
resident_id: resident.id,
id: record_id
}
})
})
.catch((error) => {
console.log(error);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
And #retrieve gets called in the Created hook like:
methods: {
loadAssessmentPlan() {
this.$store.dispatch('AssessmentPlanForm/retrieve', this.$route.params.id)
},
},
created() {
this.loadAssessmentPlan();
},

Vue Router Resets Global Data

Im trying to store user data globally using a Vue mixin: (main.js)
import Vue from 'vue';
import Resource from 'vue-resource'
import App from 'App';
import router from 'router/index';
Vue.use(Resource);
Vue.mixin({ //globals
delimiters: ["[[", "]]"],
http: {
root: 'http://127.0.0.1:5000/'
},
data: function() {
return {
user: {
authorized: false,
username: '',
password: '',
avatar: '',
entry: '',
skill: '',
arena: {
id: '',
start: false,
votes: '',
}
}
}
}
});
new Vue({
router: router,
el: '#app',
components: {
App
},
template: '<App/>'
});
I get the data from a login page just fine: (part of Login.vue)
import Vue from 'vue';
export default {
name: 'Login-Page',
data() {
return {
message: 'Hello Vue!'
}
},
methods: {
_make_basic_auth(user, pass) {
var tok = user + ':' + pass;
return "Basic " + btoa(tok);
},
_fetch_user(protocol) {
this.message = 'waiting...';
var auth = this._make_basic_auth(this.user.username, this.user.password);
Vue.http.headers.common['Authorization'] = auth;
this.$http[protocol]('api/u/' + this.user.username).then(response => {
this.message = "Success";
if (response.body.authorized) {
this.user = {...this.user, ...response.body};
setTimeout(() => {
this.$router.push({
name: 'Profile',
params: {
id: this.user.username
}
});
}, 1000);
}
}, response => {
this.message = response.body;
console.log(response.status + " " + response.body);
});
},
register() {
this._fetch_user('post');
},
login() {
this._fetch_user('get');
}
}
}
The data is just reset on redirect: (part of Main.vue)
import Profile from 'components/Profile'
export default {
name: "Main-Page",
methods: {
enterArena() {
this.$http.get('api/match').then(response => {
console.log(response.body);
this.user.arena = {...response.body, ...this.user.arena};
this.$router.push({
name: "Arena",
params: {'id': response.body.id}
});
}, error => {
console.log(error.status + " " + error.body);
});
}
},
created() {
console.log(this);
console.log(this.user);
if (!this.user.authorized)
this.$router.push({
name: "Login"
});
}
}
It was working before, here is my old repo https://github.com/Jugbot/Painter-Arena-Web-API/tree/6f3cd244ac17b54474c69bcf8339b5c9a2e28b45
I suspect that the error is from my new arrangement of components in my Router or flubbed this references.
index.js:
routes: [
{
path: '',
name: 'Main',
component: Main,
children: [
{
path: '/arena/:id',
name: 'Arena',
component: Arena
},
{
path: '/u/:id',
name: 'Profile',
component: Profile
}
]
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/404',
component: NotFound
},
{
path: '*',
redirect: '/404'
},
],
mode: 'hash'
Update:
Problem is still unsolved but as a workaround I just moved all mixin data to the $root instance and that managed to work.
I recommend you to use vuex for better state management. It is complicated to use mixins as a data storage for a vue application. Using vuex is convenient way to manipulate dynamic or static data across the application and will not be deleted in destroy hook upon exiting on a component.