VeeValidate v3 extend access to Vue data - vee-validate

I am trying to use VeeValidate v3 to validate that a start and end time on a given day are available for a reservation. In my form I have the date displayed separately and then 2 input fields for start and end time. In my rule in .extend, I have several issues. The date isn't part of the form, so how do I get access to that in extend? My second question is what is the best way to indicate which field is being validated (is value start time or end time) ?
So far,my fields look like this:
<ValidationObserver>
<div class="w3-third">
<label>From</label>
<ValidationProvider vid='st' rules="available:#st,#et" v-slot="{ errors}">
<dropdown id="starttime" :options="startTimeOptions" v-model="startTime" #change="getDuration()"></dropdown>
<span>{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="w3-third ">
<label>Until</label>
<ValidationProvider vid="et" rules="available:#st,#et" v-slot="{ errors}">
<dropdown id="endtime" :options="endTimeOptions" v-model="endTime" #change="getDuration()"></dropdown>
<span>{{ errors[0] }}</span>
</ValidationProvider>
</div>
</ValidationObserver>
And my extend looks like this, I haven't gotten very far with this since I need the value for day as well:
validate(value, {s, e} ) {
var start= moment(day + "T" + s).format('YYYY-MM-DDTHH:mmZ')
var end = moment(day + "T" + e).format('YYYY-MM-DDTHH:mmZ')
/* make call to server here to see if that is available. */
return true;
},
params:[ 's', 'e']
});

I think you're on the right track. Try switching to the rules Object instead of string for ValidationProvider, and pass in your day property there.
<ValidationProvider vid="st"
:rules="{'available':['#st','#et',day]}"
v-slot="{ errors}">

Related

Vue JS Get value of each radio group

Hi Im struggling to get the value of each radio button groups. The functions is about getting answers from different questions.
{{form.answer_ids}}
<div v-for="ans in answers" :key="ans.id">
<div v-if="ans.question_id == question.id">
<div class="p-2 border rounded border-secondary m-2" >
<input
style="cursor:pointer;"
class="form-check-input m-2"
type="radio"
:name="ans.question_id"
:value="ans.id"
v-model="form.answer_ids"/>
<h5 class="ml-4 p-1" v-html="ans.description"> </h5>
</div>
</div>
</div>
<script>
data() {
return {
form: this.$inertia.form({
answer_ids:[]
}),
}
},
</script>
When there is v-model, the radio is not grouping yet returning only 1 value (not array), On the other hand, When I Remove v-model, The grouping is working but unable to get the data.
How can i possibly achieve this? Just taking the checked radio answer ids is enough for me.
Thank you very much and have a good day!

Vue-InstantSearch with Algolia & Laravel: Hide result on empty query

I'd like to figure out how to hide the index/result on an empty search query.
In my blade I have the code (slightly modified from the official documentation):
<ais-instant-search index-name="myIndex" :search-client="searchClient">
<ais-search-box placeholder="Search here"></ais-search-box>
<ais-state-results>
<template slot-scope="{ state: { query } }">
<ais-hits v-if="query.length > 0">
<div slot="item" slot-scope="{ item }">
<h2>#{{ item.Title}}</h2>
<p>#{{ item.Text}}</p>
</div>
</ais-hits>
</template>
</ais-state-results>
</ais-instant-search>
If I enter a search query this works fine, but on empty query this displays the following unwanted notification on my page (instead of the before unwanted index):
Use this component to have a different layout based on a certain state.
Fill in the slot, and get access to the following things on the slot-scope:
results: [
"_rawResults",
"hits",
"nbHits",
[..]
How can I hide this notification?
Ah, I just found a solution I think.
This notification text is displayed if there's no DOM element inside <ais-hits>. And in case of no query there isn't, since "v-if" removes that. So If instead of "v-if" I use "v-show" it works as I need it, since the DOM element still exists, but isn't displayed (display:hidden).
<ais-instant-search index-name="myIndex" :search-client="searchClient">
<ais-search-box placeholder="Search here"></ais-search-box>
<ais-state-results>
<template slot-scope="{ state: { query } }">
<ais-hits v-show="query.length > 0">
<div slot="item" slot-scope="{ item }">
<h2>#{{ item.Title}}</h2>
<p>#{{ item.Text}}</p>
</div>
</ais-hits>
</template>
</ais-state-results>

VeeValidate odd results on custom validation

I am validating start and end time fields in a form.
The inputs look like (yes, I have a lot of parameters to be able to do server-side validation):
<ValidationObserver>
<div class="w3-third">
<label>From</label>
<ValidationProvider vid='st' mode="eager" :rules="{'available': ['#st','#et', res_date, 'startTime', selectedField.id] }" v-slot="{ errors}">
<dropdown id="starttime" :options="startTimeOptions" v-model="startTime" #change="getDuration()"></dropdown>
<span class="w3-red">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="w3-third ">
<label>Until</label>
<ValidationProvider vid="et" :rules="{'available': ['#st','#et', res_date, 'endTime', selectedField.id] }" v-slot="{ errors}">
<dropdown id="endtime" :options="endTimeOptions" v-model="endTime" #change="getDuration()"></dropdown>
<span>{{ errors[0] }}</span>
</ValidationProvider>
</div>
</ValidationObserver>
Even with a very simple validation rule like this where I am simply return true every time:
extend('available', {
validate(value, {s, e, dt, which, field } ) {
console.log("validate", which );
return true;
},
message: 'The time is not available ',
params:[ 's', 'e', 'dt', 'which', 'field']
});
It validates once for each field when I enter the form component. But subsequent validations happen 3x times. Could this be related to how I am forming my rules object? I have changed the mode, but if I try "lazy" it doesn't validate at all after entering the form. Note that this is on a select dropdown input.
I see that both fields reference themselves in the rule:
<ValidationProvider vid='st' :rules="{'available': ['#st','#et', res_date, 'startTime', selectedField.id] }" v-slot="{ errors}">
that is why it triggers multiple validation attempts per change because it validates the input itself and validates each targeted field, so for the same input it validates 2x times and one more for the other field.
You should only reference other fields and avoid referencing itself, use the value instead.
<ValidationProvider vid='st' :rules="{'available': ['#et', res_date, 'startTime', selectedField.id] }" v-slot="{ errors}">
You will need to change your rule implementation to take that into account though.

how to get selected value of dropdown in reactive form

I am trying to get the selected value of the dropdown on change event to be sent on reactive form submission. I have a very similar scenario working for radio based on the answer from how to get selected value of radio in reactive form
Here's the code for dropdown
<div class="row" *ngIf="question.controls.type.value === 'dropdown'">
<div class="col-md-12">
<div class="form-group__text select">
<label for="type">{{ question.controls.label.value }}</label>
<br><br>
<select name="value" formArrayName="component" (change)="updateSelection(question.controls.component.controls, $event.target)">
<option
*ngFor="let answer of question.controls.component.controls; let j = index" [formGroupName]="j"
[ngValue]="answer?.value?.value">
{{answer?.value?.value}}
</option>
</select>
</div>
</div>
</div>
I am not able to pass the answer as formcontrol to updateSelection on change of a selected option from the dropdown. Any help is greatly appreciated.
https://stackblitz.com/edit/angular-acdcac
Very similarily like previous question, we iterate the form controls in your array, initially set all as false, and then turn the chosen choice as true. So template let's pass $event.target.value:
<select name="value" formArrayName="component"
(change)="updateSelection(question.controls.component.controls, $event.target.value)">
<option *ngFor="let answer of question.controls.component.controls; let j = index" [formGroupName]="j"
[ngValue]="answer?.value?.value">
{{answer?.value?.value}}
</option>
</select>
And in the component we as mentioned iterate the form controls and set all as false. The value for $event.target.value will be the string value, for example Choice 1. We then search for the form control that has that value and then set the boolean for that particular formgroup:
updateSelection(formArr, answer) {
formArr.forEach(x => {
x.controls.selectedValue.setValue(false)
})
let ctrl = formArr.find(x => x.value.value === answer)
ctrl.controls.selectedValue.setValue(true)
}
Your forked StackBlitz

How do we check Twitter Bootstrap radio buttons using Laravel Dusk?

According to twitter bootstrap, this is how we do a radio:
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
Option one is this and that—be sure to include why it's great
</label>
</div>
And this is my code:
$browser->click('#menu-reports')
->waitForText('Users')
->click('#menu-reports-users')
->radio('sitesActive', '2')
->radio('includeDisabled', '2')
->radio('includeNonCertifiable', '2')
->press('Apply')
->waitForText('Showing 0 to 0 of 0 entries')
;
With the input inside the label tag. But the problem is that Dusk (actually Facebook Webdriver) is not able to find it this way. It keeps raising:
Facebook\WebDriver\Exception\ElementNotVisibleException: element not visible
To make it work I have put the input outside the label, but then, of course, the boostrap radio does not show as it should anymore.
<div class="radio">
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
<label>
Option one is this and that—be sure to include why it's great
</label>
</div>
Does not work using IDs either:
Not even setting an ID to the input:
<input
type="radio"
name="sitesActive"
id="sitesActive3"
value="2"
>
And trying to select it this way:
->radio('#sitesActive3', '2')
The problem is that Dusk (Webdriver) cannot even see the element in the page, as this simple like fails the exact same way:
$browser->waitFor('#sitesActive3');
Resulting in:
Facebook\WebDriver\Exception\TimeOutException: Waited 5 seconds for selector [#sitesActive3].
And that happens every time I have a form with an input with a label surrounding it, if I take the input out of the label, it works. But that's not as simple with radios, as it was with some other inputs, radios.
This is a properly coded radio:
This is a radio with the input outside the label tag:
So, how are you doing this?
My form has a radio button. This how I checked it.
userCreate.blade.php
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Gender</label>
<div>
<label class="radio-inline">
<input type="radio" id="gender" name="gender" value="m">Male
</label>
<label class="radio-inline">
<input type="radio" id="gender" name="gender" value="f">Female
</label>
</div>
</div>
</div>
</div>
CreateUserTest.php
class CreateUserTest extends DuskTestCase
{
public function testCreateForm()
{
$this->browse(function (Browser $browser) {
$browser->visit('/user/create')
->radio('#gender', 'm')
->click('button[type="submit"]')
->assertSee('Successfully created user.');
});
}
}
This works for me. I think this will help you.
simplest way is to click on the parent
$el = $this->resolver->resolveForRadioSelection($field, $value);
$el = $el->findElement(WebDriverBy::xpath(".."));
$el->click();
Since the radio is not visible dusk cannot click on it
You may create a trait like the following if you are using bootstrap in your project
trait BootstrapInteraction
{
/**
* Undocumented variable
* #var \Laravel\Dusk\ElementResolver $resolver
*/
public $resolver;
public function radioB($field, $value)
{
/**
* #var RemoteWebElement $el
*/
$radio = $this->resolver->resolveForRadioSelection($field, $value);
// click on parent
$el = $radio->findElement(WebDriverBy::xpath(".."));
$el->click();
// if not selected click on label
if (!$radio->isSelected()) {
$el = $el->findElement(WebDriverBy::cssSelector("label"));
$el->click();
}
PHPUnit::assertTrue(
$radio->isSelected(),
"Not able to select Radio [{$field}] within value [{$value}]."
);
return $this;
}
You may not be happy to edit your views for the sake of your test script but if you are open to that, what about adding a class to the
<input type="radio" ... >
and then using
->click('.yourClass')
in your Dusk test?
The Dusk docs say:
To "select" a radio button option, you may use the radio method. Like many other input related methods, a full CSS selector is not required. If an exact selector match can't be found, Dusk will search for a radio with matching name and value attributes: $browser->radio('version', 'php7');
In my case, Dusk was working fine for most of my radio buttons, but it would not work for:
->radio('Field728', 'Know it\'s what anyone committed to this dream would do if she found a great program that features a proven process and supportive community')
I also tried using double-quotes, but that didn't work either. (Maybe the value is too long? I don't know.)
So instead I did this:
->radio('#Field728_0', true)//this is the specific ID of the first radio button of this group

Resources