Communicating variable in vue.js between components - javascript

I am building app using laravel and vue. I have navbar, currently it looks like:
<template>
<nav class="navbar">
<p>{{msg}}</p>
</nav>
</template>
And I use it like here:
<body class="">
<div id="app">
<div class="">
<navbar></navbar>
#yield('content')
</div>
</div>
</body>
In yield I am loading another components, so I have navbar and another component together. Now I want to override that {{msg}} variable from navbar in another components. In every component that variable will be diferent.
I do not know how to override it in components and from {{msg}} do some text. Can you help me? (That code above is all what I have)

If you want to use msg in other components, then you need to use prop
Use like:
props: ['msg'],
Then, you need to bind it like:
<component-name :msg="msg"></component-name>
In your component, you can take it like:
<template>{{ msg }}</template>
Hope you understand!

Components can be communicate with props. You can transfer the data to another components and you can use if statement.
https://v2.vuejs.org/v2/guide/components.html#Props

Related

Nuxtjs: How to call component inside another component?

I understand paradigm "Page-component" but what if I have a page that renders component, how do I call another component inside this component? Currently nuxtjs does not allow me do it. I can not stick to standart "page-component" scheme as I am bulding cart which calls cart-items.
Say If a cart component which is called by page looks like this, how would it call cart-item component inside it?
<!---- cart component called from index.vue --->
<template>
<div>
<Cart-item></Cart-item> < ---------- This doesn't work.
</div>
</template>
<script>
export default {
props: ['items']
}
</script>
I managed it the standard way:
<template>
<div>
<CartItem></CartItem>
</div>
</template>
<script>
import CartItem from '../components/Cart-item'
export default {
props: ['items']
}
</script>
Since nuxtjs auto-registers all components wonder if there is more graceful way.
EDIT: as promised, here is an example on how to pass some content to a component from another one thanks to slots. This is totally working in any Nuxt page ofc.
NestedContent.vue
<template>
<div>
<p>Here is the NestedContent component and below is a slot passed to ParentWithSlots' component</p>
<hr />
<parent-with-slots>
<!-- <template #default> // this one can be omit since we do use the default slot here -->
<p>This content is inserted into the component ParentWithSlots</p>
<!-- </template> -->
</parent-with-slots>
</div>
</template>
ParentWithSlots.vue
<template>
<div>
<p>xxxxxxxxxxx ParentWithSlots' content before slot xxxxxxxxxxx</p>
<slot>Default content in case none is provided</slot>
<p>xxxxxxxxxxx ParentWithSlots' content after slot xxxxxxxxxxx</p>
</div>
</template>
Here is how it looks
PS: you may also give a try to layouts, it can be useful for overall positioning of some of your components visually.
If your components are in the components directory, you can set components: true in your nuxt.config.js and have access to it pretty much anywhere without any additional step with the <cart-item></cart-item> syntax.
More details here: https://nuxtjs.org/blog/improve-your-developer-experience-with-nuxt-components/

Render external element inside Vue component

I have an external div that I need to render inside my Vue app. I'm trying to use a slot, like but that's a no go as nothing renders.
Any ideas?
Goal is to have HTML like this (Vue mounts on #app):
<div id="app" data-slot-header="#header"></div>
<div id="header">
<h1>Title here</h1>
</div>
Then the Vue component
<template>
<div>
<slot name="header"></slot>
</div>
</template>
You can use a dynamic <component> and refer to your #header element as a template reference.
For example
new Vue({
data: () => ({
headerComponent: {
template: '#header' // refer to template element by selector
}
}),
}).$mount('#app')
#app:before,#header:before{position:absolute;top:0;right:0;color:rgba(1,1,1,.5);font-size:.8rem}#app{border:1px solid #666;position:relative}#app:before{content:'Vue app'}#header{position:relative;opacity:.5}#header:before{content:'Original header'}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<p>Dynamic component rendered here 👇</p>
<component :is="headerComponent"></component>
</div>
<div id="header">
<h1>Title here</h1>
</div>
Slots are mainly used with reusable Vue components so that the parent component can render custom stuff inside designated sections of the child. The root component does not have a parent, so it doesn't make sense to use slots for this.
Why can't you just hard-code the div in the template? Or do you need it to be dynamic; will you be swapping out the header contents in some situations? Please provide more information about what your use-case is, otherwise my answer is "just hard-code it".
Take a look at portal-vue. It allows child components to render templates anywhere in the DOM. This might work for your situation.

ng component not useful

I was learning the ng-content. Why need to use it when we can easily write like this
with ng-content
menu.component.html
<div>
<app-message> <h1>Laptop</h1></app-message>
</div>
message.component.html
<div>
<ng-content></ng-content>
<p>something text to be display</p>
<button > Submit</button>
</div>
without ng-content
menu.component.html
<div>
<h1>Laptop</h1>
<app-message> </app-message>
</div>
message.component.html
<div>
<p>something text to be display</p>
<button > Submit</button>
</div>
Using <ng-content> allows you to create flexible reusable components.
For example, a custom app-modal component:
<div class="modal">
<div class="modal-title">
{{title}}
</div>
<div class="modal-body">
<ng-content></ng-content>
</div>
</div>
This is very powerful, and only the start of what you can achieve with <ng-content>
A core prinpiple of software engineering is code reuse (DRY). With this we can bundle all of the logic relating to a component into one component, and inject the content into it.
This is like asking the question
Why not just declare your CSS styles inline instead of in CSS classes?
It is possible, but unmaintainable, and we have evolved beyond that.
Your example is fairly trivial, but it is still useful if you wanted to change style or behaviour based on some injected logic.
There exist some situations when is necessary, maybe no in simple cases but when the app grows up and you have to insert a custom component inside another is very usefully
As you can see from your own example, without using ng-content, the parent component suddenly is partially responsible for the child's layout. It'll get messy real quick, the quicker the more complex the layout gets.
It's useful for displaying components inside of other components, so the components can be reusable. For example, if you want to display some text on a few pages, you call the component with the text inside of the other components that you want the text to be displayed on. It could also be useful for showing different information but styled in the same format. E.g.
if your ng-content already has the information this is an example of how it would be used:
menu.component.html
<ng-content></ng-content>
message.component.html
<ng-content></ng-content>
This is good because you can copy and paste the exact component into another one without having to rewrite the code
If your ng-content is looking for a data source for information to pass into it
<ng-content [data]='data'></ng-content>
This is good because you can recreate the component inside of another component but with different data inside of it.
If you've ever used react, you pass data into it in a similar way here as you would with react props, but instead of props in angular, it will be an #input field. Here is some example code
test.component.html
<ng-content [data]='THIS IS THE DATA'></ng-content>
This is the actual component, as you can see, it is looking for a data source
ng-content.component.html
<p>The data we are looking for is {{data}} </p>
ng-content.component.ts - this says that when the component is called, it is looking for an input called 'data' and the type has to be a string
#Input() data: string;
We would then see the test.component.html displayed like this:
The data we are looking for is THIS IS THE DATA

Conditional Root Tag on Vue Component

My Question
I have a Vue component that renders content like so:
<template>
<div class="item">
<h1>{{ title }}</h1>
<p>{{ contents }}</p>
<!-- Lot's of other stuff... -->
</div>
</template>
<script>
// export default...
</script>
<style lang="scss">
// style...
</style>
Note the contents within the div...
In some circumstances, I need to change <div class="item"> to <a class="item">. With that in mind, is there a way to conditionally change the tag (e.g. a, div) for the root element of a Vue component?
Research
I have searched around online and was able to find something about using the render function like so:
render (createElement) {
return createElement(this.tag, {}, this.$slots.default);
}
The issue I have with the above is that it implies that I need two separate components, for example; Item.vue and ItemTag.vue. Surely there is a way to do this with one component?
I believe you could use is:
<div :is="useA ? 'a' : 'div'">
...
</div>
This isn't quite what the docs suggests it's for but it does seem to have the desired effect.
https://v2.vuejs.org/v2/api/#is
Using a render function instead wouldn't necessarily require you to have two components but it would need you to rewrite your entire template as a render function.

How to make a 'Container' component in Vue.js

I'm new to Vue and cannot find a way to implement a React-like 'Wrapper' component with Vue.js, for example, a reusable datagrid component using a 3rd-party some-table component and a pagination component. Intuitively, datagrid will provide the data/props/events that both components need and control the communication between them. In React, this could be done as simply as something like
// inside <Datagrid />
<Table {...someProps} />
<Pagination {...otherProps} />
With Vue, It seems like something like below can only pass props down to children components
// inside Datagrid.vue
<some-table v-bind="$props"></some-table>
I'm not sure if slots could be of help. This wrapper component that I've been struggling for takes all the props/events/slots its children need and pass them down to them so that I could utilize all the functionality that it's children(which probably some 3rd-party components) provide. Moreover, it may also take responsibility for something like data exchange between its children. While datagrid could be a slots wrapper, but what if both table and pagination require a same data prop which I think should reside in datagrid. How to pass this data down to the slots?
// Datagrid.vue
<template>
<div>
<slot name="table"></slot>
<slot name="pagination"></slot>
</div>
</template>
<script>
export default {
name: 'Datagrid',
data() {
return {
data: 'How to share it with table and pagination',
}
},
}
</script>
Some solutions that I could figure out:
Render Functions, but I don't think that complicity is needed in this case
Instead of creating a 'Container' component, simply turn to mixins, but does this mean I have to input <pagination :total="total" :other-props="are-same-for-all-datagrids"></pagination> each time I want a datagrid?
Any examples dealing with such situations in Vue? Thanks in advance!
You want to use slots:
Vue.component('wrapper', { template: '#wrapper' })
new Vue({ el: '#app' })
<!-- wrapper template -->
<script type="text/x-template" id="wrapper">
<div class="wrapper" style="background: beige; padding: 5px;">
This is being wrapped:
<slot></slot>
</div>
</script>
<div id="app">
<wrapper>
<div style="background: aliceblue;">I'm being wrapped!</div>
</wrapper>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>

Categories