I have two date/times that come from a db, however these are split into four (two dates and two times) on the view, both the date and time are custom elements. See below.
My issue is my start date/time cannot be greater than my end date/time. I know how to do this in c#:
Check whether date string contains a default time 00:00:00, if so remove.
Parse date/time strings in a new datetime
Repeat for second date/time
Validate whether from < to (return from < to)
But I don't know how to do this in Aurelia without doing a API call. I have noted isLessThan, however I would need to parse my strings into a datetime before I can do that.
bind(){
return this.dataContext.getContent(this.id)
.then(baseContent => {
this.baseContent = baseContent;
this.validator = this.validation.on(this)
.ensure('baseContent.ValidFromDate').isNotEmpty()
.ensure('baseContent.ValidFromTime').isNotEmpty()
.ensure('baseContent.ValidToDate').isNotEmpty()
.ensure('baseContent.ValidToTime').isNotEmpty()
.ensure('baseContent.ValidFromDate / baseContent.ValidFromTime').isLessThan('baseContent.ValidToDate / baseContent.ValidToTime')
;
}); }
I know the above doesn't work, I am new to Aurelia and still finding my feet with it.
UPDATE
I've tried the following but received a:
Unhandled promise rejection TypeError: path.split is not a function
I think the issue is it doesn't know what to assign the error message to but I could be wrong.
.ensure(this.datetimeformat.format(baseContent.ValidFromDate, baseContent.ValidFromTime)).isLessThan(this.datetimeformat.format(baseContent.ValidToDate, baseContent.ValidFromTime));
import moment from 'moment';
export class DateTimeFormat {
format(date, time) {
if (date.indexOf("T") > -1) {
date = date.split('T')[0];
}
return moment(date + 'T' + time, 'DD/MM/YYYY HH:mm:ss');
}
}
I noticed one of the answers used a computedFrom however as I need to first change these into DateTime, and parse back I don't know what I am meant to provide where.
UPDATE
.ensure('baseContent.ValidFromDate', (config) => { config.computedFrom(['baseContent.ValidFromDate', 'baseContent.ValidFromTime', 'baseContent.ValidToDate', 'baseContent.ValidToTime']) }).isNotEmpty().passes(() => {
return this.datetimeformat.format(this.baseContent.ValidFromDate, this.baseContent.ValidFromTime) < this.datetimeformat.format(this.baseContent.ValidToDate, this.baseContent.ValidToTime);
})
I think I am getting closer, but still not working :-(
I have also tried
.ensure('baseContent.ValidFromDate', (config) => { config.computedFrom(['baseContent.ValidFromDate', 'baseContent.ValidFromTime', 'baseContent.ValidToDate', 'baseContent.ValidToTime']) }).if(() => {
return this.baseContent.ValidFromDate !== null && this.baseContent.ValidFromTime !== null && this.baseContent.ValidToDate !== null && this.baseContent.ValidToTime !== null})
.passes( () => {return this.datetimeformat.format(this.baseContent.ValidFromDate, this.baseContent.ValidFromTime) < this.datetimeformat.format(this.baseContent.ValidToDate, this.baseContent.ValidToTime)})
.endIf().isNotEmpty()
No errors with developer tools.
function validateFromDateTimeIsBeforeToDateTime(dateFrom, timeFrom, dateTo, timeTo) {
debugger;
if (dateFrom !== null && dateFrom !== undefined &&
timeFrom !== null && timeFrom !== undefined &&
dateTo !== null && dateTo !== undefined &&
timeTo !== null && timeTo !== undefined) {
return this.datetimeformat.format(dateFrom, timeFrom) < this.datetimeformat.format(dateTo, timeTo);
}
else {
return true;
}
}
.ensure('baseContent.ValidFromDate').isNotEmpty().passes(validateFromDateTimeIsBeforeToDateTime(this.baseContent.ValidFromDate, this.baseContent.ValidFromTime, this.baseContent.ValidFromDate, this.baseContent.ValidFromTime))
.ensure('baseContent.ValidFromTime').isNotEmpty()
.ensure('baseContent.ValidToDate').isNotEmpty().passes(validateFromDateTimeIsBeforeToDateTime(this.baseContent.ValidFromDate, this.baseContent.ValidFromTime, this.baseContent.ValidFromDate, this.baseContent.ValidFromTime))
.ensure('baseContent.ValidToTime').isNotEmpty()
This however throws an "Unhandled promise rejection TypeError: Unable to get property 'datetimeformat' of undefined or null reference"
I'd recommend using the ubiquitous moment.js library in this case:
jspm install moment
Then use it like this for example:
import moment from 'moment';
export class MyViewModel {
date1 = "2016-02-29";
time1 = "09:00:00";
date2 = "2016-03-01";
time2 = "13:00:00";
myMethod() {
let isValid = moment(`${this.date1 this.time1`).isBefore(moment(`${this.date2 this.time2`));
alert(isValid);
}
}
After a-lot of investigation, fixing bugs here and there, I noted the outstanding issue was from my formatting class, it wasn't creating the date/time (using moment.js) correctly (after the user has selected from the calendar), meaning it returned 'invalid date' which you cannot compare on :-|
This will work:
this.validator = this.validation.on(this)
.ensure('baseContent.ValidFromDate', (config) => { config.computedFrom(['baseContent.ValidFromDate', 'baseContent.ValidFromTime', 'baseContent.ValidToDate', 'baseContent.ValidToTime']) })
.if(() => {
return this.baseContent.ValidFromDate !== null && this.baseContent.ValidFromTime !== null && this.baseContent.ValidToDate !== null && this.baseContent.ValidToTime !== null })
.passes( () => { return this.datetimeformat.format(this.baseContent.ValidFromDate, this.baseContent.ValidFromTime) < this.datetimeformat.format(this.baseContent.ValidToDate, this.baseContent.ValidToTime) })
.withMessage('< Valid To')
.endIf()
.ensure('toDate', (config) => {config.computedFrom(['fromDate'])})
.isGreaterThan( () => {return this.fromDate}, 'the entered Date');
Related
I wanted to do conditional http call and got the solution from Angular 11 how to make one of the http request in higher order mapping conditionally
#Injectable({
providedIn: 'root'
})
export class ProjectGuidService {
private projectsGuid = '';
projectGuid$ = this.projectsGuid !== ''
? of(this.projectsGuid)
: this.apiService.get('projects/latest').pipe(
map(({id}) => id),
tap(_ => console.log('current projectsGuid', this.projectsGuid)),
tap(guid => { console.log('tap project guid service', guid); this.projectsGuid = guid; })
);
constructor(
private apiService: ApiService,
private appSettingsService: AppSettingsService
)
{
console.log('project guid service');
//this.projectsGuid = this.appSettingsService.settings.projectId ?? '';
}
}
But in the above solution the http call is made even for the second time ie. even after the first time the value is set to this.projectsGuid.
Not sure what is wrong with it.
this is because the condition will be evaluated only one time.
you set this.apiService.get('projects/latest') in projectGuid$ and alway subscribe to the same, without evaluating it again.
one way to solve your problem is to move the logic in a funcion:
public projectGuid$():Observable<string>{
return this.projectsGuid !== ''
? of(this.projectsGuid)
: this.apiService.get('projects/latest').pipe(
map(({id}) => id),
tap(_ => console.log('current projectsGuid', this.projectsGuid)),
tap(guid => { console.log('tap project guid service', guid); this.projectsGuid = guid; })
);
}
and then you do this.projectGuid$().subscribe(). do not store the result in a variable as you did before.
RxJs provides the shareReplay Operator that does exactlty what you're intending. It repeats the last given value of the Observable.
So if you change your Property declaration to :
projectGuid$ = this.apiService.get('projects/latest').pipe(
map(({ id }) => id),
shareReplay(1)
);
The this.apiService.get('projects/latest') gets only called once, even for multiple Observers.
So i have a problem loading a GraphQl schema using gql-codegen. When I try to generate, it fails with 404 error, however schema is available at http://localhost:8000/graphql below is the error.
Cannot convert undefined or null to object
TypeError: Cannot convert undefined or null to object
at Function.entries (<anonymous>)
I've tried to replace locahost with 127.0.0.1 in my npm file but i got the same result.
Any help is much appreciated. Thanks in advance!
After some debugging I found that the error comes from ../node_modules/#graphql-tools/url-loader/cjs/index.js from the following method:
class UrlLoader {
...
handleSDL(pointer, fetch, options) {
for (const [key, value] of Object.entries(options.headers)) {
if (key.startsWith(':')) {
// omit http2 headers
isHttp2 = true;
} else {
headers[key] = value
}
}
return new value_or_promise_1.ValueOrPromise(() => fetch(pointer, {
method: defaultMethod,
headers: typeof (options === null || options === void 0 ? void 0 : options.headers) === 'function' ? options.headers() : options === null || options === void 0 ? void 0 : options.headers,
}))
... }
The problem was that the options.headers property might be undefined or null, which cannot be iterated using Object.entries(). What solved my problem was adding a check to ensure that options.headers is defined before iterating through its entries:
if (options.headers) {
for (const [key, value] of Object.entries(options.headers)) {
// ...
}
}
And similarly, for the fetch call, I added a check to ensure that options.headers is defined before passing it to the fetch method.:
return new value_or_promise_1.ValueOrPromise(() => fetch(pointer, {
method: defaultMethod,
headers: typeof options?.headers === 'function' ? options.headers() : options?.headers || {},
}));
Then after running gql-gen again the error was gone and everything worked as expected.
I am having a hard time trying to joing a filterBy with orderBy, on vuejs 2.0, with all research I have found about this subject, as of link on the bottom of my question.
This is my filter, which is working:
// computed() {...
filteredResults() {
var self = this
return self.results
.filter(result => result.name.indexOf(self.filterName) !== -1)
}
A method called in the component:
// methods() {...
customFilter(ev, property, value) {
ev.preventDefault()
this.filterBook = value
}
In the component:
// Inside my component
Name..
And another filter, which works as well:
// computed() {...
orderByResults: function() {
return _.orderBy(this.results, this.sortProperty, this.sortDirection)
}
To comply with my orderBy I have this method:
// methods() {...
sort(ev, property) {
ev.preventDefault()
if (this.sortDirection == 'asc' && this.sortProperty == property ) {
this.sortDirection = 'desc'
} else {
this.sortDirection = 'asc'
}
this.sortProperty = property
}
And to call it I have the following:
// Inside my component
Name..
I have found in the docs how we use this OrderBy, and in this very long conversation how to use filter joint with sort, but I could really not implement it...
Which should be some like this:
filteredThings () {
return this.things
.filter(item => item.title.indexOf('foo') > -1)
.sort((a, b) => a.bar > b.bar ? 1 : -1)
.slice(0, 5)
}
I could not make this work...
I tried in many forms as of:
.sort((self.sortProperty, self.sortDirection) => this.sortDirection == 'asc' && this.sortProperty == property ? this.sortDirection = 'desc' : this.sortDirection = 'asc' )
But still, or it does not compile or it comes with errors, such as:
property not defined (which is defines such as I am using it in the other method)
method of funcion not found (is happens when comment my method sort.. maybe here is what I am missing something)
Thanks for any help!
The ideas of your approach seem valid, but without a full example it's hard to tell what might actually be wrong.
Here's a simple example of sorting and filtering combined. The code can easily be extended e.g. to work with arbitrary fields in the test data. The filtering and sorting is done in the same computed property, based on the parameters set from the outside. Here's a working JSFiddle.
<div id="app">
<div>{{filteredAndSortedData}}</div>
<div>
<input type="text" v-model="filterValue" placeholder="Filter">
<button #click="invertSort()">Sort asc/desc</button>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
testData: [{name:'foo'}, {name:'bar'}, {name:'foobar'}, {name:'test'}],
filterValue: '',
sortAsc: true
};
},
computed: {
filteredAndSortedData() {
// Apply filter first
let result = this.testData;
if (this.filterValue) {
result = result.filter(item => item.name.includes(this.filterValue));
}
// Sort the remaining values
let ascDesc = this.sortAsc ? 1 : -1;
return result.sort((a, b) => ascDesc * a.name.localeCompare(b.name));
}
},
methods: {
invertSort() {
this.sortAsc = !this.sortAsc;
}
}
});
</script>
I have defined a validation rule like this
ko.validation.rules["studentValidation"] = {
validator: (val: any, params: any) => {
return (this.IsInRequiredRangeForStudent(params.DateOfBirth) && val === false);
}
}
IsInRequiredRangeForStudent = (dateOfBirth: any) () => {
//my implementation
}
Here is my ViewModel class, where i consume and apply this rule on an observable
this.isStudent = ko.observable<boolean>(isStudent).extend({
studentValidation: {
message: "Invalid student option!",
params: {
DateOfBirth: this.dateOfBirth()
}
}
});
In my validation rule implementation, I always get params.DateOfBirth as null. What I am doing wrong here?
params.DateOfBirth can be null for several reasons. But firstly I would check one scenario. There is a chance that when you extending isStudent observable, you define validation params assigning value of dateOfBirth observable. But the value is evaluated at the moment of assigning, I don't see the rest of your code but it's highly possible that dateOfBirth observable is null at the moment of assigning to params. So every further check of params.DateOfBirth may return NULL value.
Please try following:
this.isStudent = ko.observable<boolean>(isStudent).extend({
studentValidation: {
message: "Invalid student option!",
params: {
DateOfBirth: this.dateOfBirth
}
}
});
and this:
ko.validation.rules["studentValidation"] = {
validator: (val: any, params: any) => {
return (this.IsInRequiredRangeForStudent(params.DateOfBirth()) && val === false);
}
}
What it changes? It defines params.DateOfBirth as function (not a value), so you can evaluate its value on every validation call.
I'm using mongoose and trying to set a custom validation that tells the property shall be required (ie. not empty) if another property value is set to something. I'm using the code below:
thing: {
type: String,
validate: [
function validator(val) {
return this.type === 'other' && val === '';
}, '{PATH} is required'
]}
If I save a model with {"type":"other", "thing":""} it fails correctly.
If I save a model with {"type":"other", "thing": undefined} or {"type":"other", "thing": null} or {"type":"other"} the validate function is never executed, and "invalid" data is written to the DB.
As of mongoose 3.9.1, you can pass a function to the required parameter in the schema definition. That resolves this problem.
See also the conversation at mongoose: https://github.com/Automattic/mongoose/issues/941
For whatever reason, the Mongoose designers decided that custom validations should not be considered if the value for a field is null, making conditional required validations inconvenient. The easiest way I found to get around this was to use a highly unique default value that I consider to be "like null".
var LIKE_NULL = '13d2aeca-54e8-4d37-9127-6459331ed76d';
var conditionalRequire = {
validator: function (value) {
return this.type === 'other' && val === LIKE_NULL;
},
msg: 'Some message',
};
var Model = mongoose.Schema({
type: { type: String },
someField: { type: String, default: LIKE_NULL, validate: conditionalRequire },
});
// Under no condition should the "like null" value actually get persisted
Model.pre("save", function (next) {
if (this.someField == LIKE_NULL) this.someField = null;
next()
});
A complete hack, but it has worked for me so far.
Try adding this validation to the type attribute, then adjust your validation accordingly. E.g.:
function validator(val) {
val === 'other' && this.thing === '';
}
thing: {
type: String,
required: function()[{
return this.type === 'other';
}, 'YOUR CUSTOM ERROR MSG HERE']
}