Validation .passes if null/empty/length == 0 doesn't fire - aurelia

Validation doesn't seem to be called when the property has no value.
This is the code I'm using to try and make it work:
.ensure('baseContent.SetNamePrint').passes((name) =>
{
var webNameLength = this.baseContent.SetNameWeb.length;
var printNameLength = name.length;
console.log(webNameLength);
console.log(printNameLength);
if ((webNameLength > 1 && webNameLength < 51) || (printNameLength > 1 && printNameLength < 51)) {
return true;
}
return false;
}).withMessage('Web Name or Print Name is Required')
The passes only fires when the value of the property changes to something with a length, when it's empty (a blank string) nothing happens.
What I need is for the .passes() to be called every time there is a change to the value not just when there is a change and it has a value.

You need to additionally constraint the target property with isNotEmpty.
.ensure('baseContent.SetNamePrint').isNotEmpty().passes(...
From the documentation:
The isNotEmpty rule is always checked first before any other
validation rule. This means that without the isNotEmpty rule, the
.hasMinLength(5) rule would still consider a value of '' as valid
because the field is allowed to be empty.
PS: I heard that aurelia-validation is under rewriting. Perhaps that's why I can't find the documentation from the master branch anymore, but in another branch here

To get what I wanted I ended up with the following code.
this.validator = this.validation.on(this)
.ensure('SetNameWeb', (config) => {config.computedFrom(['SetNamePrint', 'SetNameWeb'])})
.if(() => { return this.HasImageEitherPrintNameOrWebName === false })
.isNotEmpty().withMessage('or "Print Name" is required')
.hasLengthBetween(0, 50)
.endIf()
.ensure('SetNamePrint', (config) => {config.computedFrom(['SetNameWeb', 'SetNamePrint'])})
.if(() => { return this.HasImageEitherPrintNameOrWebName === false })
.isNotEmpty().withMessage('or "Web Name" is required')
.hasLengthBetween(0, 50)
.endIf()
This gave me the functionality I needed with both of the fields being updated with each other.
However there was a bug in the aurelia code with the way it handles computedFrom that needed fixing to get this to work.
This problem was with aurelia-validation#0.6.8 though and there is now a new version which works in a completely different way so my recommendation is to update if you're having this problem.

Related

Correct search statement when applying filters

I have created a search function for my React-native app, however, I am not quite sure how to create the correct search statement. What I have now is as follows. You can open a filter screen where you can type a few search criteria (for vehicles) so make, model, color, license plate.
After saving the filters you are re-directed to a result page. On this page, I populate a const with Redux data (the vehicle database) and then filter this data before showing it in flatlist.
const vehicles = useSelector(state => state.uploadedVehicles.vehicles)
const filters = props.navigation.getParam('savedFilters')
const filteredVehicles = vehicles.filter(vehicle =>
vehicle.make === filters.makeFilter ||
vehicle.model === filters.modelFilter ||
vehicle.color === filters.licenseplateFilter ||
vehicle
.licenseplate === filters.licenseplateFilter
)
...return Flatlist with filteredVehicles here...
If I set a filter for a particular Make, only vehicles from this make are found. If I set a filter for a model, only vehicles from this model are found. However, if I set a filter for two statements it now shows vehicles with one matching search criteria. If I would search for a blue Dodge I would find vehicles matching the make Dodge, but also every blue vehicle that is uploaded.
How can I expand my search function so It will show vehicles matching 1 filter, but if 2 or more filters are added it will combine these filters to a more specific search function?
I like to take another approach to this, also using Redux. Here I show you an example code:
case SAVE_FILTERS: {
const appliedFilters = state.filters; // Here you have your filters that have to be
initialized in your state. They also have to be turned to true or false but you can
do it in another function (See next one)
// Here we check every condition
const updatedData = state.data.filter(data => {
if (appliedFilters.filter1 && !data.filter1) {
return false;
}
if (appliedFilters.filter2 && !data.filter2) {
return false;
}
if (appliedFilters.filter3 && !data.filter3) {
return false;
} else {
return true;
}
});
// and now you will return an updated array of data only for the applied filters
return {
...state,
displayData: updatedLocations,
};
The trick is to check every single condition inside of the action. In this particular case, we are checking by filters that are true or not, but you can expand to other conditionals.
The flow for the code above is:
If we have a filter applied AND the data HAS that filter, then we pass the conditional. WE do this for all conditionals. If we pass all of them, return true which means it will be added to the display data and the user will see it.
I hope it helps.
Maybe this isn't the most beautiful way of getting the filter to work. But I didn't quite get my filter working with MIPB his response bit it did push me in the right direction.
I am passing the filters in appliedFilters. Then I constantly checking every filter with the part of the vehicle that is filtered for.
Starting with the make of the vehicle. If the make filter is nog set (so "") I just return the vehicles array which contains every vehicle, else I return every vehicle that is matched with the appliedFilters.makeFilters.
This new makeFilterArray is checked with the modelFilter. If this is not set just set it to the makeFilter array to continue checking other filters, if it is set check the makeFilterArray for the matching model.
Maybe not the best/most elegant solution, but with my limited knowledge I got it working! :-)
case FILTER_VEHICLES:
const appliedFilters = action.setFilters;
console.log(appliedFilters)
console.log(appliedFilters.makeFilter)
console.log(appliedFilters.modelFilter)
console.log(appliedFilters.licenseplateFilter)
console.log(appliedFilters.colorFilter)
const makeFilterArray = appliedFilters.makeFilter === "" ? vehicles : state.vehicles.filter(vehicle => vehicle.make === appliedFilters.makeFilter)
const modelFilterArray = appliedFilters.modelFilter === "" ? makeFilterArray : makeFilterArray.filter(vehicle => vehicle.model === appliedFilters.modelFilter)
const licenseplateFilterArray = appliedFilters.licenseplateFilter === "" ? modelFilterArray : modelFilterArray.filter(vehicle => vehicle.licenplate === appliedFilters.licenseplate)
const filteredArray = appliedFilters.colorFilter === "" ? licenseplateFilterArray : licenseplateFilterArray.filter(vehicle => vehicle.color === appliedFilters.color)
// and now you will return an updated array of data only for the applied filters
return {
...state,
filteredVehicles: filteredArray
};

How to select efficiently from a long list of options in react-select

My use case is to allow the user to select a ticker from a long list of about 8000 companies. I fetch all the companies when the component mounts, so I don't really need the async feature of react-select. The problem really is displaying and scrolling through the 8000 items (as described in several open issues like this one).
My thought is why display 8000 entries when the user can't do anything meaningful with such a big list anyway. Instead why not show a maximum of 5 matches. As the user types more, the matches keep getting better. Specifically:
When the input is blank, show no options
When the input is a single character, there will still be hundreds of matches, but show only the first 5
As the user keeps on typing, the number of matches will reduce, but still limited to 5. However they will be more relavant.
I am not seeing this solution mentioned anywhere, so was wondering if it makes sense. Also wanted to find out what's the best way to implement it with react-select. I have tried the following two approaches - can you think of a better way:
Approach 1: Use Async React Select
Although I don't need async fetching, I can use this feature to filter down the options. It seems to work very well:
const filterCompanies = (value: string) => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
let count = 0;
return inputLength === 0
? []
: companies.filter(company => {
const keep =
count < 5 &&
(company.ticker.toLowerCase().indexOf(inputValue) >= 0 ||
company.name.toLowerCase().indexOf(inputValue) >= 0);
if (keep) {
count += 1;
}
return keep;
});
};
const promiseOptions = (inputValue: string) =>
Promise.resolve(filterCompanies(inputValue));
return (
<AsyncSelect<Company>
loadOptions={promiseOptions}
value={selectedCompany}
getOptionLabel={option => `${option.ticker} - ${option.name}`}
getOptionValue={option => option.ticker}
isClearable={true}
isSearchable={true}
onChange={handleChange}
/>
);
Approach 2: Use filterOption
Here I am using the filterOption to directly filter down the list. However it does not work very well - the filterOption function is very myopic - it gets only one candidate option at a time and needs to decide if that matches or not. Using this approach I cannot tell whether I have crossed the limit of showing 5 options or not. Net result: with blank input I am showing all 8000 options, as user starts typing, the number of options is reduced but still pretty large - so the sluggishness is still there. I would have thought that filterOption would be the more direct approach for my use case but it turns out that it is not as good as the async approach. Am I missing something?
const filterOption = (candidate: Option, input: string) => {
const { ticker, name } = candidate.data;
const inputVal = input.toLowerCase();
return (
ticker.toLowerCase().indexOf(inputVal) >= 0 ||
name.toLowerCase().indexOf(inputVal) >= 0
);
};
return (
<ReactSelect
options={companies}
value={selectedCompany}
filterOption={filterOption}
getOptionLabel={option => `${option.ticker} - ${option.name}`}
getOptionValue={option => option.ticker}
isClearable={true}
isSearchable={true}
onChange={handleChange}
/>
);
you can try using react-window to replace the menulist component
ref : https://github.com/JedWatson/react-select/issues/3128#issuecomment-431397942

Can We Get Vue to 'Detect Changes' to an Array If We Use `Filter()`?

First, I had this: parts = parts.filter(part => part.id !== change.doc.id);
So, data is an Array and it gets 'clobbered' with a new 'filtered' Array.
Vue didn't seem keen on detecting the change and updating my DOM.
So, I saw this. Specifically: To deal with caveat 2, you can use splice:
I refactored (or is it 'de-factored' b/c my code 'grew'?) to this:
// Get index of part removed
const index = parts.forEach((part, i) => {
if (part.id === change.doc.id) {
return i;
}
});
parts.splice(index, 1);
She works...but really? Do I have to do this way? 😬
parts.splice(parts.findIndex(part => part.id === change.doc.id), 1);
I got the splice in there while still keeping as 1 line, so m happy! 🤓

How to clear a date from input type="date" with WebdriverIO

I'm now using WebdriverIO and developing a web app.
these days I tried to set a date from input type="date", I got errors
invalid element state: Element must be user-editable in order to clear
it.
and found that
I could get rid of the errors by using addValue() but still the value won't be cleared by any API.
client.clearElement('#deadline')
Also get
invalid element state: Element must be user-editable in order to clear
it.
How can I remove the value from the form?
You can run in browser script to clear it
browser.execute(function () {
document.querySelector('#deadline').value = '';
}, null);
OR give it some value
var date = '2020-03-28';
browser.execute(function (date) {
document.querySelector('#deadline').value = date';
}, date);
reference: https://github.com/webdriverio/webdriverio/issues/386
A more elegant way is to create a custom command and put this piece of code inside
this one worked for me:
client.selectorExecute("#dateInput", function(inputs, value) {
// you can run over the inputs
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].type == 'date') // any condition
inputs[i].value = "1973-12-09";
}
// or just do that:
inputs[i].value = "1973-12-09";
return;
})

KnockoutJS throttle input

I'm trying to implement something like a typesafe ViewModel using KnockoutJS. It works pretty well until I start to update observables via HTML input tags.
I have implemented type extender which returns computed observable:
return ko.computed({
read: target,
write: fixer
})
Where fixer is something like:
function (newValue) {
var current = target(),
valueToWrite = (newValue == null ? null : fixNumber(newValue, 0));
if (valueToWrite !== current) target(valueToWrite);
else if (newValue !== current) target.notifySubscribers(valueToWrite);
}
And fixNumber is
function fixNumber(value, precision) {
if (value == null || value === '') return null;
var newValue = (value || '').toString().replace(/^[^\,\.\d\-]*([\.\,\-]?\d*)([\,\.]?\d*).*$/, '$1$2').replace(/\,/, '.'),
valueToWrite = Number(newValue);
return !!(valueToWrite % 1) ? round(valueToWrite, precision) : valueToWrite;
}
It looks not so straightforward, but I have to consider possible use of comma as a decimal separator.
Often I need to update my observables as soon as user presses key to reflect this change immediately:
<input type="text" data-bind="value: nonThrottled, valueUpdate: 'afterkeyup'"></input>
And here comes a lot of problems, because, for example, I can't input decimal values less than 1 (0.1, 0.2, etc) there.
When I try to throttle an observable it mostly works. But sometimes user input and type fixer go out of sync, so it looks like some input gets lost occasionally.
Full example is there http://jsfiddle.net/mailgpa/JHztW/. I would really appreciate any hints, since I have spent days trying to fix these problems.
UPDATE 11/04/2013
I solved my problem providing custom value binding, so now throttled observables doesn't eat my input occasionally.
I've added additional valueThrottle option-binding to throttle updating of element's value:
var valueThrottle = allBindingsAccessor()["valueThrottle"];
var valueThrottleTimeoutInstance = null;
/* ... */
if (valueThrottle) {
clearTimeout(valueThrottleTimeoutInstance);
valueThrottleTimeoutInstance = setTimeout(function () {
ko.selectExtensions.writeValue( element, ko.utils.unwrapObservable(valueAccessor()) );
}, valueThrottle);
} else applyValueAction();
Also I've noticed that inability to enter values like 0.2 in my case comes from that statement in original value binding:
if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
valueHasChanged = true;
I've rewritten it as
if ((newValue === 0) && (elementValue != 0))
valueHasChanged = true;
It works at least at Chrome, but I haven't tested it properly and even not sure that it's correct.
Example is to be added, for some reason jsFiddle does not accept my custom binding.
Any comments are really appreciated.