MatDatepickerFilter - Filter function can't access class variable - angular-material2

A MatDatePicker with a filter defined as followed:
<mat-form-field class="example-full-width">
<input matInput [matDatepickerFilter]="myFilter" [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
export class DatepickerFilterExample {
someDateToBlock: number = 3;
myFilter = (d: Date): boolean => {
const day = d.getDay();
// THIS FUNCTION CANNOT ACCESS THE VARIABLE 'someDateToBlock'
return day !== 0 && day !== 6;
}
}
I would like to access the variable someDateToBlock (or any other) in the filter function. Is there a workaround to make this possbile?

I had the same issue, seems like material date picker doesn't have access to "this" of the component for filter function. For me changing:
[matDatepickerFilter]="myFilterFunction"
to
[matDatepickerFilter]="myFilterFunction.bind(this)"
did the trick.

This is working, here is plunkr link: https://plnkr.co/edit/oRGfxLSrn6GdfRhYO1rr?p=preview
export class DatepickerOverviewExample {
someDateToBlock: number = 3;
myFilter = (d: Date): boolean => {
const day = d.getDay();
// THIS FUNCTION CANNOT ACCESS THE VARIABLE 'someDateToBlock'
return this.someDateToBlock;
}
}
I checked with alert(this.someDateToBlock) also

Type '(d: Date) => boolean' is not assignable to type 'DateFilterFn<Date | null>'
myFilter = (d: Date ** |null **): boolean => {
const day = (d || new Date()).getDay();
// Prevent Saturday and Sunday from being selected.
return day !== 0 && day !== 6;
};

You can
myLegalDate = (d: Date): boolean => {
//Your code
//You can see the variable someDateToBlock
console.log(this.someDateToBlock);
}
myFilter = this.myLegalDate.bind(this);

Related

Copy/Original printing in Netsuite

I am trying to print original first time and copy after the first time printing. I have created a custom field that stored the timestamp of first time printing. So, the template will check first time the field is empty so "original" is printed and the timestamp will stored to the field. Then, when the template is printed after the first time it will check the field, find that there is a content (The timestamp) so it will print copy on the printed template. everything is work fine, buttt when trying to access the advance template of the applied transaction (like: Bill or any) it show an error like below the code!!! What is the issue?
/**
*#NApiVersion 2.x
*#NScriptType UserEventScript
*/
define(['N/render','N/record'], function(render,record) {
function beforeLoad(context) {
var UserEventType = context.UserEventType;
var contextType = context.type;
var newRecord = context.newRecord;
var newRecordID= context.newRecord.id;
var currentDate = sysDate(); // returns the date
var currentTime = timestamp(); // returns the time stamp in HH:MM:SS
var currentDateAndTime = currentDate + ' ' + currentTime;
if (contextType == UserEventType.PRINT) {
var fieldId = 'custbody_first_print' // fieldId of your custom field / checkbox (or use a datetimestamp)
var isPrinted = newRecord.getValue({ fieldId: fieldId })
if (!isPrinted) {
var myRecord = record.load({id: newRecordID , type: newRecord.type}); // in the beforeLoad, editing the newRecord is not allowed, so you need to load the record first, edit and save.
myRecord.setValue({ fieldId: fieldId, value: currentDateAndTime })
myRecord.save();
}
}
}
function sysDate() {
var date = new Date();
var tdate = date.getDate();
var month = date.getMonth() + 1; // jan = 0
var year = date.getFullYear();
return currentDate = month + '/' + tdate + '/' + year;
}
function timestamp() {
var str = "";
var currentTime = new Date();
var hours = currentTime.getHours();
var minutes = currentTime.getMinutes();
var seconds = currentTime.getSeconds();
var meridian = "";
if (hours > 12) {
meridian += "pm";
} else {
meridian += "am";
}
if (hours > 12) {
hours = hours - 12;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
str += hours + ":" + minutes + ":" + seconds + " ";
return str + meridian;
}
return {
beforeLoad: beforeLoad,
};
});
The Error: Error during loading transaction for advanced printing Caused by: com.netsuite.suitescript.exception.NLServerSideScriptException: {"type":"error.SuiteScriptError","name":"SSS_MISSING_REQD_ARGUMENT","message":"load: Missing a required argument: id","stack":["createError(N/error)","beforeLoad(/SuiteScripts/Copy_Original.js:23)","createError(N/error)"],"cause":{"name":"SSS_MISSING_REQD_ARGUMENT","message":"load: Missing a required argument: id"},"id":"","notifyOff":false,"userFacing":true}
At first glance the base problem is that you are not passing a record ID to the record.load method.
A breakdown of the error:
What is the Error: "SSS_MISSING_REQD_ARGUMENT" (you are missing an argument that it needs in order to execute)
Where: "beforeLoad(/SuiteScripts/Copy_Original.js:23)" (the beforeLoad event in your suitescript Copy_Original, on line 23.)
What argument are you missing: "load: Missing a required argument: id" (after the : it tells you id)
I would change the names of your variables so they are different from the netsuite names. particularly newRecord, this will eliminate confusion and the possibility of you referencing a netsuite enum when you are trying to use your variable. (I believe that is what is happening here as I am able to get this to work on my record when using the following.)
function beforeLoad(context) {
var UserEventType = context.UserEventType;
var contextType = context.type;
var recObj= context.newRecord;
var recId= recObj.id;
var currentDate = sysDate(); // returns the date
var currentTime = timestamp(); // returns the time stamp in HH:MM:SS
var currentDateAndTime = currentDate + ' ' + currentTime;
if (contextType == UserEventType.PRINT) {
var fieldId = 'custbody_first_print' // fieldId of your custom field / checkbox (or use a datetimestamp)
var isPrinted = recObj.getValue({ fieldId: fieldId })
if (!isPrinted) {
var myRecord = record.load({id: recId, type: recObj.type}); // in the beforeLoad, editing the newRecord is not allowed, so you need to load the record first, edit and save.
myRecord.setValue({ fieldId: fieldId, value: currentDateAndTime })
myRecord.save();
}
It is worked now, check the sol. :
I should add && recId --->(context.newRecord.id)
if (contextType == UserEventType.PRINT && recId) {
var fieldId = 'custbody_first_print' // fieldId of your custom field / checkbox (or use a datetimestamp)
var isPrinted = recObj.getValue({ fieldId: fieldId })
if (!isPrinted) {
var myRecord = record.load({id: recId, type: recObj.type}); // in the beforeLoad, editing the newRecord is not allowed, so you need to load the record first, edit and save.
myRecord.setValue({ fieldId: fieldId, value: currentDateAndTime })
myRecord.save();

Dyanamic Dropdown List Validation (third level) using app script

I have a script written to apply three levels of validation. The first level of validation is in column A on the "master" sheet and the second and third are in the app script.
I am able to select the first level of validation in the "master"sheet and the list is filtered to only show the relevant answers in a dropdown list in Column B. After selecting my answer in Column B, the dropdown arrow appears in Column C but there are no fields to select. I can manually enter a value that is on my list and it is accepted and any thing that is not in the list is rejected. I have narrowed my issue to:
var filteredOptions = options.filter(function(o){ return o[0] === firstLevelColValue && o[1] === val });
When I place Logger.log(filteredOptions); in function applySecondLevelValadation(val, r){, it returns with an empty array [].
When I place
Logger.log(filteredOptions);` in `function applyFirstLevelValadation(val, r){
, it returns with the array:
[[Intermediate, 9:00 AM, Upper Dumont], [Intermediate, 12:00 PM, Outer Limts], [Intermediate, 3:30 PM, Satelite], [Intermediate, 9:00 AM, Box], [Intermediate, 12:00 PM, Hidden Valley], [Intermediate, 3:30 PM, Kermits]]
Screenshot of "options" sheet
Screenshot of "master" with all possible options for column A & B and no options for selection in column C
Any help figuring out why var filteredOptions = options.filter(function(o){ return o[0] === firstLevelColValue && o[1] === val }); is returning an empty array would be great!
Code being used:
var mainWsName = "master";
var optionsWsName = "options";
var firstLevelColumn = 1;
var secondLevelColumn = 2;
var thirdLevelColumn = 3;
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(mainWsName);
var wsOptions = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(optionsWsName);
var options = wsOptions.getRange(2,1,wsOptions.getLastRow()-1,3).getValues();
function onEdit(e) {
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var wsName = activeCell.getSheet().getName();
if(wsName === mainWsName && c === firstLevelColumn && r > 1) {
applyFirstLevelValadation(val, r);
} else if(wsName === mainWsName && c === secondLevelColumn && r > 1) {
applySecondLevelValadation(val, r);
}
}
function applyFirstLevelValadation(val, r) {
if(val === "") {
ws.getRange(r, secondLevelColumn).clearContent();
ws.getRange(r, secondLevelColumn).clearDataValidations();
ws.getRange(r, thirdLevelColumn).clearContent();
ws.getRange(r, thirdLevelColumn).clearDataValidations();
} else {
ws.getRange(r, secondLevelColumn).clearContent();
ws.getRange(r, secondLevelColumn).clearDataValidations();
ws.getRange(r, thirdLevelColumn).clearContent();
ws.getRange(r, thirdLevelColumn).clearDataValidations();
var filteredOptions = options.filter(function(o){ return o[0] === val });
var listToApply = filteredOptions.map(function(o){ return o[1] });
var cell = ws.getRange(r, secondLevelColumn);
Logger.log(filteredOptions)
applyValidationToCell(listToApply,cell);
}
}
function applySecondLevelValadation(val, r) {
if(val === "") {
ws.getRange(r, thirdLevelColumn).clearContent();
ws.getRange(r, thirdLevelColumn).clearDataValidations();
} else {
ws.getRange(r, thirdLevelColumn).clearContent();
var firstLevelColValue = ws.getRange(r, firstLevelColumn).getValue();
var filteredOptions = options.filter(function(o){ return o[0] === firstLevelColValue && o[1] === val });
var listToApply = filteredOptions.map(function(o){ return o[2] });
var cell = ws.getRange(r, thirdLevelColumn);
Logger.log(filteredOptions);
applyValidationToCell(listToApply,cell);
}
}
function applyValidationToCell(list, cell) {
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
It looks like you are having an issue with the time column. Probably because it's comparing a string to a date or what have you. We would have more information if you shared a copy of the spreadsheet.
In any case, to avoid this, you can format data in both columns B (in 'master' as well as in 'options') as plain text (for that, go to Format > Number > Plain text in your spreadsheet). Then, you can write the trip times in 'options' as plain text.
I hope this is of any help to you.

How do I pass additional parameters to normalize?

I have a field called "decimal" and with this field, I also have a property called "precision". So my field could be 0.00 or 0.000... etc.
Now I can have a normalize function that allows only numbers and one period, but I also need to pass something that tells me the precision of the field. What can I do here?
I guess I could create precisionOne(),precisionTwo(), precisionThree() etc.. but that seems like a lot of code duplication, for what would be precision(value, previousValue, precision)
EDIT: I've added some code below. It's pretty standard and made up of a few key parts:
createField.js = this is basically my component that receives the fields.json that holds the fields object, and their keys: {row_position, id, identifier, required, min_length, max_length, field_type}
field_type could be: number, text, decimal, date etc.
const row = [];
for(let j = fields.length - 1 - 1; j > 0; j--) {
const {row_position, id, identifier, required, min_length, max_length, field_type} = fields[j];
const validations = [];
//As of now normalize can only be one fuction
let normalize = null;
/// SETUP VALIDATIONS FOR FIELDS ///
if(required) {validations.push(validationFunctions.required)}; // REQUIRED
if(field_type === 3) {validations.push(validationFunctions.email)}; //EMAIL
if(field_type === 4) {validations.push(validationFunctions.number); normalize = normalizeFunctions.number;}; //NUMBER
if(field_type === 7) {validations.push(validationFunctions.number); normalize = normalizeFunctions.decimal;}; //DECIMAL
row.unshift(<Field key={id} name={identifier} component={renderField} validate={validations} normalize={normalize} props={fields[j]}/>);
}
this is my renderField component:
import React from 'react';
import {padWithZero} from '../../helpers/objectManipulationHelpers';
//Get field to display based on type
export default function constructField(field) {
const {input, type, field_type, className, cols, precision, required, id, identifier, addOn} = field;
let fieldTypeBasedCss = '';
let typeAddOn = null;
let iconAddOn = null;
let _precision = null;
let fieldType = 'text';
switch(field_type) {
case 2:
fieldType = 'password';
typeAddOn = <span class="fa fa-asterisk" aria-hidden="true"/>;
break;
case 3:
fieldTypeBasedCss = 'email-field';
typeAddOn = <span class="fa fa-at" aria-hidden="true"/>;
break;
case 4:
fieldTypeBasedCss = 'number-field';
typeAddOn = <span class="fa fa-sort-numeric-asc" aria-hidden="true"/>;
break;
case 6:
fieldTypeBasedCss = 'money-field';
typeAddOn = '$';
break;
case 7:
fieldTypeBasedCss = 'decimal-field';
typeAddOn = padWithZero('0.', precision + 2);
_precision = precision;
break;
case 8:
fieldTypeBasedCss = 'percent-field';
_precision = precision;
typeAddOn = '%';
break;
}
const css = `${className} ${fieldTypeBasedCss}`;
switch(type) {
//Type Text
case 0:
return (
<div className={typeAddOn || addOn ? 'input-group' : ''}>
{addOn ? <span class="input-group-addon" dangerouslySetInnerHTML={{__html: addOn}} /> : ''}
<input {...input} required={required} type={fieldType} size={cols} class={`form-control ${css}`} data-field-id={id} data-field-identifier={identifier} data-precision={_precision}/>
{typeAddOn ? <span class="input-group-addon">{typeAddOn}</span> : ''}
</div>
);
//....more field types below...
//.....
}
}
Now here is the important part:
My formNormalize.js holds all my normalizer functions like so:
//Intercept user input and return valid value field
//Force uppercase
export const upper = value => value && value.toUpperCase();
//Force lowercase
export const lower = value => value && value.toLowerCase();
//Force number
export const number = function(value, previousValue) {
return value.replace(/[^\d]/g, '');
};
//Here I need another variable passed that's called "precision" or
//something that let's me use the same function to return either 0.00 or
//0.000 etc depending on the precision of the decimal field. Right now the
//function will return any valid decimal number, so 0.0 or 0.00 or 0.0000000
//anything, but I need to be able to limit the number of decimal places.
export const decimal = function(value, previousValue) {
if(value && !isNaN(Number(value))){
return value;
} else {
return previousValue;
};
};
Your normalizer can take additional parameters like so:
export const normalizeWhatever = (paramOne,paramTwo) =>
(value, previousValue, allValues) => {
console.log(paramOne,paramTwo,value)
}

resuable validator for multiple cases

I made a date validator. It validates existing dates. I need multiple date validator with other restrictions, for ex : a max date validator that won't let the user put in a future date or a date validator that only takes past dates. This is my current validator.
export function dateValidator(group) {
const {day, month, year} = group.value;
const fullDate = `${day}/${month}/${year}`;
const dobPattern = /^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/;
const isStringValid = dobPattern.test(fullDate);
let isValid = false;
if (isStringValid) {
const intDay = Number(day);
const intMonth = Number(month);
const intYear = Number(year);
const jsMonth = intMonth - 1;
const date = new Date(intYear, jsMonth, intDay);
isValid = (date.getFullYear() === intYear && date.getMonth() === jsMonth && date.getDate() === intDay ;
}
return isValid ? null : { invalid: 'Invalid date' };
};
How can I restrict the user from putting in future dates.
I used this code with the following line:
isValid = (date.getFullYear() === intYear && date.getMonth() === jsMonth && date.getDate() === intDay ;
But I wonder if there is an easier way without having to copy and past this code over and over again to make small restrictions to it.
Your dateValidator() function should be a function factory (i.e. a function that returns a function) instead of a function that returns the error directly:
export function dateValidator(maxDate: string): ValidatorFn {
// Return a validator function.
return (group: FormGroup): {[key: string]: any} => {
// Re-use your existing validation code here and return the error if any.
// Optionally, use the `maxDate` param to customize the validation:
// entered dates should not go beyond `maxDate`.
};
}
As you can see, you can customize the validator function by passing parameters to the function factory. In my example, I used a maxDate parameter to indicate the furthest date in time that the validator should allow.
In your form model, use this validator by calling the factory with the appropriate value, e.g. :
this.myForm = fb.group({
'date': ['', [Validators.required(), dateValidator('02/20/2017')]]
});
You can see another example of a function factory for a validator in the doc: https://angular.io/docs/ts/latest/cookbook/form-validation.html#custom-validation

jQuery.validator.unobtrusive.adapters.addMinMax round trips, doesn't work in MVC3

I am creating a day range validator using DataAnnotations, jQuery.validate and jquery.validate.unobtrusive. I've already read the following:
http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html
http://weblogs.asp.net/mikaelsoderstrom/archive/2010/10/06/unobtrusive-validation-in-asp-net-mvc-3.aspx
and other but can't post them (noob)
As well as most of the post on SO. I'm baning my head against a wall, any help could be rewardde with beer/food/code/etc ;) Anyway here's the code:
I have a model object with the following parameter:
[Display(Name = "Start date"),
DayRange(0, 5, ErrorMessage = "The Start Date must be between today and 5 days time.")]
public DateTime StartDate { get; set; }
DayRange is a custom attribute class :
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DayRangeAttribute : RangeAttribute, IClientValidatable
{
private int _minimumDays;
private int _maximumDays;
public DayRangeAttribute(int minimumDays, int maximumDays) : base(minimumDays, maximumDays)
{
_minimumDays = minimumDays;
_maximumDays = maximumDays;
}
public override bool IsValid(object value)
{
var dateToBeTested = value as DateTime?;
return dateToBeTested.HasValue && dateToBeTested.Value >= DateTime.Today.AddDays(_minimumDays) && dateToBeTested.Value <= DateTime.Today.AddDays(_maximumDays);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "dayrange"
};
rule.ValidationParameters.Add("min", _minimumDays);
rule.ValidationParameters.Add("max", _maximumDays);
yield return rule;
}
}
I have the following in my web.config:
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
I have have following JS trigger before the document is ready (have tried triggering it when the doc is ready too ):
jQuery.validator.addMethod('dayrange', function (value, element, param) {
if (!value) return false;
var now = Date();
var dateValue = Date.parse(value);
var minDate = now.setDate(now.getDate() - param.min);
var maxDate = now.setDate(now.getDate() + param.max);
return this.optional(element) && dateValue >= minDate && dateValue <= maxDate;
}, 'Must fall in range');
jQuery.validator.unobtrusive.adapters.addMinMax('dayrange', 'minlength', 'maxlength', 'dayrange');
What am I doing wrong? Thanks in advance, Jol
Solved! I forgot/didn't understand that you have to pass jQuery itself into the function closure. Therefore the custom validator on the client side should look like this:
$(function () {
jQuery.validator.addMethod('dayRange', function (value, element, param) {
if (!value) return false;
var valueDateParts = value.split(param.seperator);
var minDate = new Date();
var maxDate = new Date();
var now = new Date();
var dateValue = new Date(valueDateParts[2],
(valueDateParts[1] - 1),
valueDateParts[0],
now.getHours(),
now.getMinutes(),
(now.getSeconds()+5));
minDate.setDate(minDate.getDate() - parseInt(param.min));
maxDate.setDate(maxDate.getDate() + parseInt(param.max));
return dateValue >= minDate && dateValue <= maxDate;
});
jQuery.validator.unobtrusive.adapters.add('dayrange', ['min', 'max', 'dateseperator'], function (options) {
var params = {
min: options.params.min,
max: options.params.max,
seperator: options.params.dateseperator
};
options.rules['dayRange'] = params;
if (options.message) {
options.messages['dayRange'] = options.message;
}
});
}(jQuery));
I also change the way I add the adapter to unobtrusive so I can add additional properties. Never send to server-side dev to do a front-end engineers job ;) Hope this helps someone.
Referenceļ¼š
http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html
adapters.addMinMax()'s param is orderby this:
adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute
so you need this:
jQuery.validator.unobtrusive.adapters.addMinMax('dayrange', '', '', 'dayrange','minlength', 'maxlength');
AND,,,
param.min, param.max be sure to undefine. param is an purely array as: ['111','000'].
so you need:
var minDate = now.setDate(now.getDate() - param[0]);
var maxDate = now.setDate(now.getDate() + param[1]);

Resources