How to throw data to main App.vue from views? [duplicate] - javascript

I have two components:
App.vue
Sidekick.vue
In my App.vue component, I have a property that I would like to access from Sidekick.vue
App.vue
<template>
<div id="app">
<p>{{ myData }}</p>
<div class="sidebar">
<router-view/> // our sidekick component is shown here
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
myData: 'is just this string'
}
}
}
</script>
Sidekick.vue
<template>
<div class="sidekick">
{{ myData }}
</div>
</template>
<script>
export default {
name: 'Sidekick'
}
</script>
I would like access to myData (which is declared in App.vue) from Sidekick.vue
I have tried importing App.vue from within Sidekick.vue by doing something like:
Sidekick.vue (incorrect attempt)
<script>
import App from '#/App'
export default {
name: 'Sidekick',
data () {
return {
myData: App.myData
}
}
}
</script>
I have read about props - but have only seen references to child / parent components. In my case, Sidekick.vue is shown in a div inside App.vue (not sure if this makes it a "child"). Do I need to give access of myData to <router-view/> somehow?
UPDATE: (to show relationship between App.vue and Sidekick.vue
index.js (router file)
import Vue from 'vue'
import Router from 'vue-router'
import Sidekick from '#/components/Sidekick',
import FakeComponent from '#/components/FakeComponent'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
redirect: '/fakecomponent'
},
{
path: '/sidekick',
name: 'Sidekick',
component: Sidekick
},
{
path: '/fakecomponent',
name: 'FakeComponent',
component: FakeComponent
}
]
})
export default router
Sidekick.vue gets rendered when we hit /sidekick

Just keep in mind, the rule of thumb is using props to pass data in a one-way flow
props down, events up.
https://v2.vuejs.org/v2/guide/components.html#Composing-Components
Quick solution:
Global event bus to post messages between your <App/> and <Sidekick/> components.
https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
Long term solution:
Use a state management library like vuex to better encapsulates data in one place (a global store) and subscribe it from your components tree using import { mapState, mapMutations } from 'vuex'

When you have parent-child communication, the best and recommended
option is to use props and events. Read more in Vue docs
When want to have shared state between many components the best and
recommended way is to use Vuex.
If you want to use simple data sharing you can use Vue observable.
Simple example: Say that you have a game and you want the errors to be accessible by many components. (components can access it and manipulate it).
errors.js
import Vue from "vue";
export const errors = Vue.observable({ count: 0 });
Component1.vue
import { errors } from 'path-of-errors.js'
export default {
computed: {
errors () {
get () { return errors.count },
set (val) { errors.count = val }
}
}
}
In Component1 the errors.count is reactive. So if as a template you have:
<template>
<div>
Errors: {{ errors }}
<button #click="errors++">Increase</button>
</div>
</template>
While you click the Increase button, you will see the errors increasing.
As you might expect, when you import the errors.js in another component, then both components can participate on manipulating the errors.count.
Note: Even though you might use the Vue.observable API for simple data sharing you should be aware that this is a very powerful API. For example read Using Vue Observables as a State Store

App.vue:
<router-view pass_data='myData'/>
Sidekick.vue:
export default {
name: "Sidekick",
props: ["pass_data"],
created() {
alert("pass_data: "+this.pass_data)
}
}

If App.js(Parent) and Sidekick(Child)
App.js
in Template
In script
import Sidekick from './Sidekick.vue:
Sidekick.vue
props: ['myData']
now you can access myData anywhere in sidekick.
In template myData and
in scripts this.myData

Related

Pass data attribute to vue 3 root instance [duplicate]

I am terribly new to Vue, so forgive me if my terminology is off. I have a .NET Core MVC project with small, separate vue pages. On my current page, I return a view from the controller that just has:
#model long;
<div id="faq-category" v-bind:faqCategoryId="#Model"></div>
#section Scripts {
<script src="~/scripts/js/faqCategory.js"></script>
}
Where I send in the id of the item this page will go grab and create the edit form for. faqCategory.js is the compiled vue app. I need to pass in the long parameter to the vue app on initialization, so it can go fetch the full object. I mount it with a main.ts like:
import { createApp } from 'vue'
import FaqCategoryPage from './FaqCategoryPage.vue'
createApp(FaqCategoryPage)
.mount('#faq-category');
How can I get my faqCategoryId into my vue app to kick off the initialization and load the object? My v-bind attempt seems to not work - I have a #Prop(Number) readonly faqCategoryId: number = 0; on the vue component, but it is always 0.
My FaqCategoryPAge.vue script is simply:
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { Prop } from 'vue-property-decorator'
import Card from "#/Card.vue";
import axios from "axios";
import FaqCategory from "../shared/FaqCategory";
#Options({
components: {
Card,
},
})
export default class FaqCategoryPage extends Vue {
#Prop(Number) readonly faqCategoryId: number = 0;
mounted() {
console.log(this.faqCategoryId);
}
}
</script>
It seems passing props to root instance vie attributes placed on element the app is mounting on is not supported
You can solve it using data- attributes easily
Vue 2
const mountEl = document.querySelector("#app");
new Vue({
propsData: { ...mountEl.dataset },
props: ["message"]
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Vue 3
const mountEl = document.querySelector("#app");
Vue.createApp({
props: ["message"]
}, { ...mountEl.dataset }).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Biggest disadvantage of this is that everything taken from data- attributes is a string so if your component expects something else (Number, Boolean etc) you need to make conversion yourself.
One more option of course is pushing your component one level down. As long as you use v-bind (:counter), proper JS type is passed into the component:
Vue.createApp({
components: {
MyComponent: {
props: {
message: String,
counter: Number
},
template: '<div> {{ message }} (counter: {{ counter }}) </div>'
}
},
}).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app">
<my-component :message="'Hello from HTML'" :counter="10" />
</div>
Just an idea (not a real problem)
Not really sure but it can be a problem with Props casing
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you're using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents
Try to change your MVC view into this:
<div id="faq-category" v-bind:faq-category-id="#Model"></div>
Further to Michal Levý's answer regarding Vue 3, you can also implement that pattern with a Single File Component:
app.html
<div id="app" data-message="My Message"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const mountEl = document.querySelector("#app");
Vue.createApp(MyComponent, { ...mountEl.dataset }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
Or you could even grab data from anywhere on the parent HTML page, eg:
app.html
<h1>My Message</h1>
<div id="app"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const message = document.querySelector('h1').innerText;
Vue.createApp(MyComponent, { message }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
To answer TheStoryCoder's question: you would need to use a data prop. My answers above demonstrate how to pass a value from the parent DOM to the Vue app when it is mounted. If you wanted to then change the value of message after it was mounted, you would need to do something like this (I've called the data prop myMessage for clarity, but you could also just use the same prop name message):
<template>
{{ myMessage }}
<button #click="myMessage = 'foo'">Foo me</button>
</template>
<script>
export default {
props: {
message: String
},
data() {
return {
myMessage: this.message
}
}
};
</script>
So I'm not at all familiar with .NET and what model does, but Vue will treat the DOM element as a placeholder only and it does not extend to it the same functionality as the components within the app have.
so v-bind is not going to work, even without the value being reactive, the option is not there to do it.
you could try a hack to access the value and assign to a data such as...
const app = Vue.createApp({
data(){
return {
faqCategoryId: null
}
},
mounted() {
const props = ["faqCategoryId"]
const el = this.$el.parentElement;
props.forEach((key) => {
const val = el.getAttribute(key);
if(val !== null) this[key] = (val);
})
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="12">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
where you get the value from the html dom element, and assign to a data. The reason I'm suggesting data instead of props is that props are setup to be write only, so you wouldn't be able to override them, so instead I've used a variable props to define the props to look for in the dom element.
Another option
is to use inject/provide
it's easier to just use js to provide the variable, but assuming you want to use this in an mvc framework, so that it is managed through the view only. In addition, you can make it simpler by picking the exact attributes you want to pass to the application, but this provides a better "framework" for reuse.
const mount = ($el) => {
const app = Vue.createApp({
inject: {
faqCategoryId: {
default: 'optional'
},
},
})
const el = document.querySelector($el)
Object.keys(app._component.inject).forEach(key => {
if (el.getAttribute(key) !== null) {
app.provide(key, el.getAttribute(key))
}
})
app.mount('#app')
}
mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="66">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
As i tried in the following example
https://codepen.io/boussadjra/pen/vYGvXvq
you could do :
mounted() {
console.log(this.$el.parentElement.getAttribute("faqCategoryId"));
}
All other answers might be valid, but for Vue 3 the simple way is here:
import {createApp} from 'vue'
import rootComponent from './app.vue'
let rootProps = {};
createApp(rootComponent, rootProps)
.mount('#somewhere')

Run mutation on Route change VueJS?

So i am using Vuex and have a simple mutation set up which console logs a message. I have two routes setup, the /which goes to my HelloWorld component and /another which goes to the AnotherWorld component. I am trying to setup a watch on my route so that when the route changes, it fires off the mutation. I did setup a watch but it doesn't seem to be firing off my mutation.
Check out this CodeSandbox.
Check out the code snippet:-
This is My Vuex Store:-
mutations: {
routeChange() {
console.log("Helloooo!!!!!");
}
This is My Hello World Component:-
<template>
<div>
<h1>Hello World!!!</h1>
<router-link to="/another">Switch</router-link>
</div>
</template>
<script>
export default {
name: "HelloWorld",
watch: {
$route(to, from) {
this.$store.commit("routeChange");
}
}
};
</script>
This is my AnotherWorld Component:-
<template>
<div>
<h1>Another World</h1>
<router-link to="/">Back</router-link>
</div>
</template>
<script>
export default {};
</script>
As you can see i have setup the watch but it doesn't seem to be doing anything. Any help will be appreciated. Thank you.
I would instead use a global after hook navigation guard in your router.
For example
// router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '../store'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [...]
})
router.afterEach(() => {
store.commit('routeChange')
})
export default router
If you only want to catch route navigation away from certain components, you can use the beforeRouteLeave in-component guard
// MyComponent.vue
export default {
name: 'MyComponent',
beforeRouteLeave (to, from, next) {
this.$store.commit('routeChange')
next()
}
}
If you put watchers in individual route components they will never fire because $route is set before these components instantiation and is then changed after these components destruction.
You either need to put the watch in App.vue (parent of <router-view>), or commit from one of the vue-router guards (https://router.vuejs.org/guide/advanced/navigation-guards.html)

passing props data to component which i can only access with routes

My router file
import DomainAction from './components/domainaction/DomainAction.vue'
...
{ path: '/domainaction' , component: DomainAction },
...
router link File
...
<router-link to="/domainaction" tag="li" class="list-group-item "class-active="active" exact > Domain Action</router-link>
...
From another routes going to the domainaction Route like this
...
itemAction(action,data){
if(action=='Suspend'){
this.$router.push('/domainaction')
}
}
...
My Actual DomainAction component
<template>
.......
</template>
<script>
export default {
props:['data'],
data(){
return{
.........
</script>
I want to pass data props from itemAction function .
How do i achieve this ?
I'm new to vue js .Sorry if my question is not very complete.
Well if you just need to pass a property (a value) to the component, you just need to use data passing via the router's props: https://router.vuejs.org/guide/essentials/passing-props.html#function- mode.
And receive using this.$router within the component.
Alternatively you can use the Vuex for data passing.
https://vuex.vuejs.org/
So that you can make a communication between components outside this form that was informed, both components have to be parent and child, ie, 1 calls the other inside it, so yes you can pass the values, otherwise it should be used, or the data path via route, or via vuex which is the vue status manager.
There is also the following possibility, as requested in this question.
Passing props to Vue.js components instantiated by Vue-router
Note that sosmii is right about using a Vuex to share data across components.
But if you're looking into pass soma data as params using routes, you have to change a little bit how you're defining the components routes. Using a routers params you would be able to pass props, then you can push the route and add params. See example below.
And related question:
Passing props with programmatic navigation Vue.js
const DomainActionA = {
props: ['dataProp'],
template: '<div>A data: {{dataProp}}</div>'
}
const DomainActionB = {
props: ['dataProp'],
template: '<div>B data: {{dataProp}}</div>'
}
const routes = [
{
path: '/DomainActionA/:dataProp',
name: 'domainA',
component: DomainActionA,
props: true },
{
path: '/DomainActionB/:dataProp',
name: 'domainB',
component: DomainActionB,
props: true
}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router,
methods: {
goToA() {
this.$router.push({name: 'domainA', params: {dataProp: "blah"}})
},
goToB() {
this.$router.push({name: 'domainB', params: {dataProp: "bleh"}})
}
}
}).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello Router!</h1>
<div>
<button #click="goToA()">Go A with params: { data: "blah" }</button>
<button #click="goToB()">Go B with params: { data: "bleh" }</button>
</div>
<div>
<h2>router view</h2>
<router-view></router-view>
</div>
</div>
you should use vuex
props are designed to pass variables from a parent component to child components,
so it is unusual to use it when you want to share data between pages.
an example of right usage of prop:
parent.vue
<child-component :data="data">
<child-component :data="anotherData">
so, if you want to share variables between pages, use vuex (store pattern) instead.
similar question:
https://stackoverflow.com/a/40955110/10440108

Vuejs Share Data Between Components

I have two components:
App.vue
Sidekick.vue
In my App.vue component, I have a property that I would like to access from Sidekick.vue
App.vue
<template>
<div id="app">
<p>{{ myData }}</p>
<div class="sidebar">
<router-view/> // our sidekick component is shown here
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
myData: 'is just this string'
}
}
}
</script>
Sidekick.vue
<template>
<div class="sidekick">
{{ myData }}
</div>
</template>
<script>
export default {
name: 'Sidekick'
}
</script>
I would like access to myData (which is declared in App.vue) from Sidekick.vue
I have tried importing App.vue from within Sidekick.vue by doing something like:
Sidekick.vue (incorrect attempt)
<script>
import App from '#/App'
export default {
name: 'Sidekick',
data () {
return {
myData: App.myData
}
}
}
</script>
I have read about props - but have only seen references to child / parent components. In my case, Sidekick.vue is shown in a div inside App.vue (not sure if this makes it a "child"). Do I need to give access of myData to <router-view/> somehow?
UPDATE: (to show relationship between App.vue and Sidekick.vue
index.js (router file)
import Vue from 'vue'
import Router from 'vue-router'
import Sidekick from '#/components/Sidekick',
import FakeComponent from '#/components/FakeComponent'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
redirect: '/fakecomponent'
},
{
path: '/sidekick',
name: 'Sidekick',
component: Sidekick
},
{
path: '/fakecomponent',
name: 'FakeComponent',
component: FakeComponent
}
]
})
export default router
Sidekick.vue gets rendered when we hit /sidekick
Just keep in mind, the rule of thumb is using props to pass data in a one-way flow
props down, events up.
https://v2.vuejs.org/v2/guide/components.html#Composing-Components
Quick solution:
Global event bus to post messages between your <App/> and <Sidekick/> components.
https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
Long term solution:
Use a state management library like vuex to better encapsulates data in one place (a global store) and subscribe it from your components tree using import { mapState, mapMutations } from 'vuex'
When you have parent-child communication, the best and recommended
option is to use props and events. Read more in Vue docs
When want to have shared state between many components the best and
recommended way is to use Vuex.
If you want to use simple data sharing you can use Vue observable.
Simple example: Say that you have a game and you want the errors to be accessible by many components. (components can access it and manipulate it).
errors.js
import Vue from "vue";
export const errors = Vue.observable({ count: 0 });
Component1.vue
import { errors } from 'path-of-errors.js'
export default {
computed: {
errors () {
get () { return errors.count },
set (val) { errors.count = val }
}
}
}
In Component1 the errors.count is reactive. So if as a template you have:
<template>
<div>
Errors: {{ errors }}
<button #click="errors++">Increase</button>
</div>
</template>
While you click the Increase button, you will see the errors increasing.
As you might expect, when you import the errors.js in another component, then both components can participate on manipulating the errors.count.
Note: Even though you might use the Vue.observable API for simple data sharing you should be aware that this is a very powerful API. For example read Using Vue Observables as a State Store
App.vue:
<router-view pass_data='myData'/>
Sidekick.vue:
export default {
name: "Sidekick",
props: ["pass_data"],
created() {
alert("pass_data: "+this.pass_data)
}
}
If App.js(Parent) and Sidekick(Child)
App.js
in Template
In script
import Sidekick from './Sidekick.vue:
Sidekick.vue
props: ['myData']
now you can access myData anywhere in sidekick.
In template myData and
in scripts this.myData

How to pass props to all routes in vue.js?

I want to pass BASE_URL to all components. My App.js is like:
<template>
<router-view></router-view>
</template>
<script>
import addJoke from './components/addJoke.vue'
import showJokesAll from './components/showJokesAll.vue'
export default {
components: {
'add-joke': addJoke,
'show-jokes-all': showJokesAll
},
data () {
return {
BASE_URL : 'http://127.0.0.1:8090'
}
}
}
</script>
<style>
</style>
And the routes.js:
import showJokesAll from './components/showJokesAll.vue';
import addJoke from './components/addJoke.vue';
export default [
{path:'/', component: showJokesAll, props: {BASE_URL: 'http://127.0.0.1:8090'} },
{path:'/add', component: addJoke, props: {BASE_URL: 'http://127.0.0.1:8090'} }
]
and in showJokesAll component I have:
<script>
import axios from 'axios';
export default {
name: 'showJokesAll',
props: ['BASE_URL'],
data () {
return {
jokes:[]
}
},
methods: {
},
created() {
axios.get( BASE_URL + '/api/jokes').then( response => this.jokes = response.data);
}
}
</script>
But the components is not received BASE_URL.
[Vue warn]: Error in created hook: "ReferenceError: BASE_URL is not
defined"
How can I fix this?
To access the prop define with props: ['BASE_URL'], you would use this.BASE_URL:
axios.get( this.BASE_URL + '/api/jokes').then(/*...*/)
You can write a Mixins file which contains data or a function which returns your BASE_URL and then import mixins file using,
import myMixins from './my_mixins.js'
Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.
If you want to manage the state of the data you should have a look at Vuex.
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
Updated:
Also have a look a Vue Instance Properties.

Categories

Resources