No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    data key value changed in dialog route not being seen by other routes

    Help
    vue data quasar
    2
    10
    2896
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      dgk last edited by dgk

      I have a button icon on the toolbar which brings up a dialog (by route). When the user enters a pin correctly a “adminMode” key on the vue data is set to true and then it’s routed back to the “home” subroute. In the toolbar there is a drawer menu that is only visable when this mode is set and yet it doesn’t appear. If I set the mode in the code for the home route it does show up so must me my misunderstanding about how vue data keys are updated between components/routes

      Could be just my ignorance of vue in general or it might be something to do with pushing a subroute not refreshing the toobar ofthe parent (index) router.ng this repo

      I am modifying this code https://github.com/claustres/quasar-feathers-tutorialy
      by @luc-claustres

      dialog route to Admin.vue

      <template>
      </template>
      
      <script>
      import { Toast, Dialog } from 'quasar'
      // import api from 'src/api'
      
      export default {
        data () {
          return {
          }
        },
        computed: {
        },
        methods: {
          login (pin) {
      //     return api.authenticate({
      //        strategy: 'local',
      //        password: pin     })
            if (pin === '1234') {
              return Promise.resolve()
            }
            else {
              return Promise.reject()
            }
          }
        },
        mounted () {
          Dialog.create({
            title: 'Enter Administrator Mode',
            form: {
              pin: {
                type: 'password',
                label: 'Pin',
                model: ''
              }
            },
            buttons: [
              {
                label: 'Ok',
                handler: (data) => {
                  this.login(data.pin)
                  .then(_ => {
                    this.$data.adminMode = true
                    Toast.create.positive('You are now in admin mode')
                    this.$router.push({ name: 'home' })
                  })
                  .catch(_ => {
                    Toast.create.negative('Incorrect Pin - Try Again')
                    this.$router.push({ name: 'home' })
                  })
                }
              }
            ]
          })
        },
        beforeDestroy () {
        }
      }
      </script>
      
      <style lang="styl">
      
      </style>
      

      Index.vue

      <template>
        <q-layout>
          <div slot="header" class="toolbar">
      
            <button @click="$refs.menu.open()" v-show="admin_mode">
              <i>menu</i>
              <q-tooltip anchor="bottom middle" self="top middle" :offset="[0, 20]">Menu</q-tooltip>
            </button>
      
            <q-toolbar-title :padding="0">
              Home Lighting
            </q-toolbar-title>
      
            <button class="primary circular" @click="goTo('admin')" v-show="admin&&!admin_mode">
              <i>settings</i>
              <q-tooltip anchor="bottom middle" self="top middle" :offset="[0, 20]">Enter Admin Mode</q-tooltip>
            </button>
      
             <button class="primary circular" @click="admin_mode_off()" v-show="admin_mode">
              <i>stop</i>
              <q-tooltip anchor="bottom middle" self="top middle" :offset="[0, 20]">Exit Admin Mode</q-tooltip>
            </button>
        
            
            <!--q-tabs slot="navigation">
              <q-tab route="/singin" exact replace>Sign In</q-tab>
              <q-tab route="/singup" exact replace>Register</q-tab>
              <q-tab icon="featured_play_list" route="/chat" exact replace>Your Tasks</q-tab>
            </q-tabs-->
      
          </div>
          
          <!-- system settings menu admin only -->
          <q-drawer swipe-only left-side ref="menu" >
            <div class="toolbar light">
              <i>menu</i>
              <q-toolbar-title :padding="1">
                  Menu
              </q-toolbar-title>
            </div>
      
            <q-drawer-link icon="home" to="/chat">Home</q-drawer-link>
            <q-drawer-link icon="chat" to="/chat">Chat</q-drawer-link>
      
            <q-collapsible icon="info" label="About">
              <p style="padding: 25px;" class="text-grey-7">
                This is a template project combining the power of Quasar and Feathers to create real-time web apps.
              </p>
            </q-collapsible>
          </q-drawer>
      
          <!-- sub-routes -->
          <router-view class="layout-view" :user="user"></router-view>
          
        </q-layout>
      </template>
      
      <script>
      import { Toast } from 'quasar'
      import api from 'src/api'
      import users from 'src/users'
      
      export default {
        data () {
          return {
            user: null
          }
        },
        computed: {
          authenticated () {
            return users.authenticated()
          },
          admin () {
            return users.admin()
          },
          admin_mode () {
            return this.$data.adminMode
          }
        },
        methods: {
          goTo (route) {
            this.$router.push({ name: route })
          },
          admin_mode_off () {
            this.$data.adminMode = false
          },
          getUser (accessToken) {
            return api.passport.verifyJWT(accessToken)
            .then(payload => {
              return api.service('users').get(payload.userId)
            })
            .then(user => {
              this.$data.user = user
              return user
            })
          }
        },
        mounted () {
          // Check if there is already a session running
          api.authenticate()
          .then((response) => {
            return this.getUser(response.accessToken)
          })
          .then(user => {
            Toast.create.positive('Restoring previous session')
          })
          .catch(_ => {
            this.$router.push({ name: 'home' })
          })
          // On successfull login
          api.on('authenticated', response => {
            this.getUser(response.accessToken)
            .then(user => {
              this.$router.push({ name: 'home' })
            })
          })
          // On logout
          api.on('logout', () => {
            this.$data.user = null
            this.$router.push({ name: 'home' })
          })
        },
        beforeDestroy () {
        }
      }
      </script>
      
      <style lang="styl">
      
      </style>
      

      hHomerouter.vue sub

      <template>
           <div class="layout-padding">
            <div class="column items-center">
              <div v-if="authenticated">
                <div v-if="admin">
                     <p> admin user </p>
                  <div v-if="admin_mode">
                     <p> in admin mode </p>
                  </div>
                  </div>
                <div v-else >
                <p> authenticated user </p>
               </div>
             </div>
             <div v-else >
             <p> See system administrator for how authenticate on system </p>
             </div>
          </div>
        </div>
      </template>
      
      <script>
      import users from 'src/users'
      
      export default {
        props: ['user'],
        data () {
          return {
          }
        },
        computed: {
          authenticated () {
            return users.authenticated()
          },
          admin () {
            return users.admin()
          },
          admin_mode () {
            return users.admin_mode()
          }
      
        },
        methods: {
        },
        mounted () {
        },
        beforeDestroy () {
        }
      }
      </script>
      
      <style lang="styl">
      
      </style>
      
      1 Reply Last reply Reply Quote 0
      • S
        spectrolite last edited by spectrolite

        this.$data.adminMode = true
        Most probably, this is the statement that does not work.
        You should probably read these two paragraphs:
        https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
        https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties
        What this basically means (and before you go and use $set, which should be a last resort), is that you should declare all of the values that need reactivity in your “data” section, just set dummy/empty values even if you know they’ll be overwritten by some function immediately. Not declaring them makes it all NOT work as you would expect.
        So just try and do this:

        <template>
        </template>
        
        <script>
        import { Toast, Dialog } from 'quasar'
        // import api from 'src/api'
        
        export default {
          data () {
            return {
              adminMode: false
            }
          },
          compu...
        

        and report back

        1 Reply Last reply Reply Quote 0
        • D
          dgk last edited by

          From the original code I had seen that data keys were being “preset/declared” like for user. I tried what you suggested before and that did not work. I also tried putting that in the Index.vue data export and the Home.vue data export sections thinking it might need to be “preset/declared” before the dialog is fired. All those didn’t work. I should have said I tried this already, but this is why I am asking as that didn’t work.

          1 Reply Last reply Reply Quote 0
          • D
            dgk last edited by dgk

            per the vue2 page referenced i tried putting the “declaration” in main.js like this.

            Quasar.start(() => {
              /* eslint-disable no-new */
              new Vue({
                data: {
                  adminMode: false
                },
                router,
                el: '#q-app',
                render: h => h(require('./App'))
              })
            })
            

            but still no love

            1 Reply Last reply Reply Quote 0
            • D
              dgk last edited by

              even tried $set as last resort and still no “reactivity”. I’m out of ideas.

                        label: 'Ok',
                        handler: (data) => {
                          this.login(data.pin)
                          .then(_ => {
                            // this.$data.adminMode = true
                            this.$set(this.$data, 'adminMode', true)
                            Toast.create.positive('You are now in admin mode')
                            this.$router.push({ name: 'home' })
                          })
                          .catch(_ => {
                            Toast.create.negative('Incorrect Pin - Try Again')
                            this.$router.push({ name: 'home' })
                          })
              
              1 Reply Last reply Reply Quote 0
              • D
                dgk last edited by dgk

                A bit of investigation with the console and it seems that my computed function in Index.vue is not called when the value of adminMode is changed in the Admin.vue dialog. If it is never called the the v-show in the template never calls it (except when it is first rendered).

                admin_mode () {
                      return this.$data.adminMode
                    }
                
                       <button class="primary circular" @click="admin_mode_off()" v-show="admin_mode">
                        <i>stop</i>
                        <q-tooltip anchor="bottom middle" self="top middle" :offset="[0, 20]">Exit Admin Mode</q-tooltip>
                      </button>
                

                I’m to much a noob to express this but it seems that change in the DOM data is not triggering the re-rendering of the Index.vue template.
                So still scratching my head on how to get this to happen

                1 Reply Last reply Reply Quote 0
                • S
                  spectrolite last edited by spectrolite

                  Maybe there’s something wrong with my reasoning, in this case I truly hope someone with better Vue expertise will correct me.
                  In my mind computed properties are for stuff that is expensive to calculate, and you don’t want it recalculated each time it’s used.
                  So the computed value is recalculated only when some other value V that it depends on has changed.
                  I assume that V must also be a piece of reactive Vue data for this to happen.
                  You should not need to use $data nor $set.

                  I hope this gets you further along.
                  It seems your search for a solution has got you a bit confused. I would start from scratch, right after reading again a couple of Vue docs regarding computed and reactivity, and keeping these last comments in mind.

                  Or maybe computed is simply not the right tool for the job, and maybe a method would work best for you.

                  1 Reply Last reply Reply Quote 0
                  • S
                    spectrolite last edited by

                    It seems I remembered correctly
                    from https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods
                    […] computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed. This means as long as message has not changed, multiple access to the reversedMessage computed property will immediately return the previously computed result without having to run the function again.

                    1 Reply Last reply Reply Quote 0
                    • D
                      dgk last edited by

                      @spectrolite thx

                      After further study it seems that the issue is vue-router and the parent/child subroutes and the <vrouter-view> tag. Passing state between is not as trivial as when one embeds a child component directly in a parent template. If I embed my child component (Admin.vue) directly in Index.Vue it works (reactivity works).

                      So passing state between routed children I guess is not so trivial. I’ve decided to try vuex as it has some built in ways to pass state to routed child components.

                      Still if someone has a best practices way to modify(mutate) the state in a child (called by a vue-router) that will then re-render in the parent please share

                      1 Reply Last reply Reply Quote 0
                      • D
                        dgk last edited by

                        I still haven’t figured out two way data between parent and child routes but I decided to have the admin dialog be a method in parent instead of a child route and that solves my particular issue of state. So we’ll call this solved.

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post