I have a directives.js file in which i have my directives defined. I need to add one more directive for password verification.
I have gone thru lot of links. I am not able to succeed. From the below link,
password-check directive in angularjs
I am not able to understand how come 'data-password-verify' attribute refers to the definition of 'passwordVerify' directive?? where should the mapping be defined?
I've struggled with this, and it's messy. I see no help in a directive, because logically, all that's needed is to constrain one value to match another by validation. All I needed was ng-pattern and a helper filter to escape a string into a tight regular expression. (Note I'm checking emails, but the concept is identical.)
My code:
<input type="email" name="email" ng-model="data.email"
required placeholder="jane.doe#example.com">
<input type="email" name="verify" ng-model="verify"
required ng-pattern="data.email | quotepattern">
The quotepattern filter implementation is:
function(input) {
if (!! input) {
return "^" + input.replace(/(\W)/g, "\\$1") + "$";
} else {
return input;
}
}
This does a valid binding on both, and checks for validity. If the first value changes, both controls get updated. If the verify control value changes, only that control is affected.
To me, this is infinitely simpler than directive hackery, and conceptually more correct too, since logically all you need to do is check for a string match in the verify control. Engineering an entire new control with custom editing logic to do that is breeding an entire yak only to shave it.
In the following example, we say that the <input> element
matches the ngModel directive.
<input ng-model="foo">
The following also matches ngModel:
<input data-ng:model="foo">
Angular normalizes an element's tag and attribute name to
determine which elements match which directives. We typically refer to
directives by their case-sensitive {#link
http://en.wikipedia.org/wiki/CamelCase camelCase} normalized name
(e.g. ngModel). However, since HTML is case-insensitive, we refer to
directives in the DOM by lower-case forms, typically using {#link
http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
attributes on DOM elements (e.g. ng-model).
The normalization process is as follows:
Strip x- and data- from the front of the element/attributes.
Convert the :, -, or _-delimited name to camelCase.
Read the documentation
Related
I'm designing an AngularJS 1.5.x app that will have lots of forms and lots of fields per form. I'm finding that I'm repeating attributes a lot, e.g.:
<div class="form-group">
<label class="control-label" for="thing.Field1">Field 1</label>
<input class="form-control ctrl-md"
type="text"
id="thing.Field1"
name="Field1"
ng-model="thing.Field1"
ng-maxlength="30"
required
uib-tooltip="Field 1 is required"
tooltip-placement="right"
tooltip-trigger="none"
tooltip-is-open="thingForm1.Field1.$invalid && showValidationErr('Thing1')"
ng-blur="fieldBlur('Thing1')">
</div>
I've attempted to use a directive + template, and it works more or less, but it seems very complex and slow.
Is there a good way to make the input reusable? Or should I just get used to doing a lot of copy+paste in my editor?
In addition to reducing repetitive coding, it would be nice to be able to change all elements in one place, in the case that I want to change the tooltip position on all fields for example.
There is many ways. Have a look at this library http://angular-formly.com/. In my projects I use plain inputs and writing directives only in a complex cases, like 2 field controls. Or special field like card expiration date.
UPDATE
Ok, have a look at this pls. Angular: better form validation solution
You have 3 solutions:
To store your attributes as it is.
To store a group of attributes, commonly used together, in a directive. Or use directive which utilize array of properties. Like validation directive in my example.
To use directive with transclusion to utilize 1st and 2nd approach. So you can swap elements position (such as label, input control, error hint) in a single place. Plus you can easily create property like... preset on this directive and store attribute presets in it.
So, as I already told, there is many ways to reduce number of attribute repetition.
I've got a directive that renders a simple searchbox - its HTML looks as follows:
<div class="search input-group">
<input type="text"
ng-model="text"
ng-change="onChange()"
placeholder="Search here..."
class="form-control">
<span class="input-group-btn">
<button class="btn btn-default glyphicon glyphicon-search"></button>
</span>
</div>
All is well and working, I'm able to use it like this:
<searchbox ng-model="search" />
However, now I would like the searchbox to have autofocus in some cases, and in some cases not, for that, it would be neat to just be able to do:
<searchbox ng-model="search" autofocus />
and have that result in having the autofocus attribute carried over to the <input> tag within the directive. Is this possible? How would I go about doing that? Is there a way to carry over specific attributes over to a specific sub-element?
This is a way: from your directive's link function, read the autofocus attribute and, if it is defined, write it to the <input> using DOM manipulation. (DOM manipulation is OK inside the link function):
link: function(scope,elem,attrs) {
if( angular.isDefined(attrs.autofocus) ) {
var inp = elem[0].querySelectorAll('input');
inp[0].setAttribute('autofocus','autofocus');
}
}
A fiddle demonstrating the principle: http://jsfiddle.net/5yhp2xa0/
Possible catch: I am not sure if HTML's autofocus would work for templates that are inserted to the page "later" (i.e. after Angular route change, when a ng-if is shown etc). If this is the case, then a different solution should be used (could be easy, just call inp[0].focus() instead of inp[0].setAttribute('autofocus','autofocus');).
Since the title of the question is "Carry over attributes from directive to sub-element", let me address the general issue as well:
Attributes are not transferred automatically
If the attribute is non-directive, then techniques similar to the answer above can be used, i.e. manipulate the DOM from the link function. Things can get more complex if the attribute value is dynamic, but the general idea is the same.
If the attribute is a directive things are more difficult. Most probably you will have to use the compile function and manipulate the template of the DOM. In this case however, I would prefer to make the directives cooperate directly using the require configuration, especially with the optional modifier, e.g. require: '?otherAttributeDirective'. Of course this is possible only if you control both directives.
You can use the tab index. I m listing some of the behaviors of tab index as under
The tabindex value can allow for some interesting behaviors .
If given a value of "-1", the element can't be tabbed to but focus can be given to the element programmatically (using element.focus()).
If given a value of 0, the element can be focused via the keyboard and falls into the tabbing flow of the document.
Values greater than 0 create a priority level with 1 being the most important.
Or you can use following javascript code for that.
document.getElementById('txtId').focus();
I would do that programmatically. It feels like you are asking too much of angular to carry the attributes in automatically. The attributes of the directive are available as arguments to the link and compile functions, it should be easy to use the directive template to apply the attribute inside when it's on the outside.
For example, try this:
... directive code
link: function(scope, elem, attrs) {
console.log(attrs.autofocus);
}
You can check the value of autofocus from the attrs like that
I'm trying to build widgets directives that play well with forms
The complete example is available at http://jsfiddle.net/jy81bchd/4/
The main idea is:
Write your own form, put widget in it
<form name="otherForm" novalidate ng-init="model = {name: 'test'}" novalidate>
<swif-widget-text name="name" required="required" input-model="model.name">
<span translate>Name</span>
</swif-widget-text>
</form>
The widget directive copy all attributes to the input and transculde the contennt in the the label.
My differents problems are:
1) I can't update the formController of the main form, it doesn't find the different inputs throw directives. The best should be to use formcontrolller.addControl but I don't have succeed with it
2) To work around 1 I have tried to make each widget a different form. This is working execpt formcontroller doesn t update after link has been called (attributed copied to input doesn't affect the controller).
In the fiddle I copied required attribute to the input but if you empty the field it's still valid according to the formcontroller.
I have added name="input" also because if I copy name attribute to the input the form controller doesn't find any input.
Conclusion:
From what I understand formcontroller is initialized and loaded before the link is called.
How could I change that ?
There are several posts about how to create your own form component that plays nicely with the form and the controller that is attached to that.
For instance: http://blog.revolunet.com/blog/2013/11/28/create-resusable-angularjs-input-component/
Examining your fiddle I have a few things you need to look at:
1) add a name and a controller to the form element in order to work with all form elements that live inside of it. Via the $scope that is injected in that controller you can handle the form elements (also add meaningfull names to your form elements).
2) And your custom component:
- should use ng-model instead of input-model.
- should require 'ngModel' in the Directive definition
- in the link phase you get a reference to the ngModelController, and check the link above to see how you should interact with that to achieve a two way binding like behaviour. (using $viewValue, $render and $setViewValue)
I wonder if anyone can help. On the surface of it, my question title may sound kind of stupid! I'm trying to use angularjs form validation in a non-angularjs application! Let me try to explain ...
We have a 'traditional' web app. Its not an SPA. The backend is java, and the java app server manages the session and the data within. Each page is a full http request/response. (There are a small number of ajax request/responses, but these are to add some bling to the page, rather than it's core functionality). In this respect, the architecture of the app is very traditional/old-skool, in that the server-side java code is responsible for generating the markup and populating form field values from it's version of the model data held in it's session store. (I think this is the crux of the problem)
The app is predominantly a HTML form based application, and to enhance the UX we have written some javascript field validators based around jQuery. For a number of reasons these have started to get a bit out of control, and we are exploring alternative options.
A simple google search finds countless jQuery plug ins for form validation. We are looking at these, but random jQuery plug ins are not our favoured approach (we tend to steer clear of 'somebloke.com' plug ins because we can't guarantee how well they're written, browser compatibility, future maintenance, how well they work with other plug ins etc - we've had our fingers burnt with this kind of thing before)
So we are looking at other approaches, and are currently exploring the use of angularjs.
Being able to 'gently introduce' angularjs into our architecture has some advantages. It's from a stable best-of-breed organisation (ie. it's not 'somebloke.com') so is well supported and maintained. It encourages us to write our js in a very modular & testable manner (our current rats nest of jQuery plugins, callbacks etc is far from that!). angularjs form validation is based around html5 standards and is declarative and semantic. It gives us a way forward for migrating our other jQuery based code to something better (angularjs directives). And overall, if we can layer angularjs into our current app, it gives us a good foundation for converting the app to a modern SPA at some point in the future.
Rewriting the entire app as an angularjs (or any other mv* framework) SPA at this point in time is not an option, so as mentioned above, we are looking at introducing small bits of functionality at a time; and today's challenge is form validation.
So, that's the background.
I've stripped out our current js client side validation, and our server-side java code is generating markup like this:
<form method="POST" action="/renew">
<input name="firstname" type="text" value="alf" />
<input name="surname" type="text" value="garnet" />
<input name="age" type="number" value="88" />
<input type="submit" />
</form>
(where the values for the input fields have been populated server-side from the model held by the server)
I've added the angularjs library to the page, and have got form validation working as follows:
<form method="POST" action="/renew" novalidate name="renewForm"
ng-controller="yourDetails" ng-submit="submitForm(renewForm, $event)">
<input name="firstname" type="text" value="alf" required ng-model="firstname"/>
<input name="surname" type="text" value="garnet" required ng-model="surname"/>
<input name="age" type="number" value="88" required ng-model="age"/>
<input type="submit" />
</form>
app.controller('yourDetails', function($scope) {
$scope.submitForm = function(form, $event) {
if (!form.$valid) {
$event.preventDefault();
return false;
}
};
});
This is a reasonable starting point. Broadly speaking it works in that angularjs is handling the form validation and submission. The submitForm method is executed, and if the form is not valid then the if block is entered and the form submission is cancelled. From here I can see it would be easy to add in the field error messages etc using ng-show etc.
The problem however is the use of ng-model on each html field. As I understand it I need to use this so that angularjs binds the field to the form, and can therefore track each fields valid status.
However, ng-model also appears to setup the 2-way data binding and sets the value of the field to it's version of the model data ... which is empty. For example:
Our server-side template might contain this:
<input th:field="*{firstname}" type="text" required ng-model="firstname"/>
Which might generate this markup:
<input name="firstname" value="alf" type="text" required ng-model="firstname"/>
The markup that gets served to the client includes value="alf"
But then angularjs steps in and sets up 2-way binding for the field. Because we don't have a firstname property in the angularjs scope, it initialises one with a blank value, and sets that blank value in the DOM of the field.
This results in the page being rendered by the browser with blank values in the fields, even though server-side we have values in the model, and the server has correctly generated the markup etc.
So, I think I understand the core problem and why it's happening. My question is, can I do angularjs form validation without the ng-model attribute on each field, or is there a version of the ng-model directive that only does 1-way binding - specifically DOM -> model
Any help would be very much appreciated;
Thanks
Nathan
When generating your form at server side, you can initialize your model with ng-init:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
OK, #Alexandre's answer was almost right, and it was his answer that pointed me in the direction of my final solution (so he should get the credit for this really :) )
ng-init does work as #Alexandre has suggested. The reason I couldn't get it to work was that I was trying to use it on a number field.
The following works because the value being set on the model with ngInit and the html input type are both text/string:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
I was trying it on the age field as follows:
<input ng-init="age='88'" th:field="*{age}" type="number" required ng-model="age" />
This does not work because the age property was being set on the model as a string, but the html input type is a number. The following does work:
<input ng-init="age=88" th:field="*{age}" type="number" required ng-model="age" />
This led me to realise that the value being set on the angular model needs to match the data type of the html input type (certainly true of chrome, not sure about other browsers) (ie. string properties in the model - even if they are parse-able as numbers - cannot be used in a html number field with ngModel)
With this in mind, I decided there were 2 options. I could either do it server-side in the code that generates the markup:
<input ng-init="age=(some-potentially-complex-logic-to-workout-whether-its-a-string-or-number)" th:field="*{age}" type="number" required ng-model="age" />
Or I could do it client-side with a custom directive. In the end I went with a custom directive because a) it meant I could have a go at writing a directive (all part of the learning :)) and b) I realised there might be other cases that need special consideration which might make doing it server-side even more complex (ie. select fields don't have a value, they have a selected index of which you need to get it's value; radio buttons all have a value but you only want to set the value of the checked radio)
Here's what I came up with:
angularApp.directive('lvInitializeValueOnScope', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
var propertyName = attrs.name,
propertyValue = attrs.value,
elementName = element.get(0).tagName.toLowerCase(),
fieldType = ( elementName === 'input' ? attrs.type.toLowerCase() : elementName ),
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
// if the input field type is number and propertyValue is parse-able as a number
if (fieldType === 'number' && !isNaN(parseFloat(propertyValue))) {
// set expression without quotes surrounding propertyValue
expression = propertyName + '=' + propertyValue;
}
// if the field is a html select element
if (fieldType === 'select') {
// propertyValue will be blank because select elements don't have a value attribute
// instead, we need to use the value of the child option element that is selected
propertyValue = $(element.html()).filter(":selected").val();
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
}
// if the input field type is a radio button but its not checked (selected)
if (fieldType === 'radio' && !element.is(':checked')) {
// we need to reset the expression so a blank value is used
// doing this means that only the checked/selected radio button values get set on the model
expression = propertyName + '=\'\'';
}
// evaluate the expression, just as angular's ngInit does
scope.$eval(expression);
}
};
});
(it looks more complex than it actual is because I've left the comments in - strip those out and there's really nothing to it)
To use it, each field that I am using ng-model on, I also need to use the attribute data-lv-initialize-value-on-scope. You don't need to pass any value on the attribute, the directive gets everything it needs from the element and attr parameters
So far it caters with text, number, radio and select fields. The pages I'm working on don't have any checkboxes, but as and when I come across those I dare say I'll need to add some code for those.
It works, and I think I prefer this to using ng-init with a load of server-side to determine whether to set a value, what its data type should be, etc
The only downside I can see at the moment is that processing a radio button set might be inefficient. IE. if you have 10 radio buttons, each marked up with ng-model and data-lv-initialize-value-on-scope; all 10 will run the data-lv-initialize-value-on-scope directive and set a value on the scope. At least 9 of the iterations will set a blank value on the scope, and at most only 1 will set the actual value on the scope.
Hey-ho, it seems to work for now :). Hope this helps someone in the future
Nathan
From 1st view seems like data-ng-click can pass some data as argument to method should be invoked during pressing on button.
But I don't see the difference.
I have followed snippets of code:
HTML
<input
type="button"
value="Fess"
ng-click="toggle(2)">
OR
<input
type="button"
value="Fess"
data-ng-click="toggle(2)">
JS
$scope.toggle = function (valueS) {
alert(valueS);
}
Both work.
Thanks,
They are the same thing. You can use data-ng-click to make a valid html.
From the angular docs on directives:
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _. Optionally the directive can be
prefixed with x-, or data- to make it HTML validator compliant. Here
is a list of some of the possible directive names: ng:bind, ng-bind,
ng_bind, x-ng-bind and data-ng-bind.
Leaving them out is totally fine for practical purposes. It's just that if you run it through an html validator service, it will not pass as complliant.
HTML5 has an ability to embed custom data attributes on all HTML elements
These new custom data attributes consist of two parts:
Attribute Name
The data attribute name must be at least one character long and must be prefixed with 'data-'. It should not contain any uppercase letters.
Attribute Value
The attribute value can be any string.
<li data-spacing="30cm" data-sowing-time="February to March">Celery</li>
source : http://html5doctor.com/html5-custom-data-attributes/
Found a difference while using with and without Form.
When my element is in a Form they act the same.
When I use data-ng-click on an element not within a form, Click event is not happening.