Form-Elements added by JavaScript to a Fluid-Form are not passed as Argument.
How to do it the right way?
How to dynamically add Form-Fields with JavaScript?
Expected behavior:
Form is submmitted to "subscribeSubmitAction"
Validation of "subscribeSubmitAction" fails
Fallback to "subscribeAction" is called
"$subscription" is set and assigned (NOT HAPPENING) - instead "$subscription" is allways NULL (but subscription.test is set in the Fluid-Form)
My Guess:
The PropertyMapper is removing "subscription.childs" because they are not in "TrustedProperties" - How can I add them?
Why "$subscription" is allways NULL - I have no Idea!
Controller:
namespace VENDOR\EXTENSION\Controller;
class EventController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
public function initializeAction()
{
if ($this->arguments->hasArgument('subscription'))
{
//Try to add "subscription.childs" to the PropertyMapperConfiguration
$subscriptionConfiguration = $this->arguments->getArgument('subscription')->getPropertyMappingConfiguration();
$subscriptionConfiguration->allowProperties();
$subscriptionConfiguration->forProperty('*')->allowAllProperties();
$subscriptionConfiguration->forProperty('*')->allowCreationForSubProperty('*');
$subscriptionConfiguration->forProperty('*')->forProperty('*')->allowAllProperties();
$subscriptionConfiguration->forProperty('*')->forProperty('*')->allowCreationForSubProperty('*');
$subscriptionConfiguration->forProperty('childs')->allowProperties();
$subscriptionConfiguration->forProperty('childs')->allowCreationForSubProperty('*');
$subscriptionConfiguration->forProperty('childs.*')->allowProperties();
$subscriptionConfiguration->forProperty('childs.*')->allowCreationForSubProperty('*');
print_r($this->arguments->getArgument('subscription')->getValue()); // => NULL
}
}
/**
* subscribeAction
* #param array $event
* #param array $subscription
* #ignorevalidation $subscription
*/
public function subscribeAction($event,$subscription = null)
{
print_r($subscription); // => NULL
$this->view->assign('event',$event);
$this->view->assign('subscription',$subscription);
}
/**
* subscribeSubmitAction
* #param array $event
* #param array $subscription
* #param string $g_recaptcha_reponse
* #validate $g_recaptcha_reponse NotEmpty
*/
public function subscribeSubmitAction($event,$subscription = null, $g_recaptcha_reponse = null)
{
/**
* This Method will never be called because the Validation of "$g_recaptcha_reponse" must fail (it's empty)
*/
}
}
Template:
<f:debug>{subscription}</f:debug>
<f:form action="subscribeSubmit" name="subscription" object="{subscription}">
<f:form.hidden name='event[]' value='{event.uid}' />
<f:form.textfield property="test" />
<!--suppose this was added by javascript-->
<input type="text" name="tx_extension_plugin[subscription][childs][0][test]" value="{subscription.childs.0.test}">
<f:form.hidden name='g_recaptcha_reponse' value='' />
<f:form.submit name="submit" value="Submit" />
</f:form>
I would recommend to check the request via Network panel of your browser. Do you see the arguments there? Do they follow the naming schema of tx_extension_plugin[subscription][...?
They should be contained in the request within extbase.
Perhaps you shouldn't add them to the existing argument subscription but instead create another argument. You can validate it the same way and errors should be available the same way within Fluid.
You can be right with "TrustedProperties" I'm not sure about that. But if so, you definitely should be able to workaround that within your initializeAction method.
Related
I have been looking for a way to document this in js:
function foo(param1: 'opt1' | 'opt2') { ... }
But found nothing because I don't know how can I search for it and didn't find any example alike to what I want to do in JSDoc
I have tried with:
/**
* #param {string} param1 - Receives either 'opt1' | 'opt2'
*/
function foo(param1) { ... };
But I think there would be a better way to specify if param1 receives a string that's either 'these' or 'that'. Another solution than only put in the param description what you can pass as value like shown in my example.
You can document your function this way in case of strings:
/**
* #param {('opt1' | 'opt2')} param1
*/
function foo(param1) { };
Using the same method you can also handle integer values
/**
* #param {(1 | 2)} param1
*/
function foo(param1) { };
This will allow editors to give suggestions using the documented values like following
I'm centralizing user-facing message string templates in a Node API I'm writing so all my strings live together. I'd like to be able to build these strings dynamically from elsewhere in the code by passing a template function the arguments it needs to build the message, but the problem is that I can't figure out how to structure JSDoc to perform autocompletion for the object that's passed to my "string builders", AKA the functions returned from template below. See below for an example of what this would ideally look like. You can mostly ignore the template function implementation as it's taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates.
// strings.js
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
function template(strings, ...keys) {
return function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
};
}
/**
* Returns a string for welcoming mentees to the platform.
* #param {object} params
* #param {string} params.name The name of the mentee.
*/
export const menteeOnboardingWelcome = template`Hi ${'name'}, welcome!`;
/**
* Returns a string for when a mentee and mentor are matched.
* #param {object} params
* #param {string} params.menteeName The name of the mentee.
* #param {string} params.mentorName The name of the mentor.
*/
export const menteeMentorMatched = template`${'menteeName'} and ${'mentorName'} have been matched!`;
console.log(menteeMentorMatched({ menteeName: 'Jack', mentorName: 'Jill' }));
When typing that last line, I'd like autocomplete (in VSCode, if that's helpful) to list menteeName and mentorName plus their associated documentation, but since template returns a function that takes in ...values that's what it shows instead. How can I structure my JSDoc to show what I want instead of ...values?
Screenshot
Another option, of course, is to simply wrap the string builder functions in a function wrapper for documentation:
export const menteeMentorMatched = ({ menteeName, mentorName }) => `...`
I suspect this is the only way to make JSDoc work in this case. While this wouldn't be too bad, it's just a little more verbose than I'd like it to be since I'd like to avoid as much bloat as humanly possible in this file.
Is it possible to annotate JavaScript function parameters as can be done with attributes in C#?
Example (C#):
public void DoSomething([RequiredAction(Action="some_action")] Client client) {
// ...
}
The aim is - given a function - use reflection to inspect for instance the "type" of the parameters and perform the necessary conversions before calling it. JavaScript is indeed dynamically typed, but one could for instance want to use annotations to define the "type" of instances, a specific function expects (for instance, param should be an integer, or an array).
EDIT: The type aspect is only one possible use of an annotation. One could for instance also specify one must first run a specific function on that attribute or aspects like the maximum allowed length of an array.
One can of course use this answer and annotate the parameters by using specific parameter prefixes. For instance the Hungarian notation where sParam would identify the parameter should be a string. But that's not really convenient nor that extensible since it requires to specify names. Is there a more generic way to achieve this?
I like to use JSDOC, these are not checked at runtime but can be checked in certain editors (komodo edit for example) and stand alone applications (I think Google closure compiler is one). An example.
/**
* #namespace {Object} myObject
*/
var myObject = {};
/**
* This returns true if the operand inputArg is a String.
* #memberof myObject
* #name something
* #function
* #param {*} inputArg
* #returns {boolean}
*/
myObject.something = function (inputArg) {
return type inputArg === 'string';
};
If you want to require the argument type, rather than comment on its type, then the closest you can do is to make a function to simulate it.
function assertType(val, type) {
if (typeof val != 'object') {
if (typeof val != type.name.replace(/^./, function(f){return f.toLowerCase();}))
throw new Error("`" + val + "` is not of the data type `" + type.name + "`.");
}
else if (!(val instanceof type)) {
throw new Error("`" + val + "` is not of the data type `" + type.name + "`.");
}
}
function double(num) {
assertType(num, Number);
return num * 2;
}
console.log(double("malformed"));
/*
Error: `malformed` is not of the data type `number`.
at assertType:14:9
at double:18:2
at eval:21:13
at eval
*/
However, there isn't way to do this in the function declaration itself.
I need a way to loop through the registered controls of an AngularJS form. Essentially, I'm trying to get all the $dirty controls, but there's no array of the controls (the FormController has a number of different properties/functions in addition to containing the controls themselves - each as its' own object).
I've been looking at the source code, and I see that there is a controls array in the FormController that is exactly the array I'm looking for. Is there a way to get access to this value, or extend the FormController to include a function that returns this controls array?
Edit: Plnkr demo
Also, I realized that technically I could check the first character in the key string for "$", but I'd like to avoid that in case the FormController/directive changes in a future version of Angular.
Edit 2: Another clarification: My goal in all of this is to be able to determine which specific fields are $dirty, whether by looping through the entire list of controls (not including the $dirty, $invalid, $error, $name, and other properties that live in the Form object as it is) or by extending the FormController and creating a function that returns only the controls which are currently dirty (and not equal to their starting values)
Edit 3: The solution I'm looking for needs to be applicable to forms/models of different structures. The models on the scope are generated via AJAX, so their structure is already set (I'd like to avoid having to hardcode a new structure for all the data I'm already receiving via AJAX). Also, I'm looking to use this form submission process across multiple forms/models, and each of them have differing JSON structures - as they apply to different entities in our Object Model. This is why I've chosen to ask for a way to get access to the controls object in the FormController (I'll post the code from FormController below), because it's the only place where I can get a flat array of all of my fields.
function FormController(element, attrs) {
var form = this,
parentForm = element.parent().controller('form') || nullFormCtrl,
invalidCount = 0, // used to easily determine if we are valid
errors = form.$error = {},
controls = [];
// init state
form.$name = attrs.name || attrs.ngForm;
form.$dirty = false;
form.$pristine = true;
form.$valid = true;
form.$invalid = false;
parentForm.$addControl(form);
// Setup initial state of the control
element.addClass(PRISTINE_CLASS);
toggleValidCss(true);
// convenience method for easy toggling of classes
function toggleValidCss(isValid, validationErrorKey) {
validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
element.
removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey).
addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
}
/**
* #ngdoc function
* #name ng.directive:form.FormController#$addControl
* #methodOf ng.directive:form.FormController
*
* #description
* Register a control with the form.
*
* Input elements using ngModelController do this automatically when they are linked.
*/
form.$addControl = function(control) {
controls.push(control);
if (control.$name && !form.hasOwnProperty(control.$name)) {
form[control.$name] = control;
}
};
/**
* #ngdoc function
* #name ng.directive:form.FormController#$removeControl
* #methodOf ng.directive:form.FormController
*
* #description
* Deregister a control from the form.
*
* Input elements using ngModelController do this automatically when they are destroyed.
*/
form.$removeControl = function(control) {
if (control.$name && form[control.$name] === control) {
delete form[control.$name];
}
forEach(errors, function(queue, validationToken) {
form.$setValidity(validationToken, true, control);
});
arrayRemove(controls, control);
};
// Removed extra code
}
As you can see, the form itself has the controls array privately available. I'm wondering if there's a way for me to extend the FormController so I can make that object public? Or create a public function so I can at least view the private array?
For a direct solution to the question, modify #lombardo's answer like so;
var dirtyFormControls = [];
var myForm = $scope.myForm;
angular.forEach(myForm, function(value, key) {
if (typeof value === 'object' && value.hasOwnProperty('$modelValue') && value.$dirty)
dirtyFormControls.push(value)
});
The array 'dirtyFormControls' will then contain the form controls that are dirty.
You can also use this trick to show error messages on form submission for 'Required' validations and all others. In your submit() function you will do something like;
if (form.$invalid) {
form.$setDirty();
angular.forEach(form, function(value, key) {
if (typeof value === 'object' && value.hasOwnProperty('$modelValue'))
value.$setDirty();
});
//show user error summary at top of form.
$('html, body').animate({
scrollTop: $("#myForm").offset().top
}, 1000);
return;
}
And in your form you will show error messages with
<span ng-messages="myForm['subject-' + $index].$error" ng-show="myForm['subject-' + $index].$dirty" class="has-error">
<span ng-message="required">Course subject is required.</span>
</span>
The above solution is useful when you have dynamically generated controls using 'ng-repeat' or something similar.
You can use the following code to iterate the controls:
var data = {};
angular.forEach(myForm, function (value, key) {
if (value.hasOwnProperty('$modelValue'))
data[key] = value.$modelValue;
});
try simply with from within your controller:
$scope.checkForm = function(myFormName){
console.log(myFormName.$invalid);
}
UPDATE:
<div ng-controller="MyController">
<form name="form" class="css-form" novalidate>
<input type="text" ng-model="user.uname" name="uname" required /><br />
<input type="text" ng-model="user.usurname" name="usurname" required /><br />
<button ng-click="update(form)">SAVE</button>
</form>
</div>
app.controller('MyController',function($scope){
$scope.user = {};
$scope.update = function (form){
var editedFields = [];
angular.forEach($scope.user, function(value, key){
if(form[key].$dirty){
this.push(key + ': ' + value);
}
}, editedFields);
console.log(editedFields);
}
});
I've come up with a somewhat decent solution, but it still isn't what I was looking for. I've salvaged some code from another problem involving creating JSON objects from strings, and come up with the following:
Essentially I'm naming my fields in the same way they're tied to the model, and then building a new object for submission when the form_submit is called.
Plnkr demo
In the demo, if you change either of the form fields, then hit submit, you'll see the object pop up with only the dirty values.
In JSDoc there exists the possibility to document the exact types of array contents like this:
/** #param {Array.<MyClass>} myClasses An array of MyClass objects. */
TestClass.protoype.someMethod = function( myClasses ){
myClasses[0].aMethodOnMyClass();
}
This makes code completion in IDEs like WebStorm actually provide the right type information after the [0].. This works well for the Array type, however I have my own collection types where I would like to make use of this feature, too. The problem is I cannot find the right syntax (maybe because there is none, yet). I would love to be able to declare my class somehow like this:
/**
* #typeparam {T} the type parameter
* #constructor {Test2.<T>}
* */
Test2 = function(){};
/**
* #returns {T} a value of type T, where T is the generic type parameter of Test2
*/
Test2.prototype.getGenericValue = function(){}
This syntax or feature does not work with my IDE and is not listed here, so I am wondering whether there is a syntax for this use-case, either for WebStorm or any other JS authoring tool.
You can try using #template tag (undocumented tag used in Google Closure library - extremely limited form of generics). Something like:
/**
* Search an array for the first element that satisfies a given condition and
* return that element.
* #param {Array.<T>|goog.array.ArrayLike} arr Array or array
* like object over which to iterate.
* #param {?function(this:S, T, number, ?) : boolean} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array) and should return a boolean.
* #param {S=} opt_obj An optional "this" context for the function.
* #return {T} The first array element that passes the test, or null if no
* element is found.
* #template T,S
*/
goog.array.find = function(arr, f, opt_obj) {
var i = goog.array.findIndex(arr, f, opt_obj);
return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
};
WebStorm uses this tag for type hinting - i.e. if we pass array of strings to goog.array.find in the sample above , IDE will know that return type is string, so string completion options will be suggested etc.
Not sure if this is what you are looking for... The post that looks related is here.
In the meantime, support for this feature has been finalized and is now documented on the Closure Compiler JSDOC page for generics.
Basically it works like this for ES6 classes:
/** #template T */
class Foo {
/** #return {T} */
get() { ... };
/** #param {T} t */
set(t) { ... };
}
... and like this for pre-ES6 code:
/**
* #constructor
* #template T
*/
Foo = function() { ... };
and
/** #return {T} */
Foo.prototype.get = function() { ... };
/** #param {T} t */
Foo.prototype.set = function(t) { ... };
WebStorm 7.0 did not support this feature at the time the original answer was written, but as of today (2019) all JetBrains IDEs understand this syntax, properly.
The following code works fine for me in WebStorm 8.
/** #type {Array.<MyPair.<Event, Array.<Thought>>>} */
scope.pairs = [];
/**
* #template TFirst, TSecond
*/
function MyPair(first, second){
this.first = first;
this.second = second;
}
/** #type {TFirst} */
MyPair.prototype.first = null;
/** #type {TSecond} */
MyPair.prototype.second = null;
...
function Event(){}
...
...
function Thought(){}
...