How to access value of nova field in another custom field vue component - laravel

PHP 7.3
Laravel 5.8
I am creating a form field total, that is the multiplication of value from two other fields quantity and price
This is my code
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
computed: {
calculatedTotal: function () {
//return parseFloat(field.price) * parseFloat(field.quantity);
return 10;
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
formData.append(this.field.attribute, this.value || '')
},
/**
* Update the field's internal value.
*/
handleChange(value) {
this.value = value
},
},
}
and template
<template slot="field">
<input
:id="field.name"
type="text"
class="w-full form-control form-input form-input-bordered"
:class="errorClasses"
:placeholder="field.name"
v-model="calculatedTotal"
/>
</template>
I am unable to access those values here, the quantity field is Laravel Nova default Number field and the price field is Money field. with the commented code above I am getting error, undefined field.

If you're inside a field, I think you should look for other fields inside current field's parent.
In similar cases I did something like:
calculatedTotal: function () {
return parseFloat(this.getElementValue(this.$parent, 'price')) * parseFloat(this.getElementValue(this.$parent, 'quantity'));
}
and in methods section I created the getElementValue as a method looking into all children of the passed parent and getting value for the one with the passed attribute:
getElementValue(root, elemName){
let value = null
root.$children.forEach(component => {
if (component.field !== undefined && component.field.attribute == elemName) {
value = component.field.value
}
})
return value
}

Related

How to create custom floating filter component in ag-grid that uses "inRange" filter type

I'm trying to build a custom filter component that takes a range from a text input control (e.g. '3-5') to filter the data. To do so I have modified the example given in the ag-grid documentation (see code below).
When changing the type in onFloatingFilterChanged() to 'equals', 'greaterThan', 'lessThan' etc. everything works fine. But with type 'inRange' no filtering is performed.
Working example can be found on Plunkr: https://plnkr.co/edit/oHWFIaHgWIDXP0P5
import { Component } from '#angular/core';
import {
IFloatingFilter,
IFloatingFilterParams,
NumberFilter,
NumberFilterModel,
} from '#ag-grid-community/all-modules';
import { AgFrameworkComponent } from '#ag-grid-community/angular';
export interface RangeFloatingFilterParams extends IFloatingFilterParams {
value: number;
}
#Component({
template: `
<input
type="text"
[(ngModel)]="currentValue"
(ngModelChange)="valueChanged()"
style="width: 70px;"
/>
`,
})
export class RangeFloatingFilter
implements IFloatingFilter, AgFrameworkComponent<RangeFloatingFilterParams> {
private params: RangeFloatingFilterParams;
public currentValue: string;
agInit(params: RangeFloatingFilterParams): void {
this.params = params;
this.currentValue = '';
}
valueChanged() {
let valueToUse = this.currentValue === 0 ? null : this.currentValue;
this.params.parentFilterInstance(function(instance) {
(<NumberFilter>instance).onFloatingFilterChanged(
'inRange',
valueToUse
);
});
}
onParentModelChanged(parentModel: NumberFilterModel): void {
if (!parentModel) {
this.currentValue = 0;
} else {
// note that the filter could be anything here, but our purposes we're assuming a greater than filter only,
// so just read off the value and use that
this.currentValue = parentModel.filter;
}
}
}
Faced the same issue with custom floating datepicker. I used setModelIntoUi method instead of onFloatingFilterChanged:
instance.setModelIntoUi({
type: 'inRange',
dateFrom: moment(value.min).format('YYYY-MM-DD'), // you have to use exactly this date format in order for it to work
dateTo: moment(value.max).format('YYYY-MM-DD'),
});
And in your case with numbers it'll be:
instance.setModelIntoUi({
type: 'inRange',
filter: value.min,
filterTo: value.max,
});
UPD: Added this line
instance.onUiChanged(true);
after the setModelIntoUi method, because of the bug: filter model wasn't updating on second use.
The code inside instance.onFloatingFilterChanged() only sets the first from value.
Use these lines below to get the correct result, as it is the only way to get inRange working.
instance.setTypeFromFloatingFilter('inRange');
instance.eValueFrom1.setValue(this._input1.value);
instance.eValueTo1.setValue(this._input2.value);
instance.onUiChanged(true);

Angular 2, custom validation messages with parameters

I'm just starting with JS/Typescript and Angular 2 and I'm struggling with the following.
export function MinImageDimensionsValidator(minWidth: number, minHeight: number): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
// it's an image control where a user uploads an image.
// the whole image related code has been removed for better readability.
//Just assume that 'actualWidth' holds the actual width of the image
if(actualWidth < minWidth) {
return { valid: false };
}
return null;
};
}
this is just a very basic example of a validator factory.
All the examples I found just wrote the validation messages/errors directly in the template (I'm using template forms)
Is it possible to "tie" the validation messages to the validator itself and use parameters with it?
like:
'Min width has to be 100. you supplied ' + actualWidth
this would be returned from the validator itself.
or is there another way (apart from storing everything in variables somewhere) ?
Yes, you can return any object from the validator. In your case it could be something like
return { minImageDimensions: { min: minWidth, value: actualWidth } }
When displaying field validation errors, you can do this:
<input #myField="ngModel" [(ngModel)]="...">
<span *ngIf="myField.errors.minImageDimensions">
Min width has to be {{ myField.errors.minImageDimensions.min }}.
You supplied {{ myField.errors.minImageDimensions.value }}.
</span>
Or even better use some localization and messages with parameters. You can make a component that will take a field object and display all kinds of error messages you use in your application according to the myField.errors object.
ValidatorFn should return a {[k:string]:any}, so it's as easy as this :
export function MinImageDimensionsValidator(minWidth: number, minHeight: number): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
if (actualWidth < minWidth) {
return {
myValidator: `Min width has to be ${minWidth}. you supplied ${actualWidth}`
};
}
return null;
};
}
then you can access this error like myFormControl.errors.myValidator.

Minimum one field required in a list of multiple fields in magento admin form

How to validate minimum one field required in a list of multiple fields in magento admin form.
Form Field includes text box,select fields in admin form.
if(Validation) {
Validation.addAllThese([ ['validation-myown','Please insert proper word',function(v,r){ var a = Validation.get('IsEmpty').test(v);
if(a == false){
return true;
}else{
return false;
}
} ], [ ] ]) }
You can try this using jquery:
$("#FORMID").submit(function(){
var elemcount=0;
$(this).find('input[type=text], select').each(function(){
if($(this).val() != "") elemcount+=1;
});
// If the value of elemcount variable is 0, all elements are empty.
// Your select should have a default value as blank.
// If it is not so you have to add separate code for select and input.
});

Dexie.js - ordering with more than one index

I am using dexie.js to interface with IndexedDB. I am wondering if it is possible to orderby or sortby using more than one index at once (eg. db.people.orderBy( index1, desc : index2, asc )...
If it is possible, what is the correct syntax?
Either use compound indexes, or use Collection.and().
If you can live with only targeting Chrome, Firefox or Opera, you can use compound indexes. If it must work on Safari, IndexedDBShim, Edge or IE, you cannot use compound indexes today. There's a shim that enables it for IE/Edge though, but it is still in beta, so I would recommend to instead use Collection.and() for those cases.
Let' say you have a form where users can fill in various attributes of friends:
<form>
<input name="name"/>
<input name="age"/>
<input name="shoeSize" />
</form>
Using Collection.and()
First, pick the most probably index to start your search on. In this case, "name" would be a perfect index that wouldn't match so many items, while age or shoeSize would probably match more friends.
Schema:
db.version(X).stores({
friends: "id, name, age, shoeSize"
});
Query:
function prepareQuery () {
// Pick a good index. The picked index will filter out with IndexedDB's built-in keyrange
var query;
if (form.name.value) {
query = db.friends.where('name').equals(form.name.value);
} else if (form.age.value) {
query = db.friends.where('age').equals(parseInt(form.age.value));
} else if (form.shoeSize.value) {
query = db.friends.where('shoeSize').equals(parseInt(form.shoeSize.value));
} else {
query = db.friends.toCollection();
}
// Then manually filter the result. May filter a field that the DB has already filtered out,
// but the time that takes is negligible.
return query.and (function (friend) {
return (
(!form.name.value || friend.name === form.name.value) &&
(!form.age.value || friend.age == form.age.value) &&
(!form.shoeSize.value || friend.shoeSize == form.shoeSize.value));
});
}
// Run the query:
form.onsubmit = function () {
prepareQuery() // Returns a Collection
.limit(25) // Optionally add a limit onto the Collection
.toArray(function (result) { // Execute query
alert (JSON.stringify(result, null, 4));
})
.catch (function (e) {
alert ("Oops: " + e);
});
}
Using compound indexes
As written above, compound indexes code will only work on mozilla- and chromium based browsers.
db.version(x).stores({
friends: "id, name, age, shoeSize," +
"[name+age+shoeSize]," +
"[name+shoeSize]," +
"[name+age]," +
"[age+shoeSize]"
});
The prepareQuery() function when using compound indexes:
function prepareQuery() {
var indexes = []; // Array of Array[index, key]
if (form.name.value)
indexes.push(["name", form.name.value]);
if (form.age.value)
indexes.push(["age", parseInt(form.age.value)]);
if (form.shoeSize.value)
indexes.push(["shoeSize", parseInt(form.shoeSize.value)]);
var index = indexes.map(x => x[0]).join('+'),
keys = indexes.map(x => x[1]);
if (indexes.length === 0) {
// No field filled in. Return unfiltered Collection
return db.friends.toCollection();
} else if (indexes.length === 1) {
// Single field filled in. Use simple index:
return db.friends.where(index).equals(keys[0]);
} else {
// Multiple fields filled in. Use compound index:
return db.friends.where("[" + index + "]").equals(keys);
}
}
// Run the query:
form.onsubmit = function () {
prepareQuery() // Returns a Collection
.limit(25) // Optionally add a limit onto the Collection
.toArray(function (result) { // Execute query
alert (JSON.stringify(result, null, 4));
})
.catch (function (e) {
alert ("Oops: " + e);
});
}
Using arrow functions here to make it more readable. Also, you're targeting chromium or firefox and they support it already.

Detect ng-model change into directive

I'm doing a custom validation (directive) to compare two dates and show error if start_date is greater than end_date...
I'm passing start_date through ng-model
ng-model="start_date"
and end_date with my directive:
lower-than-date="{{end_date.toISOString()}}" //ignore the toISOString()
The input where I'm using my directive...
<input type="text" ng-model="start_date"
datepicker-popup="yyyy-MM-dd" min="today" show-weeks="false"
lower-than-date="{{end_date.toISOString()}}"/>
The directive...
.directive("lowerThanDate", ['$parse', function($parse) {
return {
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
var lowerThan = null;
function revalidate(_val) {
var valid = _val && lowerThan ? _val < lowerThan : true;
ctrl.$setValidity('lowerThanDate', valid);
}
attrs.$observe('lowerThanDate', function(_value) {
//Here I'm detecting when end_date change
lowerThan = new Date(_value);
revalidate(ctrl.$modelValue);
});
ctrl.$parsers.unshift(function(_value) {
revalidate(_value);
return _value;
});
}
};
}])
This code is working fine, but the validation is triggered only when I change the end_date. I want to validate when I change start_date too.
So, the question is: How can I "observe" the ng-model value to trigger the validation when start_date change.
Note:
This is a generic directive to compare dates. Keep this on mind.
Set up binding to ngModel inside your link function:
var modelValue = $parse(attr.ngModel);
scope.$watch(modelValue, function(value) {
// validate value against endDate.
});
You should a formatter: "Array of functions to execute, as a pipeline, whenever the model value changes".
So just do something like:
function revalidate(_val) {
var valid = _val && lowerThan ? _val < lowerThan : true;
ctrl.$setValidity('lowerThanDate', valid);
return _val;
}
....
ctrl.$formatters.unshift(revalidate);
ctrl.$parsers.unshift(revalidate);
As the reason could be that the model changed from some other place, not directly in DOM element.

Resources