I have the following component:
<my-button #click="action">Alert</my-button>
And it is declared as:
<template>
<button><slot></slot></button>
</template>
And
export default {
methods: {
action: function () {
alert();
}
}
}
But on render, I get the message "Property or method "action" is not defined on the instance but referenced during render".
What I expects is that my #click property on my-button be transfered to native button specified on the template.
The parent component that the <my-button> tag is in does not have an action method.
You can do any of the following:
Add the #click handler to the <button> tag in the component definition because the action method is available at that scope:
// Your button component definition
<template>
<button #click="action"><slot></slot></button>
</template>
Move the action method to the parent component and add the .native modifier to the click event:
// Your parent component definition
<template>
<my-button #click.native="action">Alert</my-button>
</template>
<script>
export default {
methods: {
action: function () {
alert();
}
}
}
</script>
The .native modifier allows the #click handler to listen to the native click event of the root element of the <my-button> component.
Add a ref attribute to the <my-button> tag, so that you can refer to the button component's action method from the parent component:
<my-button ref="btn" #click.native="$refs.btn.action()"></my-button>
Related
I'm facing one issue when clicking on the button this.reloadDatatable() is not called. how can I call reloadDatatable() function from toggle function? below is the demo code for that. Thank you.
<button type="button" #click="addSettingContainer.toggle();"></button>
Alpine.data('initOrderGrid', () => ({
addSettingContainer: {
toggle() {
this.reloadDatatable();
}
},
reloadDatatable: function() {
console.log('a');
},
}));
You cannot access the parent object's scope from a child object without some ugly hacks. However it seem that you want to use an event system: on a button click you want to reload the data table. Alpine.js has builtin event system, we can use the $dispatch function to emit a custom event and the x-on method to capture our custom event:
<div x-data="initOrderGrid"
#reload-data-table.window="reloadDatatable">
</div>
<div x-data>
<button #click="$dispatch('reload-data-table')">Reload data table</button>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('initOrderGrid', () => ({
reloadDatatable() {
console.log('Data table reloaded');
}
}))
})
</script>
I am using a Laravel 9 application with Vue 3. I have created a fresh install.
I want to create some components that I want to use in a parent component. The first component that I want to create is which will be passed a value (postal code) and the component will format and validate the passed value. The parent component should have access to the updated formatted value.
As a first step, I have created the postal-code-component using the documentation from vuejs.org.
<!-- postalcode.vue -->
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
computed: {
value: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value)
}
}
}
}
</script>
<template>
<input v-model="value" />
</template>
Next, I made a copy of the example-component, that comes with the Laravel Vue installation to create a data element "postalcode" and use as a v-model for ,
<div class="card-body">
<postal-code-input v-model="postalcode" /> {{ postalcode }}
</div>
</template>
<script>
export default {
data(){
return {
postalcode:'l3r3g',
}
},
}
When I run my app, it displays the initial value (l3r3g) in the input box and also the same value at {{postalcode}}. However, when I update the value in the input box, it does not update {{postalcode}} in the parent component. When I inspect the component in vue dev tools, I see that modelValue and computed 'value' are undefined, as shown
I just don't know what is going on. I shall appreciate any help to fix this issue.
I've tried by using watcher instead of computed property because the computed properties made cache and sometime it's set() method created complications in debugging reactivity.
Following snippet works for me:
// PostalCode.vue
<template>
<div class="input-group input-group-merge mb-3">
<input
v-model="postalCode"
type="text" class="form-control"
id="postal_code" name="postal_code"
/>
</div>
</template>
<script>
export default {
name: "PostalCode",
props: {
modelValue: String,
},
data() {
return {
postalCode: null
}
},
watch: {
// watching prop
modelValue: {
handler: function (newValue) {
if (newValue) {
this.postalCode = newValue;
}
},
immediate: true,
},
// watching data() property
postalCode: {
handler: function (newValue, old) {
this.$emit('update:modelValue', newValue)
},
immediate: true
}
}
}
</script>
Usage:
<postal-code v-model="user.postal_code"/>
Your can also place your formatting logic within any watcher also.
Hint/Suggestion:
depending on requirement, if you want to do formatting on props change by parent (for old and new both) then place formatting logic in modelValue watcher.
Note:
Following snippet works perfectly on Vue3
If you’re using v-model to bind a prop like this
<postal-code-input v-model="postalcode" />
The postal-code component should emit ‘input’ and have a value prop. You can use a different prop and event but then you should avoid the v-model binding and just do something like this
<postal-code-input :modelValue="postalcode" #modelValueUpdate=“handleUpdate” />
CUSTOM COMPONENT
// ...
#Output() submit: EventEmitter < any > = new EventEmitter();
// ...
onFilterSubmit($event): void {
this.submit.emit($event);
this.formData = {
minDate: new Date().toISOString(),
maxDate: new Date().toISOString()
};
}
<form (ngSubmit)="onFilterSubmit(formData)">
<!-- -- -->
<button mat-button
mat-raised-button
[disabled]="reqsForm.form.invalid"
type="submit"
color="primary">
{{labels.submit}}
</button>
</form>
OUTER COMPONENT
// ...
onFilterSubmit($event): void {
console.info("FORM SUBMIT", $event);
}
<custom-component (submit)="onFilterSubmit($event)">
<!-- -- -->
</custom-component>
OUTPUT
FORM SUBMIT > Object
FORM SUBMIT > Object
The reason why this was happening is that an event called "submit"
is catchable already from outside the custom component.
I solved by changing the custom event name to filterSubmit
Note also that the type submit on the button - in this use case - is virtually useless, since as default one button in a form will be of type submit.
I have a simple form as below:
some.component.html
<form class="example-form" novalidate (ngSubmit)="onSubmit()" autocomplete="off" [formGroup]="testform">
<input type="text" formControlName="name" class="form-control" placeholder="Enter name" required/>
<app-show-errors [control]="claimform.controls.name"></app-show-errors>
<button type="submit" (click)="onSubmit()">Next</button>
</form>
some.component.ts
ngOnInit() {
this.testform= new FormGroup({
name: new FormControl('', { validators: Validators.required})
}, {updateOn: 'submit'});
}
onSubmit() {
if (this.testform.valid) {
alert('saving data');
} else {
this._validationService.validateAllFormFields(this.testform);
}
}
validationService.ts
validateAllFormFields(formGroup: FormGroup) {
Object.keys(formGroup.controls).forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateAllFormFields(control);
}
});
}
Reference
Problem
The form will validate on submit if left blank, but even after filling the value when I check this.testform.valid it returns false. But if I remove updateOn:'submit' on form then it validates on blur of input control and when value is entered it validates form return true. Not sure if updateOn is working fine or not or whether I've implemented this in a proper way. Could someone point me in the right direction.
in your HTML you have two calls to onSubmit() function, from submit button:
<button type="submit" (click)="onSubmit()">Next</button>
and from the form:
<form class="example-form"
ovalidate
(ngSubmit)="onSubmit()"
autocomplete="off"
[formGroup]="testform">
The first call to be triggered is the button's trigger, which actually does nothing in terms of updating your reactive form, since you set FormGroup's option to {updateOn: 'submit'}. The second call to be triggered is the form's trigger, which does actual form update.
Here is FormGroup directive config:
#Directive({
selector: '[formGroup]',
providers: [formDirectiveProvider],
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
exportAs: 'ngForm'
})
as we can see in host property DOM form's submit (triggered by hitting ENTER while focused within form or clicking form's submit button) will call onSubmit() function:
onSubmit($event: Event): boolean {
(this as{submitted: boolean}).submitted = true;
syncPendingControls(this.form, this.directives);
this.ngSubmit.emit($event);
return false;
}
which then will call syncPendingControls() function:
export function syncPendingControls(form: FormGroup, directives: NgControl[]): void {
form._syncPendingControls();
directives.forEach(dir => {
const control = dir.control as FormControl;
if (control.updateOn === 'submit' && control._pendingChange) {
dir.viewToModelUpdate(control._pendingValue);
control._pendingChange = false;
}
});
}
which updates a model at last.
So, in your case, just remove (click)="onSubmit()" from the submit button:
<button type="submit">Next</button>
also you do not need required DOM element property on your input, since you set it using Reactive Forms API validators: Validators.required and since you set your form to novalidate which cancels HTML5 form validation.
I am trying to call my Child action on clicking any link. Basically what I know is Child Actions are called with #Html.Action() method. But this methods gets invoke automatically inside our view. I am trying to use #Html.ActionLink() but this is not working. Is there any way that we can call our Child actions on clicking any link.
You could use an AJAX link which will invoke the action and inject the partial result into the DOM. For example assuming you have the following action:
public ActionResult Foo()
{
return PartialResult();
}
you could write an action link and a div to hold the results:
#Html.ActionLink("click me", "someaction", "somecontroller", null, new { id = "mylink" })
<div id="result"></div>
and then in a separate javascript file AJAXify this link:
$(function() {
$('#mylink').click(function() {
$('#result').load(this.href);
return false;
});
});