Riotjs data from child tag to parent tag - riot.js

I have looked lot similar questions, but still have difficulties getting it done. Try-d observable-s but messing up somewhere and cant get it done. new to riotjs still
in child tag i have a function that pushes data to a list:
<make-list>
...lots of html...
<script>
var piclist = []; --after first function run this list has data
....
done: function (e, data) {
piclist.push(data.result);
}
...
</script>
</make-list>
and in parent data i want to access it in a function
<main>
...lots of html..
<script>
riot.mount('make-list')
and i wana use that piclist = []; list here inside a function
</script>
</main>

Got it done this way with using mixin. Maybe it's not the right way, but it works.
<main>
...lots of html..
<script>
riot.mount('make-list')
var piclist = [];
riot.mixin(piclist)
</script>
</main>
<make-list>
... lots of html ...
<script>
...
done: function (e, data) {
piclist.push(data.result);
}
...
</script>
</make-list>

It seems you want to have the make-list tag create list items for a list that is displayed by main and make-list is to be a child of main.
You are using riot.mount('make-list') within the parent tag. That is at least very unusual: It actually triggers all make-list tags on the page to be mounted. Why not go with the riot way and add it within the html part of the parent, like this?
<main>
... lots of html ...
<make-list opts={piclist} />
<script>
this.piclist = [];
</script>
</main>
The opts allow you to pass data to the child (in this case the reference to the list). You can access that within the child tag like so:
<make-list>
... lots of html ...
<script>
...
done: function (e, data) {
this.opts.piclist.push(data.result);
}
...
</script>
</make-list>
I hope this helps.

Take a look at RiotComponent. It enable communication between elements in an intuitive way.

Related

Vue3 Reactivity in script setup for translation

I am adding some DOM elements in the script setup side but I want the messages to change when I change the language. I am using vue-i18n plugin. It's easy to do it in the template section because I can basically use the useI18n().t method but how can I do this in the script setup section. Using the useI18n().t method doesn't ensure reactivity.
Example Code:
$(".time")[0].innerHTML = `
<div>0<span>${useI18n().t("details.hour")}</span></div>
<div>0<span>${useI18n().t("details.minute")}</span></div>
<div>0<span>${useI18n().t("details.second")}</span></div>
`
Manipulating DOM directly inside the script leads to inconsistence in your app, you should drive your component by different reactive data to achieve your goal.
In your current situation try to define a computed property based on the translation then render it inside the template based on its different properties :
<script setup>
const {t} =useI18n()
const time = computed(()=>{
return {
hour:t(""details.hour"),
minute:t(""details.minute"),
second:t(""details.second"),
}
})
</script>
<template>
<div class="time">
<div>0<span>{{time.hour}}</span></div>
<div>0<span>{{time.minute}}</span></div>
<div>0<span>{{time.second}}</span></div>
</div>
</template>

Vue3 split form into components avoiding mutating props

I've created a component which does a lot of work:
Receives data from API
Populate so many inputs with this data
Manages the form submission doing the API post with data inside inputs
So the component is working but I'd like to split into small components, my idea was something like:
ViewForm: Receives Data from API and pass down to sub components. Submit form doing the POST call.
SubFormA: Show a group of inputs related with "section A" and populate with props passed from ViewForm.
SubFormB: Same but with "Section B"
can be more subForms...
viewForm
<template>
<sub-form-a v-model="formA"></sub-form-a>
<sub-form-b v-model="formB"></sub-form-b>
<button #click="submitHandler">Send</button>
</template>
<script setup>
import {ref} from 'vue'
let formA = ref({ //This data would come from an API call, but for showing the example.
a: "aaaa",
b: "bbbb",
})
let formB = ref({ c: "cccc", d: "dddd" })
function submitHandler(){
// Here I should have variables formA and formB updated with that user entered on the inputs.
}
</script>
subFormA // subFormB
<template>
<input v-model="modelValue.a" type="text" />
<input v-model="modelValue.b" type="text" />
</template>
<script setup>
const props = defineProps(["modelValue"]);
// validate inputs with rules
</script>
Eslint warns me with the popular problem:
ESLint: Unexpected mutation of "modelValue" prop.(vue/no-mutating-props)
It's weird because devtools doesn't show me this warning... Vue3 removed this warning or I'm missing something?
I've read a lot of this problem but I'm still don't understand...
Why is this an anti-pattern? I saw a lot of examples where I see the problem, but with my example, it's a form, this is not what we want? that the child can directly modify the props? I see no problem here, I want that the parent component can overwrite all inputs with props, but at the same time childs modify the parent data, so then parent component can submit the form easily.
So how can I fix this?
Should child components clone the props and using their local data to populate the inputs and watch all inputs and emit the data? But then the parent component will have to create another variable to receive the updated data right? Like:
viewForm
<template>
<sub-form-a :data="formA" #update="updateFormAHandler"></sub-form-a>
<sub-form-b :data="formB" #update="updateFormBHandler"></sub-form-b>
<button #click="submitHandler">Send</button>
</template>
<script setup>
import {ref} from 'vue'
let formA = ref({ //This data would come from an API call, but for showing the example.
a: "aaaa",
b: "bbbb",
})
let formB = ref({ c: "cccc", d: "dddd" })
let formAUpdated = ref({})
let formBUpdated = ref({})
function updateFormAHandler(data){
formAUpdated = data
}
function updateFormBHandler(data){
formBUpdated = data
}
function submitHandler(){
// Here I should have variables formA and formB updated with that user entered on the inputs.
// Submit formAUpdated and formBUpdated.
}
</script>
This is the way? I feel a bit confusing...
You don't need to emit anything from your component in your scenario, you're simply binding the data. Emit would be used for triggering custom events on the parent, like closing pop-ups (for example).
In your component, you don't want to mutate the data but you want to update a local version of it as per Vue rules. This can be done with a computed property in your component like so:
computed: {
localFormA() {
return this.formA
},
},
And then use localFormA in your component to bind to the inputs as normal, so you're not mutating the original prop being passed down.
<input type="text" v-model="localFormA.myProperty"/>
In your parent, you pass down formA as a prop:
<sub-form-a :formA="formA"></sub-form-a>

Unable to append html element to the parent in Vue js directive

I have a global directive in vue js
Vue.directive('customDirective',{
bind(el,binding,vnode){
// need to get the parent
// append a div after the input element
}
})
I have the following html code .
<div class="parentDiv">
<input type="text" name="firstName" v-customDirective="Some text"/>
</div>
<div class="parentDiv"> is loading dynamically via jquery. I need to get the parent of input tag and need to append some html after the input tag. I have tried with parent(), but it is showing as parent is not a function. Any suggestions ?
You should probably use the inserted hook instead of bind if you want to access the parent element.
You can get the parent element via el.parentElement.
You probably should use vnode, and Vue nextTick.
import Vue from 'vue';
Vue.directive('customDirective',{
bind(el,binding,vnode){
Vue.bextTick(() => {
let parent = vnode.elm.parentElement;
});
}
})
If you want to use parent() which is the jQuery function, you must get the element by jQuery.
Vue.directive('customDirective',{
bind(el,binding,vnode){
Vue.bextTick(() => {
let id = vnode.elm.id;
let parent = jQuery('#'+id).parent();
});
}
})
I'm not sure, that second code works, but it will be something like that.

Is it posible to delete `div` from template?

I have a component myHello:
<temlate>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
</template>
And main component:
<h1>my hello:</h1>
<my-hello><my-hello>
After rendering shows this:
<h1>my hello:</h1>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
How to delete <div> ?
With VueJS, every component must have only one root element. The upgrade guide talks about this. If it makes you feel better, you are not alone. For what it's worth the components section is a good read.
With the myriad of solutions to your problem, here is one.
component myHello:
<temlate>
<h2>Hello</h1>
</template>
component myWorld:
<temlate>
<p>world</p>
</template>
component main
<h1>my hello:</h1>
<my-hello><my-hello>
<my-world><my-world>
Vue gives you the tools to do so by creating templates or you can do it by having a parent div with two parent divs as children. Reset the data from the data function. Stick with convention (create templates). It's hard to get used to use Vue when you have a jQuery background. Vue is better
Ex.
data () {
message: 'My message'
}
When you click a button to display a new message. Clear the message or just set the message variable with a new value.
ex. this.message = 'New Message'
If you like to show another div. you should used the if - else statement and add a reactive variable. Ex. showDivOne
data () {
message: 'My message'
showDivOne: true,
showDivTwo: false
}
Add this reactive variables to the parent divs that corresponds to the div.
When clicking the button, you should have a function like...
methods: {
buttonClick () {
this.showDivOne = false
this.showDivTwo = true
}
}
I think you can use v-if directive to controll. Add a flag to controll status to show or hide

vuejs 2 how to add child component without npm

I use vuejs2 and want to attach some component to my main one. Now I can have something like this:
var Component ={
template: '<div>1</div>'
};
However, I want the thing in template to be a partial html from some other directory, like template:'../../com/a.html', I heard x-template but not know how to use it. Also, is there some lib other than webpack/browserify/npm series I can use directly just by add the script in the end of html?
You have your template file as
/* yourPath/name.template.html */
<div class="myClass">
1
</div>
Then in the component file, you can do following:
export default {
template: require('yourRelativePath/name.template.html'),
data: function () {
return {
};
}
}