Documenting Object with methods and their return values in JSDoc - jsdoc

I have a paging controller factory, that returns a paging controller Object with a bunch of methods (for the view to interact with, especially when an end-user does an action like navigate to a different page or enter some search text). It is defined something like:
/**
* Returns a paging controller object with data
* #param {Object[]} data
* #param {string} prop the property containing that data. If it's a function, it should be no-args.
* #param {filterFunc} filterer a callback that filters the data
*/
function pagingControllerFor(data, prop, filterer) {
let _currentPage = 0
let _filterFunc = filterer
let _stateChange = false
let _data;
const _ITEMS_PER_PAGE = 50
let _selectAllChecked = [];
/**
* Getter for all the data. Useful for debugging.
*/
function getAllData() {
if (prop) {
if (typeof data[prop] === 'function') {
return data[prop]()
}
return data[prop]
}
return data
}
/**
* Always returns fresh data for the controller
*/
function getData() {
let data = getAllData()
if (_filterFunc) {
if ((_stateChange) || (!_data)) {
_data = data.filter(_filterFunc)
_selectAllChecked = Array(Math.ceil(_data.length / _ITEMS_PER_PAGE)).fill(false)
_stateChange = false
}
return _data
}
return data
}
return {
/* a whole bunch of methods irrelevant to my use case on here */
getCurrentPageData : () => getData().slice(_currentPage * _ITEMS_PER_PAGE, (_currentPage + 1) * _ITEMS_PER_PAGE),
// get/set current "Select All" checkbox state
isCurrentSelectAllChecked : () => _selectAllChecked[_currentPage],
setCurrentSelectAllChecked : (checked) => _selectAllChecked[_currentPage] = checked
}
}
I am writing an event-binder for the "Select/Deselect All" checkboxes on the view being paginated. It is, as of the time I wrote this, defined to be:
/**
* Binds clicks on the current "Select/Deselect All" checkbox to the controller
* #param {string} modalType
* #param {{ getCurrentPageData : () => Array<{IsSelectedOnModal : boolean}>, setCurrentSelectAllChecked : () => boolean }} controller
* #param {Function} callback
*/
function bindToggleSelectAllEvent(modalType, controller, callback) {
callback = callback || bindToggleSelectAllEvent
const modalSelector = `#${modalType}-selector-modal`
$(`#toggle-all-${(modalType === ITEM) ? 'items' : 'categories'}-selected`)
.off('change')
.on('change', function() {
// get the state of this
let isChecked = $(this).prop('checked')
// change the selection state of all current items/categories in the controller to that state
controller.getCurrentPageData().forEach((data) => {
data.IsSelectedOnModal = isChecked
})
// tell the controller the new state of this "Select All" checkbox
controller.setCurrentSelectAllChecked(isChecked)
// Re-render modal?!
// TODO: implement this
})
}
VSCode knows what I'm doing, as it detects the relevant methods of controller, which I have specified.
However, JSDoc doesn't, for some reason:
ERROR: Unable to parse a tag's type expression for source file [my-project-path]\static\js\menu\edit\index.js in line 433 with tag title "param" and text "{{ getCurrentPageData : () => Array<{IsSelectedOnModal : boolean}>, setCurrentSelectAllChecked : () => boolean }} controller": Invalid type expression "{ getCurrentPageData : () => Array<{IsSelectedOnModal : boolean}>, setCurrentSelectAllChecked : () => boolean }": Expected "," or "}" but "=" found.
ERROR: Unable to parse a tag's type expression for source file [my-project-path]\static\js\menu\edit\index.js in line 439 with tag title "param" and text "{{ getCurrentPageData : () => Array<{IsSelectedOnModal : boolean}>, setCurrentSelectAllChecked : () => boolean }} controller": Invalid type expression "{ getCurrentPageData : () => Array<{IsSelectedOnModal : boolean}>, setCurrentSelectAllChecked : () => boolean }": Expected "," or "}" but "=" found.
What should I do about this?

VS Code support TypeScript types in JS Docs but the JS Doc tool only supports Closure types.
I believe that the arrow function type expressions that you are using are valid TypeScript types but cannot be understood by the JSDoc tool. Try using the function(): function type syntax instead
#param {{ getCurrentPageData : function(): Array<{IsSelectedOnModal : boolean}> }} controller

Related

Custom Validator Angular 2

I've written a web api function that takes a username from the textfield and checks if the username is already taken. To know if the username is available or not, my server returns Y if it is available and N if its not.
To validate the username, I'm using a ValidatorFn in Angular2 so validate the input. However, my validator function is not working.
Here is the validator function:
interface Validator<T extends FormControl> {
(c: T): { [error: string]: any };
}
function validateUsername(c: string) : ValidatorFn {
return (this.isAvailable(c)=='Y') ? null : {
validateUsername: {
valid: false
}
};
}
Here is the isAvailable function:
private isAvailable(username: string) {
let usernameAvailable;
let url = 'URL/api/auth/checkuser/' + username;
let headers = new Headers();
headers.append('User', sessionStorage.getItem('username'));
headers.append('Token', sessionStorage.getItem('token'));
headers.append('AccessTime', sessionStorage.getItem('AccessTime'));
let options = new RequestOptions({ headers: headers });
this.http.get(url, options)
.subscribe((res: Response) => usernameAvailable);
return usernameAvailable; //returns Y or N
}
Form Builder:
complexForm: FormGroup;
constructor(private http: Http, fb: FormBuilder) {
this.complexForm = fb.group({
'username': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10), validateUsername(this.complexForm.controls['username'].value)])],
})
}
validateUsername(this.complexForm.controls['username'].value) is failing and I'm getting this error:
[ts] Type '{ validateUsername: { valid: boolean; }; }' is not assignable to type 'ValidatorFn'. Object literal may only specify known properties, and 'validateUsername' does not exist in type 'ValidatorFn'. (property) validateUsername: {
valid: boolean;
}
You not adding your validator function correctly. You don't need to call your function when you register it:
this.complexForm = fb.group({
'username': [null, Validators.compose(
[
Validators.required,
Validators.minLength(5),
Validators.maxLength(10),
validateUsername <----- don't call it here
]
)],
})
You can see that some functions are called:
Validators.minLength(5),
But that is factory function call and not a validator function call. During initialization they return ValidatorFn:
/**
* Validator that requires controls to have a value of a minimum length.
*/
static minLength(minLength: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
...
}
See more in the official docs.
Also, it seems that your validator is async, so you have to pass it in the async array. And I don't think you need Validators.compose. The correct configuration should therefore be like this:
this.complexForm = fb.group({
'username': [null, [
Validators.required,
Validators.minLength(5),
Validators.maxLength(10),
], [validateUsername]]
})
Regarding the error:
Type '{ valid: boolean; }' is not assignable to type ValidatorFn.
You need to use the correct return type ValidationErrors instead of ValidatorFn:
function validateUsername(c: string) : ValidationErrors {
return (this.isAvailable(c)=='Y') ? null : {
validateUsername: {
valid: false
}
};
}

symfony assert for entity type variable

how validate entity variable, because in my point it's valid for empty select.
/**
* #Assert\NotBlank(message = "education level cannot be empty")
* #var EducationLevel[]|ArrayCollection
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\EducationLevel")
*/
private $educationLevel;
form type
->add('educationLevel', EntityType::class, [
'class' => 'AppBundle:EducationLevel',
'multiple' => true,
'choice_label' => function ($educationLevel) {
return $educationLevel->getName();
},
])
NotBlank won't work, because it checks if value is not null or not empty string or not false
NotBlank manual
What you have to do is to write custom Constraint and a validator:
Custom validator manual
You can create a validation method in the entity which can verify if $educationLevel is null or collection of EducationLevel instances.
/**
* #Assert\IsTrue(message="Education level has to be blank or...")
*/
public function isEducationLevelValid()
{
if ($this->educationLevel->isEmpty()) {
foreach ($this->educationLevel as $edulevel) {
if (!$edulevel instanceof EducationLevel) {
return false;
}
}
return true;
} else {
return false;
}
}
The method is used automatically during entity bound form submission and of course you can use it as a normal entity's method.

Connecting to the internet using Concept N

I can’t connect to the internet using Concept N. I am using jquery with a simple $.get, but it is just not working. Can someone help me with this?
You actually need to use the da.getXHr() function to access external web services. Here is an example of how to access rss data using jquery. First thing to do is download jquery and include it in your index.html file:
<script type="text/javascript" src="app/jquery.min.js"></script>
Now use this code snippet to access a rss url with jquery. Be sure to replace the line "** Enter-rss-2.0-url-here **" with your own rss url
/*
* Copyright 2016 Sony Corporation
*/
var title;
var description;
/**
* The callback to prepare a segment for play.
* #param {string} trigger The trigger type of a segment.
* #param {object} args The input arguments.
*/
da.segment.onpreprocess = function (trigger, args) {
console.log('onpreprocess', { trigger: trigger, args: args });
//da.startSegment(null, null);
$.ajax({
url:"** Enter-rss-2.0-url-here **",
type: "GET",
dataType: 'xml',
xhr: function () { return da.getXhr(); },
error: function (jqXHR, textStatus, errorThrown) {
console.log('ajax error jqXHR.status[' + jqXHR.status + ']');
def.reject("failed with error "+jqXHR.status);
return;
},
success: function (data, textStatus, jqXHR) {
$(data).find("item:first").each(function() {
title = $(this).find("title").text();
description = $(this).find("description").text()
})
da.startSegment(null, null)
}
})
};
/**
* The callback to start a segment.
* #param {string} trigger The trigger type of a segment.
* #param {object} args The input arguments.
*/
da.segment.onstart = function (trigger, args) {
console.log('onstart', { trigger: trigger, args: args });
var synthesis = da.SpeechSynthesis.getInstance();
synthesis.speak("Title "+title+", Content "+description, {
onstart: function () {
console.log('speak start');
},
onend: function () {
console.log('speak onend');
da.stopSegment();
},
onerror: function (error) {
console.log('speak cancel: ' + error.messsage);
da.stopSegment();
}
});
};

Angular2 interdependent form field validation

I have two form fields, where if the first field is filled in, the second field is mandatory. If I try to do this in Angular2, using a custom validator, the validator is only fired on initialization and when the specific field is changed.
Case:
- User fills in field 1
- Field 2 should become required, but isn't till the user actually changes field 2 (firing the custom validation).
private createForm():void {
this.testForm = this._formBuilder.group({
'field1': [],
'field2': ['', this.validateRequired()]
});
}
private validateRequired(){
console.log("something", this);
let component = this;
return (control: Control): { [s: string]: boolean } => {
return component.testModel.field1 && !control.value {"required":true} : null;
}
}
See this plunkr: http://plnkr.co/edit/PEY2QIegkqo8BW1UkQS5?p=preview
Edit:
For now I subscribed to field1's valueChange observable and when changed execute a manual check on field2, like:
this.testForm.controls['field1'].valueChanges.subscribe(
value => {
component.testForm.controls['field2].updateValueAndValidity();
}
)
But I feel like there must be a better way to do this.
You could use a global validator for the group like this:
private createForm():void {
this.testForm = this._formBuilder.group({
'field1': [],
'field2': ['', this.validateRequired()]
}, {
validator: this.someGlobalValidator // <-----
});
}
someGlobalValidator(group: ControlGroup) { // <-----
var valid = false;
for (name in group.controls) {
var val = group.controls[name].value
(...)
}
if (valid) {
return null;
}
return {
someValidationError: true
};
}
I want to expand on Thierry's answer a bit in order to address Arne's comment. In order to handle the validation of multiple fields and possibly multiple validations in your formgroup level validator the solution is to return a function from your validator that then returns an object that indicates the error type. Here is a example of a field matching validator that I added some extra errors to in order to illustrate the point. Note that it returns an object with possibly several properties where each object property is any string and the value is a boolean.
export function FieldMatchingValidator(field1: string, field2 :string) {
return (cg: FormGroup): { [s: string]: boolean } => {
let retVal = null;
let f1 = cg.controls[field1];
let f2 = cg.controls[field2];
retVal = f1.value === f2.value ? null : { fieldMismatch: true };
if(somecondition){
retVal['someerror'] = true;
}
if(someothercondition){
retVal['someothererror'] = true;
}
return retVal;
}
}
When this validator runs, if an error condition is encountered, then the form's errors property will be populated with the returned object with one or more properties indicating different errors. Then all you have to do it put the appropriate angular property setting on the controls that have the validation errors.
<div *ngIf="myForm.hasError('fieldMismatch')">
Field Mismatch
</div>
<div *ngIf="myForm.hasError('someerror')">
Some Error
</div>
<div [class.Errors]="myForm.hasError('someothererror')">
Some Other Error
</div>

How I can make an query with mongoose from a function using a parameter?

I try make a mongoose query using a function like this:
/*
* #param {function} Model - Mongoose Model
* #param {String} searchText- Text that will be used to search for Regexp
* #param {String} Key- key to search into a Model
* #param {object} res - Response of node.js / express
*/
function _partialSearch (Model, searchText, key, res) {
var search = new RegExp(searchText, "i");
Model.find({ key : { $regex : search } })
.exec(function (err, docs) {
if(err) log(err);
else {
res.json(docs);
}
})
}
My problem is the query take a parameter key literal and search like this:
I need this:
_partialSearch(Products, 'banana', 'fruts', res)
I spect this:
Products.find({ 'fruts' : 'banana})
But I get this:
Products.find({ key : 'banana})
Use the bracket notation to create the query object dynamically, so you could restructure your function as follows:
function _partialSearch (Model, searchText, key, res) {
var search = new RegExp(searchText, "i"),
query = {};
query[key] = { $regex : search };
Model.find(query)
.exec(function (err, docs) {
if(err) log(err);
else {
res.json(docs);
}
});
}
You can't directly use a variable as an object key like that, but assuming you're using Node.js 4.0 or above, you can use its ES6 support for computed property names to do this by surrounding key in brackets:
Model.find({ [key] : { $regex : search } })

Categories