How to add validation to Umbraco's custom backoffice section? - angularjs

I've created a custom section in my Umbraco back-end, and I'm trying to create some forms to capture data however I've been unable to fully understand how the umbraco directives work when it comes to validation.
As an example, I'm trying to capture some basic data and save it using the following:
<form name="myForm">
<umb-property property="form.properties.name">
<umb-property-editor model="form.properties.name"></umb-property-editor>
</umb-property>
<umb-button type="button"
label="Save"
button-style="success"
action="save()">
</umb-button>
And in my controller:
$scope.form: {
properties: {
name: {
alias: 'name',
label: 'Display name',
description: '',
value: '',
view: 'textbox',
validation: {
mandatory: true
}
}
}
I would have thought the 'validation' property would be where I can set the validation necessary on the property, however that doesn't seem to be the case. All that seems to do is highlight the property with a red asterix to indicate it's required rather than do any validation.
I've taken a look at the backoffice documentation here however it's unfortunately lacking any code examples and I'm puzzled how this is supposed to work.
I've looked at the other sections, particularly the create user form to see if I can pick anything up there but the only thing I've noticed is the 'valFormManager' directive which may be involved somehow.
I've also tried the obvious things like putting ng-required on the editor and property but neither has done the trick.
Is there any documentation for any of this? It's quite frustrating trying to work out how it's all supposed to work!
Thanks

Related

Hiding a text input without making it `type="hidden"`

What I have
I'm using the FormController to create inputs. A specific input will be frequently updated by Javascript for internal purposes and I want it hidden. In this case, I can't use type="hidden", instead it needs to be type="text" so that it won't be checked by the form tampering prevention when submitted.
What I tried
Adding 'hidden' => true to $this->Form->create() options works if the whole form needs to be hidden. That exact attribute doesn't seem to be working for individual inputs though, created using both $this->Form->input() and $this->Form->control()
For a specific input, first thing that comes to mind is adding 'style' => 'display:none' to its options, but that does not seem like a clean CakePHP-way solution
I think I've seen someone mention a way to do exactly that here. I think it was an attribute you'd add to the options. I searched around my answers and comments, using both this site and Google, but found nothing.
Please advise!
You can create a hidden input and make it exempt from form security in case required, either by unlocking the field via the unlockField() method:
$this->Form->unlockField('field_name');
echo $this->Form->hidden('field_name');
or by passing false or 'skip' for the secure option:
echo $this->Form->hidden('field_name', ['secure' => false]);
echo $this->Form->hidden('field_name', [
'secure' => \Cake\View\Helper\FormHelper::SECURE_SKIP
]);
See also
Cookbook > Views > Helpers > Form > Working with SecurityComponent > unlockField()

Angular ui-grid , add custom message to editableCellTemplate

I need to add a custom error message to cell when it is in editable mode, basically i just need to add message to the editable html template as follow
<a href=\'#\' data-toggle=\'tooltip\' data-placement=\'top\' title=\'error!\' ng-show=\'!inputForm.$valid\'>errMsg - {{customMsg}}</a>
i have just bind the customMsg to the global scope just for testing purposes , but still it doesnt seems to be appearing. sample plunker added here, it would be a great help if you can tell me what i am doing wrong here
Thanks
You might want to take a look at this answer
https://stackoverflow.com/a/28127498/1585304
When you are working with the ui-grid, you are actually working in an isolated scope.
This means that you cannot access to the parent scope simply by type the name of the var into your html.
In order to access to your customMsg variable, you need to refer to the parent scope using grid.appScope.
Your column definition should look more like this.
{
field: 'company',
displayName: 'Company',
enableColumnMenu: false,
editableCellTemplate: "<div><form name=\"inputForm\">
<input type=\"INPUT_TYPE\" ng-class=\"'colt' + col.uid\"
ui-grid-editor ng-model=\"MODEL_COL_FIELD\" minlength=3
maxlength=10 required><a href=\'#\' data-toggle=\'tooltip\'
data-placement=\'top\' title=\'error!\'
ng-show=\'!inputForm.$valid\'>errMsg - {{grid.appScope.customMsg}}
</a></form></div>"
}
The only problem is that is really hard to see the message since the form is almost completely hidden by the row.
Hope this helps.

Bootstrap Datepicker with angular JS issue (again)

I am trying to create an editable Grid in which there is a dateField which is editable. The cell will be shown as editable only if Cell is focussed
$scope.gridOptions = {
data: 'result',
enableCellSelection: true,
enableRowSelection: false,
enableCellEditOnFocus: true,
multiSelect: false,
columnDefs: [
{ field: 'name', displayName: 'Name', enableCellEdit: false, width: 60 },
{ field: 'age', displayName: 'Age', enableCellEdit: false, width: 60 },
{ field: 'sex', displayName: 'Sex', enableCellEdit: false, width: 90 },
{ field: 'dob', displayName: 'DOB', width: 150,
editableCellTemplate: '<input type="text" datepicker-popup="dd/MM/yyyy" is-open="true" ng-input="COL_FIELD" ng-model="COL_FIELD" datepicker-append-to-body=true />',
}
]
};
In the editableCellTemplate, if i am not using is-open="true", then pop up is not coming. If i am not using ng-input="COL_FIELD", focus is not getting removed from any cell. So, datepicker is not getting hidden .But if i am using both then, value is not getting selected. In the console the error says Expression 'true' used with directive 'datepickerPopup' is non-assignable!
There are a lot of questions on stackoverflow for this. I tried almost all, but no sucess.
What can be done? How can i remove the error? Please provide me with a working plunker. I am not able to create this issue with the plunker.
Edit
I think, i could not remove the error. The error is still the same
Expression 'true' used with directive 'datepickerPopup' is non-assignable!
How can i remove it? After removing it, i am sure it will work. I tried taking it as a scope variable, and referencing it, but value is not getting replaces as true in when i am checking it using Inspect element. If i am using it as a variable and concatenating the value in template like 'is-open="' + $scope.dateFormat.opened + '"' , still the same error is there.
Edit 2
What is this is-open used for? Somewhere i see its value equlas to true, somewhere just opened. In one of the example over here in Stackoverflow its just opened1 and opened2. What is this? And how to use this?
I have removed the error using bcombs technique
Edit 3
After further debugging, i found that, datePicker is getting opened (if i am using the method mentioned by bcombs)but, it is getting closed as soon as it is opening.
$scope.$on('ngGridEventStartCellEdit', function () {
debugger;
});
Edit 4
From more manipulation what i concluded is
If ng-input is used, then it breaks the 2 way binding. i.e If u change the date value textfield, then in the datePicker it will be reflected, but if you will try to change the value using datePicker, it will not be reflected in textField.
If you remove ng-input, this 2 way binding, remain correct, so it works, but this causes ngGridEventEndCellEdit event NOT fired. so, the focus is not getting lost. Value is getting selected, datePicker is getting closed (if instead enableCellEditOnFocus:true, i use enableCellEdit:true), but focus remains on the textField..
Need help. Hope the update helps in solving the issue
I'm no expert on bootstrap directives or their angular implementations... But to directly answer the question of how to remove the bug:
I'm fairly certain that the is-open attribute cannot be a primitive value. It needs to be a variable that will hold the (boolean) state of whether this particular widget is open. In my code, I use a single object to hold these flags for all my datepickers, such as:
openDatePickers: {}
Then, in the cellTemplate string, I give it a unique id such as:
' is-open="openDatePickers[\'dob_{{row.entity.name}}\']" '
(note that I'm using the "name" property to match your example, but a unique ID is preferred).
Edit in response to comment:
The celltemplate has access to its controller scope, so the simplest approach is to simply declare:
$scope.openDatePickers={};
As for how this works, it depends a little on which codebase you are using.
I'm using angular-ui, and in ui-bootstrap-tpls-0.8.0.js at line 1140 you can see where the directive manages the "is-open" attribute.
If the attribute is not supplied, the directive manages this flag internally with a closure, but behavior is different When the attribute IS supplied. The way the assignment works is straightforward in both cases: the variable (whether the internal closure, or the one you supplied in the attribute) is simply toggled between states.
Edit in response to your debugging
As far as the focus thing: I don't use ng-input or enableCellEditOnFocus. I do use:
enableCellSelection: true
...and our client didn't want manual edits of the field to be allowed, so my input element has:
ng-readonly="true"
This should be a functional workaround for your issue as long as your use case doesn't require that you allow manual edits to the input field.

Setup bi-directional binding from an object defined in an html attribute in AngularJS

I have implemented a remote validation directive which queries a specified JSON API endpoint once an input is blurred. It expects the response { valid: true|false }.
I now have to extend it to allow for it to send a request involving multiple values from the parent scope.
My tag definition looks as follows:
<input remote-validate endpoint="/api/action/:value" ng-model="MyInput" />
where :value is substituted with a urlencoded value of $scope.MyInput.
This is working well.
What I require is given an endpoint like this /api/action/:value/:person/:thing, the :person and :thing substitutions are bound to the parent scope values.
My initial thought is to have a bindings attribute which maps the parent scope to the endpoint.
<input remote-validate endpoint="/api/action/:value/:person/:thing" bindings="{person: 'firstName', thing: 'thingName'}" ng-model="MyInput" />
(...)
<input ng-model="firstName" /> <input ng-model="thingName" />
given var bindings = scope.$eval(attrs.bindings); is there any way to loop through the bindings object and create a two way binding to the parent scope?
EDIT: A workaround may be to do this:
<input validate-remotely
endpoint="api/action/:value/:param1/:param2"
param1="person"
param2="thing" />
which obviously means I can only use the number of parameters I specify in the scope definition. Which is a good work around for me here. Would be nice to know if there was a way to create these bindings dynamically at compile/link time.
I can provide a fiddle but I don't have the time right now, so I'm hoping that someone will have a good idea if/how this is possible.
As I learned yesterday, you can use $parse on your 'binding' attributes. See this post. This is a good way to $watch an attribute for changes that come from parent or children
Example:
<div parse-test bindings="{person: 'firstName', thing: 'thingName'}"></div>
// in your directive link function:
scope.bindings = $parse(attrs.bindings)(scope);
scope.$watch('bindings', function(val){
for (i in scope.bindings){
scope[i] = scope.bindings[i];
}
}, true);
Experiment with this plunk
This is how I solved it in a not so ideal way.
See this plunker, which has a copy of the directive I use live and works well in my app but doesn't work in Plunker (I expect the randomly chosen plunker api endpoint to be called and fail, but it is not called due to some strange errors I don't care to debug).
The problem I had with the proposed workaround in the EDIT of this question is that it seems you cannot use scope and require in the same directive. I would love it if someone could elaborate on if this is the case and why.
The Workaround
I used a params attribute to specify additional data in object notation to be extended to go to the resource request. I used the handlebars syntax in the params attribute to dynamically change the object notation string.
<input ng-model="value2" ... />
<input validate-remotely
endpoint="/api/:value/?thing=:anotherValue"
params="{ anotherValue: '{{ value2 }}' }" ... />
Not so great :/ but it works.
Then on blur of the validated input, I re-$eval the object string (app.js:40 in the plunker) and extend the resources data to include this object, which using ngResources colon (:) notation, replaces the URL.
The validation has 3 states:
remoteValidityPending: field validation fails because it is still being checked - good for showing a spinner.
remoteValidityUnchecked: The field has changed since it has been checked but has not yet blurred - ensures that any `ng-disable="form.$invalid"' submit buttons stay disabled until we know that the backend has returned a response.
remoteValidity: If this passes, the fields endpoint has been called and '{valid: true}' has been returned from the server.
I'm almost certain there are better/different ways to solve this, and I'll be happy to change the answer if someone improves this directive. I hope this helps someone out there.

Ext ComponentQuery wildcards

I've searched the examples and docs and I couldn't find if its possible to use wildcards in the ComponentQuery query. Id like to do the following:
Ext.ComponentQuery.query('button > menu > menuitem[text="Welcome*"]');
Since the button text will be the current user name for example "Welcome Shaun".
Try
Ext.ComponentQuery.query('menuitem{text.search( \'Menu Item\' )!=-1}');
Per the docs, you can use custom expressions. This works for the code I've included below:
var toolbar = Ext.widget('toolbar', {
items : [
{
xtype:'splitbutton',
text: 'Menu Button',
iconCls: 'add16',
menu: [{text: 'Menu Item 1'},{text: 'Menu Item 2'}],
reorderable: false
},
{
xtype: 'button',
text: 'Get matches',
handler: function(){
var match = Ext.ComponentQuery.query('menuitem{text.search( \'Menu Item\' )!=-1}');
console.log( match )
}
}
]
});
Ext.widget('panel', {
renderTo: Ext.getBody(),
tbar : toolbar,
border : true,
width : 600,
height : 400
});
Just for the sake of completeness (and because Member Expressions can crash the query)
As #existdissolve stated you can archive something similar (not the same!) of a wildcard with the use of a Member Expression like in your case
Ext.ComponentQuery.query('button > menu > menuitem{text.search("Welcome")!=-1}');
This will try to perform the native JS search operation on the text property of any found menuitem. No matter if this property exist (or the method) which brings me to the main issue of Member Expressions
What you need to know about Member Expressions
You can use a Member Expressions on nearly every selector but they need to exist on that selector or you end up with a exception which aborts the query (totally, like a exception does).
Here's a simple example. Let's say you want to find a button by it's text. That should be no problem, all buttons need a text, didn't they? This works for quite a time till your customer complains that some buttons don't need text a simple image would be enough. You have forgotten about the query thing and add the button and suddenly parts of your application stop working while you end up with a exception like
Uncaught TypeError: Cannot call method 'search' of undefined
You can try it yourself in this JSFiddle example. Try 'find me' and after that hit the 'add image button' and try it again.
Why does this happen? The Ext.ComponentQuery test neither if property exist nor if the expression exist which can quickly lead to a total fail of the query.
Yes, the example above is simple and you may find the source of the error real quick but it can also be completely different.
What now? I don't want to say to say that you should not use a Member Expression but you really need to care about the side effects that you may face.
Starting with ExtJS 5 functionality was introduced that makes this task much cleaner by allowing matching via regular expressions. The documentation provides further detail, but a solution that solves the posted question would look like this:
Ext.ComponentQuery.query('button > menu > menuitem[text/="^Welcome"]');

Resources