As I understand you can not change the props value directly in the child component.
But I find out that this will work, I want to know the reason behind it.
For reference: I am using vue3+vite
For example:
<template>
<input v-model="price"/>
</template>
<script lang="ts" setup>
defineProps({
price : Number
});
</script>
this can change the props value based on the input. with no warning or error
but if I write this way
<template>
<input v-model="props.price"/>
</template>
<script lang="ts" setup>
const props = defineProps({
price : Number
});
</script>
there will be a warning in the console.
notice I didn't write any computed to handle the change of the props.
Is it a bad practice?
Both should issue warning. The reasoning is that the parent will not usualy be aware of the change unless it is mutated there. It also allows the parent to validate the change. The idea is that only the owner of the data should modify it.
So emit an event instead. The conventional way to write the code is.
<input :value="price" #input='$emit("input", $event)'/>
// or
<input :value="price" #update:value='$emit("update:value", $event)'/>
// or
<input :value="price" #input='$emit("update:value", $event)'/>
You can access both because Vue automatically exposes both the props object itself and all the props' properties into the template.
Related
I know how to pass data from a parent component to a livewire component, but how to do it, if the value is dynamic and generated by JavaScript?
<x-my-layout>
<livewire:my-component id="test" foo="this value should be passed" />
#push('scripts')
<script>
const livewireComponent = document.getElementById('test'); // <-- this is not working
livewireComponent.setAttribute('foo', 'bar'); // <-- In consequence this is not working
</script>
#endpush
</x-my-layout>
Please don't answer, that I just can use <livewire:my-component foo="bar" />, because I need to pass a value that is generated on client side like an text input.
Blade-components are rendered server-side, and thus you cannot set that property via Javascript like that. You would have to emit an event instead. Here are some examples on how you can do that. All of these following examples that are event-based, which means that you would have to listen for it within your component by adding
protected $listeners = ['setFooProperty'];
public function setFooProperty($value)
{
$this->foo = $value;
}
Using Alpine.js with x-init
<x-my-layout>
<div x-data x-init="Livewire.emit('setFooProperty', 'bar')">
<livewire:my-component id="test" foo="this value should be passed" />
</div>
</x-my-layout>
Using a global emit event with JavaScript
<x-my-layout>
<div x-data x-init="$wire.emit('setFooProperty', 'bar')">
<livewire:my-component id="test" foo="this value should be passed" />
</div>
#push('scripts')
<script>
Livewire.emit('setFooProperty', 'bar');
// or
Livewire.emitTo('my-component', 'setFooProperty', 'bar');
</script>
#endpush
</x-my-layout>
I'm using Vee Validte 3.x in my Nuxt JS/Vue project. I need to be able to get the classes from a particular Validation Provider element by it's name or some unique identifier and output the classes object somewhere else on my page.
I'm struggling to figure out how, and thought a ref would work but didn't.
Here's my code:
<!-- Data Source (Table) (Validation Workaround) -->
<validation-provider
tag="div"
name="data source"
:rules="{ required: { allowFalse: false } }"
v-slot="{ errors, classes }"
>
<CustomInput
class="hidden-spans"
:options="editor.sources"
v-model="mockedCheckboxes.source.isChecked" />
<span class="block text-xs text-red-500 mt-1">{{ errors[0] }}</span>mockedCheckboxes.source.classes = classes" />
</validation-provider>
I'd like to be able to do something like:
this.$validator('data source').classes
Somewhere in my Vue file so that I can use the classes from one input elsewhere, this isn't working though.
this.$validator was actually removed in favor of ValidationObserver: https://vee-validate.logaretm.com/v3/migration.html#migrating-from-2-x-to-3-0
So far, if you try to validate something that is not directly in the component you're working on, you should watch the flags of the validation (errors) and update the status on a Vuex store. Then access Vuex back to decide if the input is valid and proceed further.
There is no global validation to my knowledge.
I want to create the following component in ember (templates / components / echo-hlabel-tf.hbs)
<div id="echo-hlabel-tf">
<span id="tf-label">{{label}}</span>
<span id="tf-value">
<input type="text" value={{textValue}}
{{action "validateRegExp" textValue regExp post on="focusOut"}}
{{action "showText" textValue post on="mouseUp"}}
/>
</span>
</div>
Just a textfield and a label placed in the same row . I have created the functions in the controller of the component (components / echo-hlabel-tf.js):
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
validateRegExp(text,regExpStr){
let regExpObj = new RegExp(regExpStr)
if(regExpObj.test(text)){
console.log('entry matches')
}else{
console.log('entry is wrong');
}
},
showText(text){
console.log(text);
}
}
});
I want to use this component from another template (template / programmers.hbs) :
<ul>
{{people-list title="List of programmers" people=model}}
</ul>
<div>
{{echo-hlabel-tf
label="Textfield horizontal"
textValue="..."
regExp="^([a-z0-9]{5})$"
}}
</div>
The thing is that , even the actions are fired as the events trigger, the variable that represents the value of the input (textValue) always stands the same ("...") . seems that the binding between the outer template, and the components is created at the beginning, but if I put more letters in the textfield , the value of textValue remains the same and doesn't change . I would like to, as I'm adding letters to the textBox, print them with the console with the method showText, but I always print "..."
You are using pure html5 input that does not do two way binding by itself. That is even if you type something, it is not reflected to textValue attribute. You can use ember's builtin input component as in this twiddle. I believe this will solve your problem.
I have something like this in my parent scope:
<form-error :errors="errors"></form-error>
<textarea class="form-control" name="post" cols="30" rows="8" #keydown="errors.clear('post')" v-model="post"></textarea>
Note the #keydown event, where I am clearing out the errors, by calling
method on a class.
With :errors="errors" I am passing instance of the below Errors class,
into <form-error> child component:
class Errors {
constructor() {
this.errors = {};
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
clear(field) {
delete this.errors[field];
}
has(field) {
return this.errors.hasOwnProperty(field);
}
}
And in <form-error> child component I have this:
<template>
<div v-if="errors.has('post')" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
<script>
export default {
props: ['errors']
};
</script>
Now, while v-text="errors.get('post')" works fine, and I am getting error
displayed, the v-if="errors.has('post')" part doesn't work at all.
I am assuming errors is passed the right way as props, otherwise that
errors.get('post') wouldn't work.
Question is, why when parent triggers that #keydown event, and I see the
errors object is being emptied properly (Vue addon for chrome), the v-if
part doesn't update, thus hiding the div?
As you can see, the <FormError> child component is being updated to reflect the change in errors when I start typing, but still v-if doesn't trigger.
Edit
What's even more confusing, docs say:
Note that objects and arrays in JavaScript are passed by reference, so if the
prop is an array or object(as in my case), mutating the object or array itself inside the
child will affect parent state.
Although of course I am not mutating the object from with in my child, but the
important part is that object changes in parent should be reflected in child.
Are you getting any error. As I can see Map.has has poor support in browsers. You can try using any of following alternatives:
post in errors
<template>
<div v-if="'post' in errors" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
errors['post']
<template>
<div v-if="errors['post'] !== undefined" class="alert alert-danger" v-text="errors.get('post')"></div>
</template>
(to save some reading -- the problem boiled down to a missing 'this' when referencing the #Input'ed variables)
I have a parent class - this will (eventually) display a list of "QuestionComponents".
The relevant part of the parent class template is as follows:
<question [(justText)]="testText" [(justObj)]="testObj" [(myQuestion)]="singleQuestion"></question>
Suffice it to say I'm getting the "singleQuestion" object from a http service call. the Question class, of which "singleQuestion" is an instance, has a basic "toString" method for easier output debugging.
testText, textObj, and singleQuestion are all just objects of increasing complexity for me as I tested. For now it's very basic and only has one "question" as I work on my understanding.
The child "QuestionComponent" looks like this:
#Component ({
selector:
'question',
templateUrl:
'./app/components/question/question.component.html',
directives: [FORM_DIRECTIVES],
})
export class QuestionComponent {
#Input() myQuestion:USGSQuestion
#Input() justText:string
#Input() justObj:object
constructor() {
}
onClick() {
myQuestion.generalInfo.label = "changed";
console.log("change attempted");
}
}
Note that as time moves on, I'll need to be able to do some heavy interaction with the objects that are passed into the QuestionComponent. In fact, my preference was to just pass the Question object into the constructor, but passing data into the component constructor doesn't seem to work for some reason. (I digress) To attempt to check how I can interact with passed data , I built the onClick button and tried to change something in one of the #Input'ed variables.
The template for the child object is as so:
<div *ngIf="!justText">
no text passed
</div>
<div *ngIf="justText">
<b>Here is the passed Text:</b><br>
{{justText}} <br>
</div>
<br>
<div *ngIf="!justObj">
no obj passed
</div>
<div *ngIf="justObj">
<b>Here is the passed JSON:</b><br>
Foo: {{justObj.foo}} <br>
Baz: {{justObj.baz}}
</div>
<br>
<div class="question">
<i>I am a question spot</i>
<div *ngIf="!myQuestion">
Loading Question...
</div>
<div *ngIf="myQuestion">
<b>Here is the passed question:</b><br>
{{myQuestion}} <br>
</div>
</div>
<button (click)="onClick()">Clickit</button>
How do I get a reference to the #Input'ed objects inside the class? (in this case, how would I modify "myQuestion" in the 'onClick()' function? ... and, secondarily, how would I ensure changes to the objects got passed to the view and updated?
I should note I already tried to pass the value from the 'view' back through the onClick call, like so:
(change button to:)
<button (click)="onClick(myQuestion)">Clickit</button>
(and change onClick to:)
onClick(q) { q.generalInfo.label = "changed"; }
This did not work.
I'm an idiot.
After a few hours of research and searching (Before asking) it all came down to adding "this".
... as in "myQuestion.generalInfo.label = "changed";" should have been "this.myQuestion.generalInfo.label = "changed";"
Some days you gotta love programming, eh?