在按钮上单击内部组件,调用父方法。怎么样?

时间:2019-09-09 17:05:10

标签: javascript vue.js vue-component

下面是一个包含3个notify()方法的功能性应用程序。

在组件索引中找到第一个notify()方法。

在组件登录中找到第二个notify()方法。

在父notify()实例中找到第三个App方法。

问题:

如何通过点击登录按钮来调用在父notify()实例中找到的第三个App

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple Vue.js Router Example</title>

    <!-- VUE JS v2.6.1 -->
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <!-- VUE ROUTER JS v3.1.3 -->
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <!-- BOOTSTRAP CSS v4.3.1 -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <!-- GOOGLE FONT CSS - Roboto Mono -->
    <link href="https://fonts.googleapis.com/css?family=Roboto+Mono:100,300,400,500,700&display=swap" rel="stylesheet">
    <!-- GOOGLE FONT CSS - Material Icons -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <!-- CSS -->

    <style type="text/css">

      body {
        font-family: 'Roboto Mono', monospace;
        font-weight: 400;
        font-size: 1rem;
        background-color: #d7ccc8;
      }

      .active {
        color: MediumSeaGreen;
      }

      .exactActive {
        color: #e57373;
      }

    </style>

  </head>

  <body>

    <!--  
      HTML

    -->
    <!-- VUE APP - PARENT CONTAINER -->
    <div id="app" class="container">

      <!-- HEADER CONTAINER -->
      <header>

        <!--  
          LINKS

        -->
        <!-- ROUTER LINK(S) -->
        <ul>
          <li>
            <router-link tag="a" to="/" exact>/index</router-link>
          </li>
          <li>
            <router-link tag="a" to="/sign-in">/sign-in</router-link>
          </li>

        </ul>

        <!--  
          VIEWS

        -->
        <!-- ANY OF THESE CAN/DO OR DO NOT RENDER -->
        <!-- DEPENDS ON ROUTER-LINK SELECTED -->

        <router-view name="RTV_index"></router-view>
        <router-view name="RTV_sign_in"></router-view>

      </header>

    </div>

    <!--  
      JAVASCRIPT

    -->

    <!-- JAVA SCRIPT -->
    <script type="text/javascript">

      // DISABLE
      Vue.config.productionTip = false;
      // DISABLE
      Vue.config.devtools = false;


      /*
        COMPONENTS

      */

      // COMPONENT INDEX
      const CPT_index = { 

        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #bcaaa4;">
          <strong>Component:</strong> CPT_index
          <br>
          <strong>Route :</strong> ./
          <br>
          <strong>Params:</strong> none
          <br>
          <button type="button" v-on:click="notify()">NOTIFY</button>
        </div>
        `,
        // COMPONENT SIGN IN - METHODS
        methods:{
          notify: function() {
            console.log('Hi! I am a method inside a local component CPT_index.');
          }
        }

      };

      // COMPONENT SIGN IN
      const CPT_sign_in = { 

        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #bcaaa4;">
          <strong>Component:</strong> CPT_sign_in
          <br>
          <strong>Route :</strong> ./sign-in
          <br>
          <strong>Params:</strong> none
          <br>
          <button type="button" v-on:click="notify()">NOTIFY</button>
        </div>
        `,
        // COMPONENT SIGN IN - METHODS
        methods:{
          notify: function() {
            console.log('Hi! I am a method inside a local component CPT_sign_in.');
          }
        }


      };

      // COMPONENT 404
      const CPT_404 = { 

        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #ef9a9a;">
          <strong>CPT_404</strong>
          <br>
          <strong>Route :</strong> ./404
          <br>
          <strong>Params:</strong> none
        </div>
        `
      }

      /*
        ROUTER

      */
      // VUE.JS ROUTER INSTANCE
      const router = new VueRouter({

      // IN THIS ROUTE I WILL RENDER THESE COMPONENTS..

        // ROUTER MODE - hash, history
        mode: 'hash',

        base: '/',

        // CSS FOR ROUTER-LINK ACTIVE
        linkActiveClass: "active",
        // CSS FOR ROUTER-LINK exact ACTIVE
        linkExactActiveClass: "exactActive",

        // ROUTES(S) TO EVALUATE IN ORDER
        routes: [

          /*
            ROUTES

          */
          // ROUTE INDEX
          { name: 'index',
            path: '/',
            // ROUTE(S) WITH COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_index: CPT_index,
            }
          }, // END ROUTE INDEX

          // --------------------------------------------------------------------------------

          // ROUTE SIGN IN
          { name: 'sign_in',
            path: '/sign-in',
            // ROUTE(S) WITH COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_sign_in: CPT_sign_in,
            }
          }, // END ROUTE SIGN IN

          // --------------------------------------------------------------------------------

          /*
            REDIRECT

          */
          // ROUTE REDIRECT
          { name: null,
            path: '*', redirect: { name: '404' },
            // TRAP ANY UNDEFINED ROUTE AND...
            // FORWARD IT TO /404 ROUTE
          }, // END ROUTE REDIRECT


          /*
            TO /404

          */
          // ROUTE 404
          { name: '404',
            path: '/404',
            // ROUTE COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_404: CPT_404,
            }
          }, // END ROUTE 404

        ] // END ROUTES(S) TO EVALUATE IN ORDER

      });



      /*
       APP

      */
      // VUE.JS APP INSTANCE
      const App = new Vue({
        // ROOT ELEMENT
        el: '#app',
        // ROUTER
        router,

        /*
          DATA

        */
        // APP DATA (SINGLE SOURCE OF TRUTH)
        data: {
          data0: true,
          data1: 'Data1 - Rendered successfuly.',
          data2: 'Data2 - Rendered successfuly.',
          data3: 'Data3 - Rendered successfuly.',

        },

        /*
          COMPUTED

        */
        // COMPUTED PROPERTIES
        computed: {


        },

        /*
          METHODS

        */
        // APP METHODS
        methods:{
          //Initialize App.
          init: function() {
            
            console.log('App initialized.');

            if (this.supportsLocalSessionStorage()) {
              console.log('HTML5 localStorage & sessionStorage Supported.');
            } else {
              console.error('Fatal Error: No HTML5 localStorage & sessionStorage support.');
            }

          },

          // HTML5 Local & Session sotrage detection
          supportsLocalSessionStorage: function() {
            return typeof(Storage) !== 'undefined';
          },

          notify: function() {
            console.log('Hi! I am a method inside the parent instance App.');
          }

        }, // END APP METHODS

        /*
          HOOKS

        */
        // APP LIFE CYCLE HOOKS
        mounted(){
          this.$nextTick(function () {
            // Code that will run only after the
            // entire view has been rendered
            // Initialize App
            this.init();
          })

        }

      });


    </script>

  </body>

</html>

2 个答案:

答案 0 :(得分:3)

要实现您想要的目标,只需更改:

<button type="button" v-on:click="notify()">NOTIFY</button>

<button type="button" v-on:click="$parent.notify()">NOTIFY</button>

但是,由于“父级”是$ root组件,因此您可以实际调用:

<button type="button" v-on:click="$root.notify()">NOTIFY</button>

将绕过以下使用父级的陷阱。调用$ parent时,您正在耦合组件层次结构,这将使重用这些组件变得困难。您可能要考虑使用$ emit的事件驱动系统。

<button type="button" v-on:click="$emit('notify')">NOTIFY</button>

然后在任何父组件上,您将像这样监听'notify':

mounted () {
  this.$on('notify', (event) => {
    // do something in your case it would be
    this.notify()
  })
}

您可能还需要考虑实现事件总线以捕获应用程序中任何位置的事件。

一个简单的例子是:

在main.js中添加:

Vue.prototype.$bus = new Vue

在组件中添加:

<button type="button" v-on:click="$bus.$emit('notify')">NOTIFY</button>

然后在任何其他组件中可以添加:

mounted () {
  this.$bus.$on('notify', (event) => {
    // do something in your case it would be
  })
}

希望这会有所帮助!

答案 1 :(得分:0)

我已经更新了原始示例以反映所有更改。现在,我们有了一个事件总线原型,并且为清楚起见重新标记了组件和方法。这个功能示例应该使您对Vue路由器,路由,本地组件,事件总线和简单的axios获取请求有基本了解。

我添加了ASCII注释,如果您具有SublimeText 3,这些注释看起来很棒。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple Vue.js Router Example</title>

    <!-- VUE JS v2.6.1 -->
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <!-- VUE ROUTER JS v3.1.3 -->
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <!-- AXIOS JS v0.19.0 -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- BOOTSTRAP CSS v4.3.1 -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <!-- GOOGLE FONT CSS - Roboto Mono -->
    <link href="https://fonts.googleapis.com/css?family=Roboto+Mono:100,300,400,500,700&display=swap" rel="stylesheet">
    <!-- GOOGLE FONT CSS - Material Icons -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <!-- CSS -->

    <style type="text/css">

      body {
        font-family: 'Roboto Mono', monospace;
        font-weight: 400;
        font-size: 1rem;
        background-color: #d7ccc8;
      }

      .active {
        color: MediumSeaGreen;
      }

      .exactActive {
        color: #e57373;
      }

    </style>

  </head>

  <body>

    <!-- 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
                                                                                                               
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
     -->

    <!--  
      ##     ## ######## ##     ## ##       
      ##     ##    ##    ###   ### ##       
      ##     ##    ##    #### #### ##       
      #########    ##    ## ### ## ##       
      ##     ##    ##    ##     ## ##       
      ##     ##    ##    ##     ## ##       
      ##     ##    ##    ##     ## ######## 

    -->
    <!-- VUE APP - PARENT CONTAINER -->
    <div id="app" class="container">

      <!-- HEADER CONTAINER -->
      <header>
        <hr>
        <h4>Static Nav</h4>
        <p>by router-link</p>

        <!--  
          ########   #######  ##     ## ######## ######## ########     ##       #### ##    ## ##    ##  ######  
          ##     ## ##     ## ##     ##    ##    ##       ##     ##    ##        ##  ###   ## ##   ##  ##    ## 
          ##     ## ##     ## ##     ##    ##    ##       ##     ##    ##        ##  ####  ## ##  ##   ##       
          ########  ##     ## ##     ##    ##    ######   ########     ##        ##  ## ## ## #####     ######  
          ##   ##   ##     ## ##     ##    ##    ##       ##   ##      ##        ##  ##  #### ##  ##         ## 
          ##    ##  ##     ## ##     ##    ##    ##       ##    ##     ##        ##  ##   ### ##   ##  ##    ## 
          ##     ##  #######   #######     ##    ######## ##     ##    ######## #### ##    ## ##    ##  ######  

        -->
        <!-- ROUTER LINK(S) -->
        <ul>
          <li>
            <router-link tag="a" to="/" exact>/index</router-link>
          </li>
          <li>
            <router-link tag="a" to="/sign-in">/sign-in</router-link>
          </li>
          <li>
            <router-link tag="a" to="/404">/404</router-link>
          </li>

        </ul>

        <!--  
          ########   #######  ##     ## ######## ######## ########     ##     ## #### ######## ##      ##  ######  
          ##     ## ##     ## ##     ##    ##    ##       ##     ##    ##     ##  ##  ##       ##  ##  ## ##    ## 
          ##     ## ##     ## ##     ##    ##    ##       ##     ##    ##     ##  ##  ##       ##  ##  ## ##       
          ########  ##     ## ##     ##    ##    ######   ########     ##     ##  ##  ######   ##  ##  ##  ######  
          ##   ##   ##     ## ##     ##    ##    ##       ##   ##       ##   ##   ##  ##       ##  ##  ##       ## 
          ##    ##  ##     ## ##     ##    ##    ##       ##    ##       ## ##    ##  ##       ##  ##  ## ##    ## 
          ##     ##  #######   #######     ##    ######## ##     ##       ###    #### ########  ###  ###   ######  

        -->
        <!-- ANY OF THESE CAN/DO OR DO NOT RENDER -->
        <!-- RELATIVE TO ROUTER-LINK CURRENTLY SELECTED -->

        <router-view name="RTV_index"></router-view>
        <router-view name="RTV_sign_in"></router-view>

        <router-view name="RTV_404"></router-view>

      </header>

    </div>

    <!-- 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
                                                                                                               
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
      ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
     -->

    <!--  
            ##    ###    ##     ##    ###        ######   ######  ########  #### ########  ######## 
            ##   ## ##   ##     ##   ## ##      ##    ## ##    ## ##     ##  ##  ##     ##    ##    
            ##  ##   ##  ##     ##  ##   ##     ##       ##       ##     ##  ##  ##     ##    ##    
            ## ##     ## ##     ## ##     ##     ######  ##       ########   ##  ########     ##    
      ##    ## #########  ##   ##  #########          ## ##       ##   ##    ##  ##           ##    
      ##    ## ##     ##   ## ##   ##     ##    ##    ## ##    ## ##    ##   ##  ##           ##    
       ######  ##     ##    ###    ##     ##     ######   ######  ##     ## #### ##           ##    

    -->
    <!-- JAVA SCRIPT -->
    <script type="text/javascript">

      // DISABLE
      Vue.config.productionTip = false;
      // DISABLE
      Vue.config.devtools = false;

      /*
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 

        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        
      */

      /*
         ######   #######  ##     ## ########   #######  ##    ## ######## ##    ## ########  ######  
        ##    ## ##     ## ###   ### ##     ## ##     ## ###   ## ##       ###   ##    ##    ##    ## 
        ##       ##     ## #### #### ##     ## ##     ## ####  ## ##       ####  ##    ##    ##       
        ##       ##     ## ## ### ## ########  ##     ## ## ## ## ######   ## ## ##    ##     ######  
        ##       ##     ## ##     ## ##        ##     ## ##  #### ##       ##  ####    ##          ## 
        ##    ## ##     ## ##     ## ##        ##     ## ##   ### ##       ##   ###    ##    ##    ## 
         ######   #######  ##     ## ##         #######  ##    ## ######## ##    ##    ##     ######  

      */
      // COMPONENT INDEX
      const componentIndex = { 

        // HTML TEMPLATE
        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #bcaaa4;">
          <strong>Component:</strong> componentIndex
          <br>
          <strong>Route :</strong> ./
          <br>
          <strong>Params:</strong> none
          <br>
          <button type="button" v-on:click="componentMethod1()">componentMethod1()</button>
        </div>
        `,
        // COMPONENT SIGN IN - DATA
        data: function () {
          return {
            componentDataInt: 0,
            componentDataString: 'A',
            componentDataArray: [],
            componentDataObject: {},
          }
        },
        // COMPONENT SIGN IN - METHODS
        methods:{
          componentMethod1: function() {
            console.info('componentMethod1() inside local componentIndex called.');
          }
        }

      };

      // COMPONENT SIGN IN
      const componentSignIn = { 

        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #bcaaa4;">
          <strong>Component:</strong> componentSignIn
          <br>
          <strong>Route :</strong> ./sign-in
          <br>
          <strong>Params:</strong> none
          <br>
          <button type="button" v-on:click="componentMethod1()">componentMethod1() -> appMethod1()</button>
        </div>
        `,
        // COMPONENT DATA
        data: function () {
          return {
            componentDataInt: 0,
            componentDataString: 'A',
            componentDataArray: [],
            componentDataObject: {},
          }
        },
        // COMPONENT SIGN IN - METHODS
        methods:{
          // COMPONENT METHOD 1
          componentMethod1: function() {
            // EMIT A CALL TO A PARENT APP METHOD
            // this approach assures that our components remain decoupled from the app
            // so they can be re-used.
            // eg. We do not use $parent.appMenthod1() inside the button click; we instead, 
            // emmit a call to this app method using our Vue prototype Event Bus.
            console.info('componentMethod1() inside local componentSignIn called.');
            this.$appEventBus.$emit('appMethod1');
          }
        }

      };

      // COMPONENT 404
      const component404 = { 

        template: 
        `
        <!-- HTML PARENT CONTAINER -->
        <div style="background-color: #ef9a9a;">
          <strong>Component:</strong> component404
          <br>
          <strong>Route :</strong> ./404
          <br>
          <strong>Params:</strong> none
        </div>
        `,
        // COMPONENT DATA
        data: function () {
          return {
            componentDataInt: 0,
            componentDataString: 'A',
            componentDataArray: [],
            componentDataObject: {},
          }
        },
        // COMPONENT 404 - METHODS
        methods:{
          // COMPONENT METHOD 404
          componentMethod404: function() {
            // DEBUG
            console.log('componentMethod404 called.');
          }
        }

      };


      /*
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        
      */


      /*
        ########   #######  ##     ## ######## ######## ########  
        ##     ## ##     ## ##     ##    ##    ##       ##     ## 
        ##     ## ##     ## ##     ##    ##    ##       ##     ## 
        ########  ##     ## ##     ##    ##    ######   ########  
        ##   ##   ##     ## ##     ##    ##    ##       ##   ##   
        ##    ##  ##     ## ##     ##    ##    ##       ##    ##  
        ##     ##  #######   #######     ##    ######## ##     ## 

      */
      // VUE.JS ROUTER INSTANCE
      const router = new VueRouter({

      // IN THIS ROUTE I WILL RENDER THESE COMPONENTS..

        // ROUTER MODE - hash, history
        mode: 'hash',

        base: '/',

        // CSS FOR ROUTER-LINK ACTIVE
        linkActiveClass: "active",
        // CSS FOR ROUTER-LINK exact ACTIVE
        linkExactActiveClass: "exactActive",

        // ROUTES(S) TO EVALUATE IN ORDER
        routes: [

          /*
            ########   #######  ##     ## ######## ########  ######  
            ##     ## ##     ## ##     ##    ##    ##       ##    ## 
            ##     ## ##     ## ##     ##    ##    ##       ##       
            ########  ##     ## ##     ##    ##    ######    ######  
            ##   ##   ##     ## ##     ##    ##    ##             ## 
            ##    ##  ##     ## ##     ##    ##    ##       ##    ## 
            ##     ##  #######   #######     ##    ########  ######  

          */

          // --------------------------------------------------------------------------------

          // ROUTE INDEX
          { name: 'index',
            path: '/',
            // ROUTE(S) WITH COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_index: componentIndex,
            }
          }, // END ROUTE INDEX

          // --------------------------------------------------------------------------------

          // ROUTE SIGN IN
          { name: 'sign_in',
            path: '/sign-in',
            // ROUTE(S) WITH COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_sign_in: componentSignIn,
            }
          }, // END ROUTE SIGN IN

          // --------------------------------------------------------------------------------

          /*
            ########  ######## ########  #### ########  ########  ######  ######## 
            ##     ## ##       ##     ##  ##  ##     ## ##       ##    ##    ##    
            ##     ## ##       ##     ##  ##  ##     ## ##       ##          ##    
            ########  ######   ##     ##  ##  ########  ######   ##          ##    
            ##   ##   ##       ##     ##  ##  ##   ##   ##       ##          ##    
            ##    ##  ##       ##     ##  ##  ##    ##  ##       ##    ##    ##    
            ##     ## ######## ########  #### ##     ## ########  ######     ##    

          */
          // ROUTE REDIRECT
          { name: null,
            path: '*', redirect: { name: '404' },
            // TRAP ANY UNDEFINED ROUTE AND...
            // FORWARD IT TO /404 ROUTE
          }, // END ROUTE REDIRECT

          // --------------------------------------------------------------------------------

          /*
            ########  #######           ## ##          #####   ##        
               ##    ##     ##         ##  ##    ##   ##   ##  ##    ##  
               ##    ##     ##        ##   ##    ##  ##     ## ##    ##  
               ##    ##     ##       ##    ##    ##  ##     ## ##    ##  
               ##    ##     ##      ##     ######### ##     ## ######### 
               ##    ##     ##     ##            ##   ##   ##        ##  
               ##     #######     ##             ##    #####         ##  

          */
          // ROUTE 404
          { name: '404',
            path: '/404',
            // ROUTE COMPONENT(S) TO RENDER IN ORDER
            components: {
              // ONE OR MORE...
              RTV_404: component404,
            }
          }, // END ROUTE 404

        ] // END ROUTES(S) TO EVALUATE IN ORDER

      });


      /*
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## 
        
      */


      /*
           ###    ########  ########  
          ## ##   ##     ## ##     ## 
         ##   ##  ##     ## ##     ## 
        ##     ## ########  ########  
        ######### ##        ##        
        ##     ## ##        ##        
        ##     ## ##        ##        

      */
      // Implement an appEventBus to capture events 
      // ...from anywhere in our application.
      // We make it available to each Vue instance by
      // ...defining them on the prototype:
      Vue.prototype.$appEventBus = new Vue();

      // VUE.JS APP INSTANCE
      const App = new Vue({

        // ROOT ELEMENT
        el: '#app',

        // ROUTER
        router,

        // --------------------------------------------------------------------------------

        /*
             ###    ########  ########     ########     ###    ########    ###    
            ## ##   ##     ## ##     ##    ##     ##   ## ##      ##      ## ##   
           ##   ##  ##     ## ##     ##    ##     ##  ##   ##     ##     ##   ##  
          ##     ## ########  ########     ##     ## ##     ##    ##    ##     ## 
          ######### ##        ##           ##     ## #########    ##    ######### 
          ##     ## ##        ##           ##     ## ##     ##    ##    ##     ## 
          ##     ## ##        ##           ########  ##     ##    ##    ##     ## 

        */
        // APP DATA (SINGLE SOURCE OF TRUTH)
        data: {
          dataInt: 0,
          dataString: 'A',
          dataArray: [],
          dataObject: {},


        },

        // --------------------------------------------------------------------------------

        /*
           ######   #######  ##     ## ########  ##     ## ######## ######## ########  
          ##    ## ##     ## ###   ### ##     ## ##     ##    ##    ##       ##     ## 
          ##       ##     ## #### #### ##     ## ##     ##    ##    ##       ##     ## 
          ##       ##     ## ## ### ## ########  ##     ##    ##    ######   ##     ## 
          ##       ##     ## ##     ## ##        ##     ##    ##    ##       ##     ## 
          ##    ## ##     ## ##     ## ##        ##     ##    ##    ##       ##     ## 
           ######   #######  ##     ## ##         #######     ##    ######## ########  

        */
        // COMPUTED PROPERTIES
        computed: {
          // INFO...
          computed1: function() {
            return true;
          }

        },

        // --------------------------------------------------------------------------------

        /*
          ##      ##    ###    ########  ######  ##     ## 
          ##  ##  ##   ## ##      ##    ##    ## ##     ## 
          ##  ##  ##  ##   ##     ##    ##       ##     ## 
          ##  ##  ## ##     ##    ##    ##       ######### 
          ##  ##  ## #########    ##    ##       ##     ## 
          ##  ##  ## ##     ##    ##    ##    ## ##     ## 
           ###  ###  ##     ##    ##     ######  ##     ## 

        */
        // WATCH PROPERTIES
        wath: {
          // INFO...
          watch1: function() {
            return true;
          }

        },

        // --------------------------------------------------------------------------------

        /*
          ##     ## ######## ######## ##     ##  #######  ########   ######  
          ###   ### ##          ##    ##     ## ##     ## ##     ## ##    ## 
          #### #### ##          ##    ##     ## ##     ## ##     ## ##       
          ## ### ## ######      ##    ######### ##     ## ##     ##  ######  
          ##     ## ##          ##    ##     ## ##     ## ##     ##       ## 
          ##     ## ##          ##    ##     ## ##     ## ##     ## ##    ## 
          ##     ## ########    ##    ##     ##  #######  ########   ######  

        */
        // APP METHODS
        methods:{
          // INITIALIZE APP
          init: function() {
            
            // DEBUG
            console.log('App initialized.');

            // HTML5 - LOCAL & SESSION STORAGE CHECK
            if (this.supportsLocalSessionStorage()) {
              // DEBUG
              console.log('HTML5 localStorage & sessionStorage Supported.');
            } else {
              // DEBUG
              console.error('Fatal Error: No HTML5 localStorage & sessionStorage support.');
            }

          },

          // HTML5 - LOCAL & SESSION STORAGE DETECTION
          supportsLocalSessionStorage: function() {
            return typeof(Storage) !== 'undefined';
          },
          // APP METHOD 1
          appMethod1: function(event) {
            // DEBUG
            console.log('appMethod1() called.');
            console.info('axios request started...');
            // AXIOS GET REQUEST
            axios.get('https://jsonplaceholder.typicode.com/users')
              // handle success
              .then(function (response) { 
                console.info('axios response:');

                // LOOP THORUGH RESPONDE DATA ARRAY OF OBJECTS
                response.data.forEach(function (item, index) {
                    // DEBUG
                    console.log(index, item.name);
                });


              })
              // handle error
              .catch(function (error) { 
                console.error('axios error:', error); 
              })
              // always executed
              .finally(function ()  { 
                console.info('axios request complete.'); 
              });
          }

        }, // END APP METHODS

        // --------------------------------------------------------------------------------

        /*
          ##       #### ######## ########     ######  ##    ##  ######  ##       ######## 
          ##        ##  ##       ##          ##    ##  ##  ##  ##    ## ##       ##       
          ##        ##  ##       ##          ##         ####   ##       ##       ##       
          ##        ##  ######   ######      ##          ##    ##       ##       ######   
          ##        ##  ##       ##          ##          ##    ##       ##       ##       
          ##        ##  ##       ##          ##    ##    ##    ##    ## ##       ##       
          ######## #### ##       ########     ######     ##     ######  ######## ######## 


          ##     ##  #######   #######  ##    ##  ######  
          ##     ## ##     ## ##     ## ##   ##  ##    ## 
          ##     ## ##     ## ##     ## ##  ##   ##       
          ######### ##     ## ##     ## #####     ######  
          ##     ## ##     ## ##     ## ##  ##         ## 
          ##     ## ##     ## ##     ## ##   ##  ##    ## 
          ##     ##  #######   #######  ##    ##  ######  

        */
        // APP LIFE CYCLE HOOKS
        mounted(){
          // Our mounted hook doesn’t guarantee that the whole component tree renders.
          // so we use nextTick
          this.$nextTick(function () {
            // The whole view is now rendered, so we can safely access or query
            // the DOM.
            // Initialize App
            this.init();
          });

          // EventBus now listening for $emit('appMethod1')
          // coming from ANY v-on:event ANYwhere.
          this.$appEventBus.$on('appMethod1', (event) => {
            // DEBUG
            console.log('EventBus used to call a parent method.');
            // CALL APP METHOD 1
            this.appMethod1();
          });

        },

        // --------------------------------------------------------------------------------

      });


    </script>

  </body>

</html>