#StackBounty: #vue.js #vuetify.js #vue-router Vuetify bottom navigation not removing active state on route change after hard refresh

Bounty: 50

In my app I have a bottom navigation component set up as such:

<v-bottom-navigation
  v-else
  background-color="overlay"
  grow
  app
  color="primary"
>
  <v-btn
    v-for="(route, key) in routes"
    ref="link"
    :key="'route' + key"
    :to="route.to"
    min-width="48"
    class="px-0"
  >
    <span v-if="$vuetify.breakpoint.smAndUp">{{ route.name }}</span>
    <v-icon>{{ route.icon }}</v-icon>
  </v-btn>
</v-bottom-navigation>

This works well, I can use it to navigate around my app as intended. When clicking one of the buttons on the bottom navigation, the button state updates as it should to show that it is active (primary color).

My routes are defined as follows:

routes: [
  { icon: mdiViewDashboard, name: 'Dashboard', to: '/dashboard' },
  { icon: mdiCart, name: 'Orders', to: '/orders' },
  { icon: mdiDolly, name: 'Receiving', to: '/receiving' },
  { icon: mdiClipboardText, name: 'Tasks', to: '/tasks' },
  { icon: mdiTruck, name: 'Deliveries', to: '/deliveries' },
],

I have an additional route /settings which is not part of this main navigation, but defined on the header bar of my application as such:

<v-btn
  aria-label="Settings"
  icon
  to="/settings"
>
  <v-icon>{{ mdiCog }}</v-icon>
</v-btn>

Since the settings button/router link is not part of the bottom navigation, when I click on the settings button, it disables the active state on the buttons in the bottom navigation, as it should since it is not part of the bottom navigation.

Here is where the odd behavior occurs:

If I am in one of the routes defined on the bottom navigation, and hard refresh the page I refresh to the proper location. From here, if I click the settings button, the router displays the settings as it should, but the active state on the bottom navigation of the stale state still shows. This only occurs on a hard refresh, if I select a bottom navigation route, and then go to settings, it removes the active state from the bottom navigation button.

In my research I thought it may be an issue with the exact property for the router links, but it seems to make no different.

Additionally, I set a breakpoint to display the bottom navigation on small and below, if I stretch the window to hide the bottom navigation, then shrink it to re-show, when the component is shown again, it has the proper state.


Get this bounty!!!

#StackBounty: #javascript #vue.js #google-chrome-extension #vue-component #vuex Shared vuex in chrome extension

Bounty: 100

Original question:

vuex shared state in chrome extension

I have the following setup in a chrome extension;

  1. A content script that needs to write to a vuex store
  2. A background script that initializes that store
  3. And a popup script that renders stuff from the store (received from the content script)

store.js

import Vue from "vue";
import Vuex from "vuex";
import "es6-promise/auto";

import createMutationsSharer from "vuex-shared-mutations";

import dummyData from "./dummyData";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    chromePagesState: {
      allSections: [],
    },
  },
  mutations: {
    setChromePagesState(state, value) {
      state.chromePagesState = value;
    },
    addWhiteListedItem(state, item) {
      state.chromePagesState.allSections[0].itemSectionCategory[0].tasks.splice(
        0,
        0,
        item
      );
    },
  },
  actions: {
    // init from local storage
    async loadChromePagesState({ commit }) {
      const json = await getStorageValue("inventoryData");
      commit(
        "setChromePagesState",
        Object.keys(json).length === 0 && json.constructor === Object
          ? dummyData
          : JSON.parse(JSON.stringify(json))
      );
    },
    // send message to background script to call init (shortened)
    async loadChromePagesStateBrowser({ commit }) {
      browser.runtime
        .sendMessage({ type: "storeinit", key: "chromePagesState" })
        .then(async (chromePagesState) => {
          const json = await getStorageValue("inventoryData");
          commit(
            "setChromePagesState",
            Object.keys(json).length === 0 && json.constructor === Object
              ? dummyData
              : JSON.parse(JSON.stringify(json))
          );
        });
    },
  },
  // stuff from vuex-shared-mutations
  plugins: [
    createMutationsSharer({
      predicate: [
        "addWhiteListedItem",
        "loadChromePagesState",
        "loadChromePagesStateBrowser",
      ],
    }),
  ],
});

The content script calls store from a vue component:

index.js

import store from "../popup/firstpage/store";

new Vue({
   el: overlayContainer,
   store,
    render: (h) => h(Overlay, { props: { isPopUp: isPopUp } }),
});

Overlay.vue

<script>
import { mapState, mapMutations } from "vuex";

export default {
  props: ["isPopUp"],
  data() {
    return {
    };
  },
  computed: mapState(["chromePagesState"]),
  methods: { 
    ...mapMutations(["addWhiteListedItem"]),
    // this gets called in the template
    addToWhiteList() {
        let newItem = initNewItemWithWebPageData();
        this.addWhiteListedItem(newItem);
    },
    },
}
</script>

The background script receives a message and calls a mutation on the store:

background.js

import store from "../content/popup/firstpage/store";

browser.runtime.onMessage.addListener((message, sender) => {
  if (message.type === "storeinit") {   
    store.dispatch("loadChromePagesState");
    return Promise.resolve(store.state[message.key]);
  }
});

Upon opening popup.js, a store mutation is called that sends a message to background.js that calls another mutation in the store:

popup.js

import store from "./firstpage/store";

export function showPopup() {
  const popupContainer = document.createElement("div");

  new Vue({
    el: popupContainer,
    store,
    render: (h) => h(App),
    created() {
      console.log("Calling store dispatch from popup");
      this.$store.dispatch("loadChromePagesStateBrowser");
    },
  });
}

Where App.vue is

<template>
  <div id="app">
    <OtherComponent />
  </div>
</template>

<script>
import { mapActions } from "vuex";
import OtherComponent from "./components/ChromePage.vue";

export default {
  name: "App",
  OtherComponent: {
    VueTabsChrome,
  },
  methods: {
    ...mapActions(["loadChromePagesState"]),
  },
  mounted() {
    // once fully mounted we load data
    // this is important for a watcher in ChromePage component
    console.log("App.vue mounted");
    // this.loadChromePagesState();
  },
};
</script>

Intuitively export default new creates a new instance on every import hence the not being in sync across scripts (since the stores are different objects).

How can the same store be initialized once and used across multiple entry points?

popup.js is opened when the user clicks the extension icon:

enter image description here

(in this case clicks "new tab").


Get this bounty!!!

#StackBounty: #javascript #mongodb #vue.js #express Cannot get the value of an old (previous) cart

Bounty: 50

I want to "overwrite" my cart. So I get the information I want but the problem is I can’t get the information of my old cart. I don’t get it how it cannot be properly retrieved. And whole the time I’m getting quantity of 1.

Here is the app.post request:

app.post('/add-to-cart/:id', async (req, res) => {
  try {
    let id = req.params.id;
    let cart = new Cart(req.body.cart ? req.body.cart : {});

    const { data } = await axios.get('http://localhost:4200/products');
    const singleProduct = await data.find((product) => product._id === id)
    console.log(cart);
    cart.addProduct(singleProduct);
    req.body.cart = cart;

    res.redirect('/');
    console.log(req.body.cart);
  } 
  
  catch (error){
    console.log(error)
  }
  
});

It seems that something is wrong here: let cart = new Cart(req.body.cart ? req.body.cart : {});

So this is the console.log output:

enter image description here

This is the Cart code:

module.exports = function Cart(oldCart) {
    this.productItems = oldCart.productItems || {};
    this.totalQty = oldCart.totalQty || oldCart.totalQty==0.00;
    this.totalPrice = oldCart.totalPrice || oldCart.totalPrice==0.00;
    
    this.addProduct = function(item) {
        
        let storedItem = this.productItems;
              
        if (!storedItem){
            storedItem = this.productItems = {item: item, qty: 0, price: 0};
        }
        else{
            storedItem = {item: item, qty: storedItem.qty, price: storedItem.price}
            console.log("STORED ITEM: ",storedItem)
            this.productItems=storedItem;
            
            storedItem.qty++;
            storedItem.price = storedItem.item.price * storedItem.qty;
            this.totalQty ++;
            this.totalPrice += storedItem.item.price;
        }
    }

    };


Get this bounty!!!

#StackBounty: #node.js #vue.js #express #heroku MEVN stack unable to deploy

Bounty: 50

I have a project where I try to locally and on Heroku run a MEVN application. Both are not working at the moment.

I have my server.js file in the main folder. Then inside the ‘client’ folder is the Vue 3 project. Inside there I have run npm run build which created a dist folder containing the index.html file.

When I run both locally and on Heroku I can go to ‘/api/users’ and the JSON is shown in the browser. But when I navigate to ‘/’

I have tried changing the public in the server.js to dist but this did not fix the problem.

My server.js file looks like this:

const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const path = require("path");
const cors = require("cors");

const app = express();

//Bodyparser Middleware
app.use(bodyParser.json());

app.enable("trust proxy");
app.use(cors());

app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", '*');
  res.header("Access-Control-Allow-Credentials", true);
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
  res.header("Access-Control-Allow-Headers", 'Origin,X-Requested-With,Content-Type,Accept,content-type,application/json');
  next();
});

//DB Config
const db = require("./config/keys").mongoURI;

//Connect to Mongo
mongoose
  .connect(db, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => console.log("MongoDB Connected"))
  .catch((error) => console.log(error));

app.get('/api/users', (req, res) => {
  res.json({ user: 'test' })
})

const port = process.env.PORT || 3000;

if (process.env.NODE_ENV === "production") {
  app.use(express.static("client/build"));

  app.get("*", (req, res) => {
    res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
  });
}

app.listen(port, () => console.log("Server starter on port " + port));


Get this bounty!!!

#StackBounty: #vue.js #flask #nuxt.js #server-side-rendering Combining Flask with Nuxtjs for SSR

Bounty: 50

I am developing a universal app with separated frontend and backend using Flask as an API and Nuxtjs for frontend.
Before trying Nuxtjs, I was using only vuejs (SPA) but then I realized that it was not optimized for SEO. Anyways, I was able to use generated dist files when using build mode(static and index.html). And I was able to deal with it by using this code:

app = Flask(__name__, template_folder = "../frontend/dist")
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

@app.route('/static/js/<filename>')
def send_js(filename):
    filename = str(filename)
    return send_from_directory('../frontend/dist/static/js', filename)

@app.route('/static/css/<filename>')
def send_css(filename):
    filename = str(filename)
    return send_from_directory('../frontend/dist/static/css', filename)

@app.route('/static/fonts/<filename>')
def send_img(filename):
    filename = str(filename)
    return send_from_directory('../frontend/dist/static/fonts', filename)

@app.route('/static/img/<filename>')
def send_statics(filename):
    filename = str(filename)
    return send_from_directory('../frontend/dist/static/img', filename)

@app.route('/statics/icons/<filename>')
def send_icons(filename):
    filename = str(filename)
    return send_from_directory('../frontend/dist/static/statics/icons', filename)


@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    return render_template("index.html")

However, when I started using Nuxtjs, build mode generates different files (no index.html and no static folder) And I don’t know how to deal with it. You can find what it generates in the screenshot below:
generated files

Thanks in advance for your response.


Get this bounty!!!

#StackBounty: #javascript #vue.js #calendar #css-grid Make a simple calendar using css-grid, vuejs and momentjs to start with monday

Bounty: 300

I’ve made a simple calendar using the Vuejs, momentjs and css-grids layout. But the week starts with Sunday (in american style). How can I change the code to make it start with Monday (european style calendar)? I tried to change the column method decrementing it to 2, but only shows properly august and other months started with wrong weekdays. Also I guess I need somehow change the css option grid-template-columns to support Mondays as the first days of week.

Here’s the codepen: https://codepen.io/moogeek/pen/oNWyWvM

const calendar = new Vue({
  el: '#app',
  data() {
    return {
      date:moment(),
      days: [],
      monthName: '',
    }
  },
  methods: {
    column(index) {
      if (index == 0) {
        return this.days[0].day() + 1
      }
    },
    isToday(day) {
      return moment().isSame(day, 'day')
    },
    updateMonth(){
      this.monthName = this.date.format("MMMM YYYY")
      let monthDate = this.date.startOf('month');
    
      this.days = [...Array(monthDate.daysInMonth())].map((_, i) => monthDate.clone().add(i, 'day'))
    },
    nextMonth(){
      this.date.add(1,'month')
      this.updateMonth()
    },
    previousMonth(){
      this.date.subtract(1,'month')
      this.updateMonth()
    },
  },
  mounted() {
    this.updateMonth()
  }
})
html {
  overflow:hidden;
}

#app {
    width:100%;
    height:100%;
    font-size: 100%;
    user-select: none;
    overflow:hidden;
}

.page-content {
  overflow: hidden;
}

.calendar-wrap {
  padding-top:4vh;
}

.calendar-header {
    width: 100vw;
    font-size: 170%;
    text-align:center;
    background-color: transparent;
}
.calendar-header .month-name, .calendar-header button{
  display: inline-block;
}

.calendar-header .month-name{
  color:#000;
  font-size:150%;
}
.calendar-header .current-month-value {
  color:#000;
}

.calendar-header .link, .calendar-header .link:hover,.calendar-header .link:active {text-decoration:none;}

.calendar-header .calendar-prev-month-button {
  margin-right: 1vh;
}

.calendar-header .calendar-next-month-button {
  margin-left: 1vh;
}

.calendar-wrapper {
    align-items: center;
    box-sizing: border-box;
    display: flex;
    justify-content: center;
    padding: 0 2em 0 2em;
  }  
  #calendar{
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    max-width: 1024px;
    width: 100%;
    transition: transform .3s ease 0s;
  }

  #calendar > *{
    align-items: center;
    display: flex;
    justify-content: center;
  }
  
  #calendar > *::before {
    content: "";
    display: inline-block;
    height: 0;
    padding-bottom: 100%;
    width: 1px;
  }
  
  #calendar > *.today{
    color: black;
    border: 0.1em solid black;
    border-radius: 100%;
  }

  #calendar > .day {
    border-radius: 100%;
    margin:3px;
  }
<html>
  <head>
      <meta charset="utf-8">
      <title>Calendar</title>


  </head>
 <body>
   <div id="app" class="page-content">
      <div class="calendar-wrap">      
              <section class="calendar-header">
                  <a @click="previousMonth()"class="link icon-only calendar-prev-month-button"><i class="icon icon-prev"></i></a>
                  <a class="current-month-value link">{{ monthName }}</a>
                  <a @click="nextMonth()" class="link icon-only calendar-next-month-button"><i class="icon icon-next"></i></a>
              </section>

              <section id="calendar-wrapper" class="calendar-wrapper skeletons">
                <main id="calendar">
                  
                    <div class="weekday">S</div>
                    <div class="weekday">M</div>
                    <div class="weekday">T</div>
                    <div class="weekday">W</div>
                    <div class="weekday">T</div>
                    <div class="weekday">F</div>
                    <div class="weekday">S</div>
                  
                  <div v-for="(day, index) in days"
                      :data-date="day.format('DD.MM.YYYY')"
                      :style="{ gridColumn: column(index) }" 
                      :class="{ day, today: isToday(day) }">
                    <span>{{ day.format('D') }}</span>
                  </div>
                </main> 
              </section>
    </div>
</div>
</body>
</html>

upd.
Ok perhaps I came to a better solution of wrapping weekdays and days in separated containers and adding grid-column:7 property to the first day child. Also changed column function so it doesn’t add addiontal offset:

https://codepen.io/moogeek/pen/ExmRzgb

Now onmount the August month is showing correctly, but if I click prev/next months it is somehow cloned out with September’s days order… Please could you let me know what am I doing wrong and how do I correct the code so it can show all months properly:


Get this bounty!!!

#StackBounty: #node.js #azure #vue.js #deployment Azure Web Service Deployment fails for Node.js project

Bounty: 50

I have a weird problem while deploying our Vue.js project to an Azure Web Service. First things first: the setup worked out perfectly and I can access the Web Service, the only problem I face is, that the deployment fails. When I locally start the application with npm run serve everything works fine, BUT I get tons of errors like 82:18 Property 'load' does not exist on type 'Vue'..

We ignored those errors for now, since load exists and we can use it normally and did not find a solution to resolve those errors. Therefore, the question is: might those errors (even though the displayed errors are flagging something we use and which works) fail the deployment?

The last line of the deployment is as follows:

2021-06-23T15:09:46.095Z - npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.1 (node_modules/webpack/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/webpack-dev-server/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/watchpack-chokidar2/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.1 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/fork-ts-checker-webpack-plugin/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

-  Building for production...
 ERROR  Build failed with errors.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! project@0.1.0 build: `vue-cli-service build`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the project@0.1.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/.npm/_logs/2021-06-23T15_09_29_048Z-debug.log
/opt/Kudu/Scripts/starter.sh oryx build /home/site/repository -o /home/site/wwwroot --platform nodejs --platform-version 14 -i /tmp/8d93658061b46ee -p compress_node_modules=tar-gz --log-file /tmp/build-debug.log 

I honestly do not know what to do with this huge error. I searched around a bit, but did not find a fitting solution.


Get this bounty!!!

#StackBounty: #javascript #comparative-review #vue.js Which of these two Vuetify Navigation Drawer prevent prop mutation workarounds se…

Bounty: 50

I am very new to Vue.js, I just stumbled on a very hard thing to do (at least for me) where Vue was complaining about mutating a prop (children shouldn’t change parent props apparently), I was about to give up at 8~ hour mark, but then it worked! then not only that, I came with another solution. Here are the two solutions:

Using watch:

const VueNavDrawerComp={
    props : ["childSideNav", "menuItems"],
    template : 
    /*html*/
    `
    <div>
        <v-navigation-drawer v-model="otherVar" absolute temporary>
            ...
        </v-navigation-drawer>
    </div>
    `,
    data(){
        return {
            otherVar : this.childSideNav
        };
    },
    watch : {
        childSideNav : function(newValue){
            this.otherVar=newValue;
        },
        otherVar : function(newValue){
            if(!newValue){
                this.$emit("ev-toggle-drawer");
            }
        }
    }
};

Using computed:

const VueNavDrawerComp={
    props : ["childSideNav", "menuItems"],
    template : 
    /*html*/
    `
    <div>
        <v-navigation-drawer v-model="computedSideNav" absolute temporary>
            ...
        </v-navigation-drawer>
    </div>
    `,
    data(){
        return {};
    },
    computed : {
        computedSideNav : {
            get(){
                return this.childSideNav;
            },
            set(newValue){
                if(!newValue){
                    this.$emit("ev-toggle-drawer");
                }
            }
        }
    }
};

Anyway, I fiddled around with some console logs, to try to see if things were firing the correct amount of times, and things like that, but I have no idea to how to find out which of the two is more optimized or have better "code taste" as the Linux guy would say.

Any experienced Vue.js can enlighten me in which is better option or if some corrections/improvements can be done?.

To clarify a bit, v-navigation-drawer is a Vuetify component, a Boolean is passed to affect the visibility of the Drawer. When the event is listened, we toggle a common shared Boolean across more components and this Boolean must be in sync with all of them.


Get this bounty!!!

#StackBounty: #javascript #vue.js #nuxt.js #html2canvas #web-share Share Image Via Social Media From PWA

Bounty: 250

In my Nuxt PWA I have a function that converts my HTML to Canvas using this package
The generated image is in base 64. Now I want to be able to share that image via: whatsapp,facebook,email,Instagram etc. I have found several packages but they all don’t seem to support sharing files only URLs and Text. I’m stuck right now and need help with this.

This is my share function:

shareTicket(index) {
  html2canvas(this.$refs['ticket-' + index][0], {
    backgroundColor: '#efefef',
    useCORS: true, // if the contents of screenshots, there are images, there may be a case of cross-domain, add this parameter, the cross-domain file to solve the problem
  }).then((canvas) => {
    let url = canvas.toDataURL('image/png') // finally produced image url

    if (navigator.share) {
      navigator.share({
        title: 'Title to be shared',
        text: 'Text to be shared',
        url: this.url,
      })
    }
  })

When I take out the if (navigator.share) condition I get an error in my console that navigator.share is not a function. I read somewhere that it only works on HTTPS so I uploaded to my staging server and tried but still got the same error.

Just to be clear I want to be able to share the generated image itself and not a URL.


Get this bounty!!!

#StackBounty: #javascript #authentication #vue.js JavaScript OAuth2.0 Client-side library that don't require use of social platform…

Bounty: 100

I am building a Single Page Application (VueJS JavaScript Framework) and I need to use my company’s enterprise identity server for user authentication.

Is there a recommended JavaScript library for handling client-sideĀ  OAuth2.0 flows? I need something like this JavaScript library (https://github.com/andreassolberg/jso) and I would use that library, but it is no longer maintained and I can’t get the libraries dependencies to pass security checks. It seems many JS OAuth2 libraries out there require the creator’s services (ie, Auth0, Okta, etc) and/or only make use of common social platforms like Facebook and Github, but my users can not use any third party providers other than my company’s enterprise identity server.

I need to pass a client Id, provide authorization URL, and redirectionURL to this library. The library should help with returning an access token to local storage.

Is there a recommended open-source library I can use that will allow me to use my own provider?


Get this bounty!!!