Vue draggable change parent data when using KeepAlive - vuejs3

I have a vue3 app, and one of the child component uses vue-draggable.
In the parent component I have an object (let's call it myJson) which propagates to child component with props.
So far it works as expected.
However, when adding 'KeepAlive' to the parent component, every time I drag the items, myJson is set to the drag event instead of the origin data it had.
It still occures even if I pass to the child component a copy of myJson (with JSON parse-JSON stringify). See details below
parent component:
<template>
<KeepAlive>
<component :is="activeComponent" :my-json="myJson" />
</KeepAlive >
</template>
data: () => ({
myJson: { ...someData }
})
mid component:
<template>
<list-items :items="items" />
</template>
<script>
export default {
components: { ListItems },
computed: {
items() {
return JSON.parse(JSON.stringify(this.myJson.value.items))
}
},
}
</script>
child component (ListItems):
<template>
<draggable
v-model="items"
animation="100"
handle=".dnd-handle"
item-key="product"
class="items-list"
#start="drag=true"
#end="drag=false"
>
<template #item="{ element, index }">
{{element}}
</template>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: { draggable },
props: ['items'],
}
</script>
The items are displayed correctly in the component.
Before dragging, myJson is an object with my data.
After dragging myJson is an event.
Any idea?
vuedraggable version is 4.1.0
--UPDATE--
In parent component there is a function "update", which gets value and updates myJson.
methods: {
update (value) {
myJson = value
}
}
I found out that every time I drag, there is a call to this function with the dragging event as value, even when I try to catch the draggable events. Thats why myJson gets wrong value.
My problem was solved when I changed the function's name. But anyone knows why this happens?

Related

Change width of event in FullCalendar (React JS)

I like to change the width of an event in context of React JS.
Similiar questions described here:
How to edit width of event in FullCalendar?
Change Fullcalendar width
...
Unfortunately, in the quoted questions is nothing mentioned how to solve this in a react environment.
I figured it out how to do it. eventRender does no longer exist (v4) but instead different "event render hooks" (v5):
eventClassNames: Specifically for changing the .css of an event
eventContent: To inject content into the event
and others (see:https://fullcalendar.io/docs/event-render-hooks)
Now, depending what you want to achieve, there are two ways to do this in React JS. (Note: I used TypeScript)
Applying CSS change to all events
We can use styled to create our own .css definition for any event and use that as a wrapper (StyleWrapper)
import React from 'react';
import FullCalendar from '#fullcalendar/react';
import timeGridPlugin from '#fullcalendar/timegrid';
import styled from '#emotion/styled';
export interface ISampleProps {}
//our Wrapper that will go around FullCalendar
export const StyleWrapper = styled.div`
.fc-event {
width: 98px !important;
}
`;
//Reacct Functional Component
const Sample: React.FunctionComponent<ISampleProps> = (props) => {
const events = [
/*some events */
];
return (
<>
<div>
<StyleWrapper>
<FullCalendar
plugins={[timeGridPlugin]}
initialView="timeGridWeek"
events={events}
/>
</StyleWrapper>
</div>
</>
);
};
export default Sample;
Apply specific CSS to specific events
With this way, you can tell FullCalendar exactly how an event has to look like depending self-defined props you add to an event. Your self-defined props will be added to extendedProps which will be used in our event render hook eventClassNames
//same imports from earlier (but you don't need "styled" for this one)
const Sample: React.FunctionComponent<ISampleProps> = (props) => {
function eventAddStyle(arg: any) {
//all self-created props are under "extendedProps"
if (arg.event.extendedProps.demanding) {
return ['maxLevel']; //maxLevel and lowLevel are two CSS classes defined in a .css file
} else {
return ['lowLevel'];
}
}
const events = [
{
id: 'a',
title: 'This is just an example',
start: '2022-03-19T12:30:00',
end: '2022-03-19T16:30:00',
backgroundColor: '#74AAEB',
demanding: true //our self-created props
},
{
id: 'b',
title: 'This is another example',
start: '2022-03-17T08:00:00',
end: '2022-03-17T11:30:00',
demanding: false // our self-created props
},
];
return (
<>
<div>
<FullCalendar
plugins={[timeGridPlugin]}
initialView="timeGridWeek"
eventClassNames={eventAddStyle}
events={events}
/>
</div>
</>
);
};
export default Sample;

How can get custom tags inside web component

I'm new in webcomponents with stenciljs, I'm testing creating a select, the idea with this code create and render the select:
<rhx-select label-text="A select web component">
<rhx-select-item value="1" text="option 1"/>
<rhx-select-item value="2" text="option 2"/>
</rhx-select>
The problem i have is how can i get the tags that inside my web component?
this is my code:
import { Component, h, Prop, } from '#stencil/core';
#Component({
tag: 'rhx-select',
styleUrl: 'select.css',
shadow: true,
})
export class RhxSelect {
#Prop() labelText: string = 'select-rhx';
#Prop() id: string;
#Element() el: HTMLElement;
renderOptions() {
let data = Array.from(this.el.querySelectorAll('rhx-select-item'));
return data.map((e) =>{
<option value={e.attributes.getNamedItem('value').value}>{e.attributes.getNamedItem('text').value}</option>
});
}
render(){
return (
<div>
<label htmlFor={this.id}>
{this.labelText}
</label>
<select id={this.id} class="rhx-select">
{this.renderOptions()}
</select>
</div>
)
}
}
Thank you for your time.
If you add the #Element() decorator you can parse the children with vanilla JS:
getItems() {
return Array.from(this.el.querySelectorAll('rhx-select-item'));
}
You can then use those elements and their properties/attributes however you want, for example to generate a list of <option> elements.
A good example is ion-select which gets the children in the childOpts() getter function.
A couple things to keep in mind:
You'll probably want to hide the items with display: none
If the options might change after the initial load you'll need to listen for those changes. Ionic uses the watchForOptions function.
this.el.querySelectorAll won't return any elements until after the component has rendered once, so that its children are available in the DOM. Therefore you will have to use something like the componentDidLoad hook:
export class RhxSelect {
// ...
#State()
items: HTMLRhxSelectItemElement[] = [];
componentDidLoad() {
this.items = Array.from(this.el.querySelectorAll('rhx-select-item'));
}
render() {
return (
<div>
<label htmlFor={this.id}>
{this.labelText}
</label>
<select id={this.id} class="rhx-select">
{this.items.map(item => (
<option value={item.getAttribute('value')}>{item.getAttribute('text')}</option>
))}
</select>
</div>
)
}
}
Note however that componentDidLoad is only executed once, after the component has loaded. If you want your component to support dynamic changes to the options, then you'll have to use something else, like componentDidRender, but then you'll also have to make sure you don't end up with an infinite render loop. There's also a couple ways to solve this, by combining different lifecycle methods.
See https://stenciljs.com/docs/component-lifecycle for a list of all available lifecycle methods.

receiving style and dynamically applying it to elements selected with class, using Vue

I'm making a web service with Vue.js. What I'm trying to do is to change styles of elements which receive from parent component's property. For example:
(there are omitted lines)
Parent component
<template>
<div>
<child v-bind:styles="styles"></child>
<div>
</template>
<script>
export default {
components: { child },
data() {
return {
styles: { width:'100px', height:'70px' }
};
}
}
</script>
The width and height of styles object can be changed, i.e. decided by anyone who use Parent Component.
Chlid component
<template>
<div>
<div class="iterDiv" v-for="(item, index) in arr" v-key="index">{{ item }}</div>
</div>
</template>
<script>
export default {
name: 'Child',
props: [ 'styles' ],
data() {
return {
arr: [1, 2, 3, 4, 5]
};
}
}
</script>
And the Child Component is consist of iterated elements which have a class iterDiv for selection like document.getElementsByClassName('iterDiv').
Now, I want to change Child Component's div elements, having iterDiv class name, style with styles dynamically.
Is there any way for it? Thanks.
If there is a answer already, I missed it and let me know it. I will delete this question.

Vue.js: How to change a class when a user has clicked on a link?

I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.

Polymer communication between elements

I want to achieve communication between child parent with Polymer element.
Here my index.html
<proto-receiver data="message">
<proto-element data="message"></proto-element>
</proto-receiver>
Both element have their respective "data" property
properties: {
data: {
value: 'my-data',
notify: true,
}
},
In proto-receiver, which is the parent I update "data" by handling simple click
<template>
<span on-tap="onClick">proto receiver: {{data}}</span>
<content></content>
</template>
onClick: function () {
this.data = 'new-message';
},
I want the change to be propagate to the child element as well, as it mentioned here.
I achieve this by passing a setter in my child element and called it like this. Which is, I guess, not the way it should be done.
Polymer.Base.$$('body').querySelector('proto-element').setData(this.data);
What I'm doing wrong
Thanks
UPDATE:
For those coming here. The proper way of doing this is by using Events.
Polymer 1.x
this.fire('kick', {kicked: true});
Polymer 2.x (simple javascript)
this.dispatchEvent(new CustomEvent('kick', {detail: {kicked: true}}));
In both case the receiver should implement the regular addEventListener
document.querySelector('x-custom').addEventListener('kick', function (e) {
console.log(e.detail.kicked); // true
})
To provide a concrete example to Scott Miles' comments, if you can wrap your parent and child elements in a Polymer template (such as dom-bind or as children to yet another Polymer element), then you can handle this declaratively. Check out the mediator pattern.
parent element:
<dom-module id="parent-el">
<template>
<button on-tap="onTap">set message from parent-el</button>
<p>parent-el.message: {{message}}</p>
<content></content>
</template>
<script>
Polymer({
is: 'parent-el',
properties: {
message: {
type: String,
notify: true
}
},
onTap: function() {
this.message = 'this was set from parent-el';
}
});
</script>
</dom-module>
child element:
<dom-module id="child-el">
<template>
<p>child-el.message: {{message}}</p>
</template>
<script>
Polymer({
is: 'child-el',
properties: {
message: {
type: String,
notify: true
}
}
});
</script>
</dom-module>
index.html:
<template is="dom-bind" id="app">
<parent-el message="{{message}}">
<child-el message="{{message}}"></child-el>
</parent-el>
</template>
<script>
(function(document) {
var app = document.querySelector('#app');
app.message = 'this was set from index.html script';
}) (document);
</script>
JS Bin
I was facing same issue and got solution for it and fixed it as below
this.fire('iron-signal', {name: 'hello', data: null});
You can refer this iron-signals you will get the solution which you are looking for its basically event fire from any element to another
Hope this will help you
Polymer iron signals

Resources