Svelte: transferring props between parent and child - javascript

I've wrote simple Modal component with two slots named button and content:
<script>
let opened = false;
function open() {
opened = true;
}
</script>
<slot name="button" opened={opened} open={open}/>
{#if opened}
<slot name="content"/>
{/if}
Also, opened and open-method are passed to parent component via <slot name="button"
In App.svelte:
<script>
import Modal from './Modal.svelte';
</script>
<Modal let:opened let:open>
<button slot="button" on:click={open} class:opened>Open</button>
<div slot="content">Content</div>
</Modal>
So, there are two questions:
1) It looks a little bit weird that props can be passed to parent just through any random slot.
Is it a good practice to do it like this?:
<slot opened={opened} open={open}/>
<slot name="button"/>
{#if opened}
<slot name="content"/>
{/if}
2) <div slot="content">Content</div> passed with its <div>. Could I pass only Content without <div> or can Content be unwrapped somehow?
Thx

1) Yes it's totally fine.
Alternative is to use data-binding or events to send info to a parent.
2) You can have a default slot, and it wouldn't need a wrapping element. Example:
<!-- Modal.svelte -->
<!-- named slot -->
<slot name="button"/>
<!-- unnamed/default slot -->
<slot/>
<!-- usage -->
<Modal>
<button slot="button"/>
I'm unwrapped in the default slot!
<slot/>

Related

Can a Svelte component access the DOM element for its slot contents?

Let's say I have svelte code like:
<!-- Widget.svelte -->
<div>
<slot>No video was provided</slot>
</div>
<!-- App.svelte -->
<Widget>
<video id="example">
</video>
</Widget>
Within Widget.svelte, is there a good way for me to get a handle on the video DOM element from the parent-supplied slot? (Of course I could pass in a prop like videoElementId="example" from App.svelte, and then do a document.getElementById(videoElementId) inside Widget.svelte -- but this would be working around Svelte's API, rather than within it.)
I'm not aware of a built-in way to get elements placed in a slot, but you could bind to a container element and query its children:
<!-- App.svelte -->
<script>
import Slotted from './Slotted.svelte';
</script>
<Slotted>
<p>
I'm a paragraph
</p>
</Slotted>
<!-- Slotted.svelte -->
<script>
import { onMount} from 'svelte';
let container;
let childContent;
onMount(() => {
let firstChild = container.children[0];
childContent = firstChild.textContent;
});
</script>
<p>
Child content: {childContent}
</p>
<div bind:this={container}>
<slot></slot>
</div>

vue button related event not fired

In:
https://codesandbox.io/s/upbeat-hodgkin-qjt61?file=/src/components/EditCategory.vue
the modal is shown as expected upon long click over a category:
but clicking OK does not fire the close event:
<template>
<div>
<p v-longclick="() => longClicked()" #click="longClicked()">
{{ taskItemLocal["name"] }}
</p>
<div v-if="this.showModal" #close="closeModal()">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header"> Edit Category </slot>
</div>
<div class="modal-body">
<slot name="name"> Edit Name </slot>
</div>
<div class="modal-body">
<slot name="delete"> Delete Category </slot>
</div>
<div class="modal-footer">
<slot name="footer">
<!-- default footer -->
<!-- EVENT NOT FIRING -->
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</div>
</div>
</template>
closeModal() is not called; changing showModal "directly" also fails.
You have dispatch event to parent but in parent component you have not done any thing with "close" event. Here, in GenericItem.vue I have made event listener with #close="closeBox($event)" . Here, it will trigger method of closeBox
GenericItem.vue
Changes on Template
<edit-category
v-if="editCategoryStatus"
:taskItem="taskItemLocal"
#close="closeBox($event)"
/>
Add one closeBox method
closeBox() {
this.editCategoryStatus = !this.editCategoryStatus;
},
Add editCategoryStatus on data
data() {
return {
editCategoryStatus: true,
taskItemLocal: {
type: Object,
default: {},
},
};
If you want to listen to an event within the component that emitted that event, you use the instance $on method:
mounted() {
this.$on("close", () => {
this.closeModal();
});
}
The template event handler #close="closeModal()" is used to listen to events from parent. It has no effect within the child component.
The working codesandbox: https://codesandbox.io/s/loving-kirch-vrhwn?file=/src/components/EditCategory.vue .
You could just make your button like this. You made this more complicated than it should be
<button class="modal-default-button" #click="showModal = false">
Also, there is this example from the official docs
here.

Create my own component vuejs, with two diferents parts

I have a component with an input that when clicking opens a modal.
When I use this component, and insert it inside a div with relative position, the modal that opens, it does not display well. I would need the html of the modal to be outside the div position relative.
It is important that my component contains both the input and the modal, since this component itself will be used several times within another component.
<div class="position-relative">
<MyOwnComponet />
</div>
<div class="position-relative">
<MyOwnComponet />
</div>
My component would be something like this, more or less:
<template>
<input #click="showModal = true" />
<div class="modal" v-if="showModal">
...
</div>
</template>
I am not sure what's your point for this kind of requirement but anyway there is a workaround you can do with props.
You can have props in "MyOwnComponet" like this. And use that prop value to render accordingly.
Your main component
<div class="position-relative">
<MyOwnComponet :isInput="true" />
</div>
<div class="position-relative">
<MyOwnComponet :isInput="false" />
</div>
Your (MyOwnComponent)
<template>
<div v-if="isInput">div-1</div>
<div v-else>div-2</div>
</template>
<script>
export default {
props: {
isInput : Boolean,
},
data() {
}
};
</script>
You can replace div-1 with your input and div-2 with modal.

VUE- How do I put the values inside of components imported?

I remember I have seen once how to put the values in the html text area after importing components in VUE.
I'm not sure there is a way to do that or I just remember things in a wrong way.
my code is as below.
<template>
<div class="container">
<div class="row">
<Heading></Heading>
</div>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-6">
<ul class="list-group">
<comp v-for='(value,index) in listing' :key='index'>{{value}}</comp>
</ul>
</div>
<serverstat></serverstat>
</div>
<hr>
<div class="row">
<footing></footing>
</div>
</div>
</template>
<script>
import Heading from './assets/Heading.vue';
import comp from './assets/comp.vue';
import serverstat from './assets/serverstatus.vue';
import footing from'./assets/footing.vue';
export default {
data() {
return {
listing: ['max','toms','judy','michael','dumdum']
}
},
components: {
Heading,comp,serverstat,footing
},
};
</script>
<style>
</style>
-comp-
<template>
<li class="list-group-item">
</li>
</template>
<script>
export default {
}
</script>
<style>
</style>
After I render this,
it doesn't show {{value}}. It only shows blank .
How do I insert the {{value}} within the html element?
Thank you in advance.
Since you are entering a value inside of a component, you can render it by using a slot in your component like this:
<template>
<li class="list-group-item">
<slot />
</li>
</template>
<comp v-for='(value,index) in listing' :key='index'>
<slot>{{ value }} </slot>
</comp>
Then in comp component use slot as
<slot/>
Not including the approach for props as you don't want to use that. Use the link above to learn more about slots.
When you use v-for it calls all the value from an array and :key='index' defines each object row from an array. If your object listing consists of firstname, lastname as your object then the value you want to print will be {{value.firstname}}. You are missing object name in value.
Can you try this once :
<comp v-for='(value,index) in listing' :key='index'>{{value.index}}</comp>

Change route with vue- <router-link>

I have a <router-link to = "/ endreco / test">, but I need to perform the same behavior on a vue-material tab, something like this: <md-tab md- icon = "books"> .. to change my route, same as the href = ""
What should I do?
I'm using vue.js, the vue-router to control routes and style with vue-material
You can add a #click.native handler to push to a route manually (the .native modifier is needed since the md-tab component does not have a click event):
<md-tab #click.native="$router.push('/endreco/test')">
Here's the documentation on Programmatic Navigation with Vue Router.
See my code I have implemented a method which traverses to router to see routes of mentioned name of component, you can easily get idea!
<template>
<div>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--3-col mdl-cell mdl-cell--1-col-tablet mdl-cell--hide-phone"></div>
<div class="mdl-cell mdl-cell--6-col mdl-cell--4-col-phone">
<div class="image-card" v-for="picture in this.pictures" #click="displaydetails(picture.id) ">
<div class="image-card__picture">
<img :src="picture.url" />
</div>
<div class="image-card__comment mdl-card__actions">
<span>{{ picture.comment }}</span>
</div>
</div>
</div>
</div>
<router-link class="add-picture-button mdl-button mdl-js-button mdl-button--fab mdl-button--colored" to="/postview">
<i class="material-icons">add</i>
</router-link>
</div>
</template>
<script>
import data from '../data'
export default {
data() {
return{
'pictures' : data.pictures
}
},
methods :{
displaydetails (id){
this.$router.push({name:'detailview', params:{id:id}});
console.log("helo");
}
}
}
</script>
Hope get something resourceful out of it!

Categories

Resources