I have an issue with angularjs and the email input type.
I want to create dynamic inputs with a directive, but the input type validation might be buggy.
Here is the jsfiddle of my test
http://jsfiddle.net/NPCHr
To avoid some trouble I have to use this trick
element.find('input')[0].type = input.type;
When I had a second character in the input the model field disappears (In the html panel)
I don't know why is this a bug or am I doing something wrong ?
The problem with your directive is not that e-mail validation doesn't work but the fact that dynamic type attribute is not supported by AngularJS (and jQuery BTW). This is due to the fact that IE doesn't allow changing input's type on-the-fly (Changing the <input> type in IE with JavaScript).
This topic was discussed in great details on the AngularJS mailing list, here is the reference: https://groups.google.com/forum/#!topic/angular/Itl-fYzeF18 where someone had exactly the same problem as yours.
The way out of this situation is to manually compile a template using the $compile service. Unfortunately this is not trivial, you can see evidence of some experiments here: https://github.com/angular-ui/angular-ui/pull/191
An alternative, simpler approach is to use ngInclude directive to include different inputs based on their type. Yet another possibility is to use the compile function and manually transform template's markup. But yes, all of those techniques require several lines of non-trivial code.
Related
I'm editing an existing code that has a lot of angular js expressions which are being detected as unsafe by our automated testing system. I was able to see the article below that describes my case, but I was not able to get any specific way to solve it (I'm mostly seeing $watch and $apply). I guess what I need to know here is where do I make changes on the code?
Related links:
http://blog.angularjs.org/2016/09/angular-16-expression-sandbox-removal.html
https://docs.angularjs.org/guide/security#angularjs-templates-and-expressions
Sample snippets on my code:
Your code looks perfectly fine. I think what you're missing is the "passing user provided content" portion of that warning.
In the first example the only thing you are passing to $apply is a function that YOU have defined, same as the second example. In the last example you don't pass anything to $apply.
The reason they have these warnings is because $apply can be passed a string to evaluate an expression on $scope.
In the same way that
{{$scope.hello = 'Hello, World'}}
will set the hello property of $scope
$scope.$apply('hello = "Hello, World"')
Will do exactly the same. now imagine you pass user defined content to this
$scope.$apply(userPassedString)
Now you have given a user the ability to run arbitrary javascript expressions in your apply function.
To see exactly what I mean by this (and how this is exploitable), I have created a codepen demo for you here: https://codepen.io/codymikol/pen/bGbzbvp
(You'll have to scroll down in the HTML to see the script, I was lazy and din't link it as a separate JS file \_('__')_/
Also if you REALLY want to understand how the above snippet is able to function (and where I learned about getting the function constructor in such a way) you should watch this video by liveoverflow: https://www.youtube.com/watch?v=DkL3jaI1cj0
This was made back when the AngularJS team was trying to create a sandbox around scope expressions to prevent XSS. There are a bunch of videos detailing different exploits people used to get around the sandbox. Because of how complicated creating a sandbox is and how often it was being exploited they decided to remove it entirely and just warn developers about passing user content in such a way.
I'm part of a team with about 6 UI devs, of varying quality and next to no Angular experience. Many are contractors, with little experience with the code base. The app has a very fancy (complicated) UI. It supports IE8+ (soon hopefully IE9+).
We're introducing Angular for a major extension to the app, and I've been asked to write guidelines on the use of Angular for the team.
We'll use directives to create fancy UI elements, all prefixed with "ipwr" to avoid name clashes. I'm trying to decide whether to mandate that devs give their directives the restriction "element" or "attribute". Mandating only one, to avoid chaos and confusion.
My question is: what restrict is better or more popular for directives, "element" or "attribute"?
My main concern is ease of use for people with little Angular experience who are new to the application code base, to reduce bugs, copy and paste behaviour, etc.
The angular guidance says that you should use the "element" restriction whenever the directive has full control over it's template meaning it has a template that it is rendering out, etc.
For attributes, they suggest to use these only when you are adding "behavior" to an existing element or decorating an existing element.
For example, think of the ng-click directive, this is used a attribute not as a element because the click directive is just adding the click behavior to some element.
Another example would be the ng-repeat directive, it is also used as an attribute not as a element because it is going to repeat the element in which it is being used in.
Now, this guidance is from the angular documentation; however, I don't know necessarily that element vs. attribute is going to give you a "better" approach it's more of a convention.
Now if you have to support older browsers, then you may want to consider using either the comment or class directives.
My personal preference is to just use the attribute restriction; mainly because people that are new to angular get overwhelmed at first when they see the restrict and it's variations of the options that can be used.
I usually defer to the John Papa AngularJS style guide when making these types of decisions. He says:
Lean towards implementing as an element when its standalone and as an
attribute when it enhances its existing DOM element.
If you want to keep your HTML valid you'd use attributes, e.g. if you have a directive ipwr-modal, you can declare it as <div data-ipwr-modal="you-could-put-some-binding-here"></div>.
When creating directives with a custom layout, however, you'd better use element declaration (if you don't need to have your HTML valid). This is the more obvious way to say: "hey, we have a custom component here".
This blog post explains it with some more ideas
Some points to consider:
Most of the time attributes is the best/most convenient option (it's not the default by chance).
Anything you can do with element-bound directives, you can do with attribute-bound as well.
Element-bound directives can be more descriptive/readable at times.
If you want your code to pass certain types of validation, you should use attributes.
Since you want to support IE8, keep in mind that custom tags have an extra overhead (more info), which hurts maintainability.
BTW, you can't limit directives to elements only (without affecting functionality), so it is more a question of allowing 'element' or not. Note that an element often has more than one directives and directives placed on the same element can cooperate and augment each other's behaviour. So limiting the use of directives to 'element', means limiting the number of custom directives per element to 1, which severly reduces the functionality-potential.
That said, this is what I ('d) do:
If IE8 is not an issue, allow both (mostly use attributes).
If IE8 (or the overhead for custom tags) is an issue, use only attributes.
In general, if only one form should be allowed, it should be attributes (works anywhere, no extra overhead, offers all functionality).
Got a webapp I'm building in Angular.
This app walks a user to authorizing accounts, presenting specific instructions based on the users choices.
I've implemented this as HTML that is shown or hidden based on values in the model, so for 3 different choices, I have 3 different sets of HTML sections that are similar but with different texts.
In the spirit of DRY, I should instead have one set of HTML sections, and instead switch the text based on the values of the model. This means putting text data inside the model, including small snippets of markup, like anchor and strong tags.
Does putting presentation data into the controller violate the principals of Angular?
There are quite a number of options to avoid repeating code depending on what you are looking to do. The following ideas are things I would consider and use when they make sense (I placed these from simple to complex, so you probably can skip the first few):
ng-bind -- Put it on a span/div. Simple & works to bind the model to the display
ng-switch, ng-if, ng-hide, ng-show -- Work to conditionally show an element
custom directive -- use this when you want to alter the behavior of an element or if you want to alter the dom based on a template. If you use "ng-transclude" the contents of the element you template will be included in the result. This can be very elegant but it works best when you have a single format. I can provide examples but angular's documentation also has excellent examples.
service -- I generally use this just to provide data only. This could be via a restful api and $resource or via $http calls. Either way, I wouldn't recommend doing much more than load/save data here.
$scope method -- In other words:
$scope.myMethod = function(x,y,z) { /* code making decisions based on the model */ }
Then you can call this method from one of the previous either via a prebuilt directive (ng-show, etc) or via a custom directive that manipulates the dom for how you expect it to be.
ng-bind-html -- Last option I know to suggest is to use this directive combined with the $sce service to bind whatever you want to the DOM. If you are binding something with angular code in it - make sure to use the $compile service as well. I generally don't favor this approach except as a last resort because it makes it harder to find where elements in the DOM are coming from and it can make debugging + testing a real pain. That said, these tools wouldn't exist if people didn't need them.
I'm sure that this isn't complete and maybe others have suggestions but that is where I would start. Best of luck!
I would put the text data in a separate angular service. This article gives an example: http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Then if you decided at some point to move it to some other storage, your service would still be the single access point for the rest of the app.
I've created a directive that wraps a text input field. One of the things I plan to embed in this directive is the validation behavior when it is required, but I'm stuck on one point. You are supposed to be able to refer to the validation errors for an input field using myForm.myField.$error or myForm[myField].$error. However, because my input is created by my directive, it shows up as myForm["{{myDirectiveName"].$error. This is unacceptable because I will have many such fields and I need to distinguish between them.
Plunkr that illustrates this.
The key line that causes problems is this:
console.log( !! form["{{htTextField}}"].$error.required);
What I expect to be able to write is:
console.log( !! form[attrs.htTextField].$error.required);
Many thanks for any help.
I ended up solving this by implementing my own required directive, borrowed from angular's, and customized to modify my own scope variables. Perhaps what I observed in my question is a bug, but I'm not expert enough to fix it in angular's code.
I am working on a new project, which includes several standard forms(Login, Registration, etc).
I have a basic client side validation with ng-required, type, and etc.
The problem is that I might get other kinds of errors from the Django REST backend, such as length, unique constraint and others, and those rules might change quite frequently.
The django REST server returns the errors in a JSON string, in shich the key is the field, and the value is the description of the error.
Is there anyway to write something in angular that will automatically append an error next to the invalid element, as a modular unit, that can be reused, so I won't have to add an error container and ng-bind for it per each field in my form?.
Thanks for any assistance.
When you want to write reusable code like this, your best choice is to use directives. You can create a directive named <email></email> and then inside the template, populate it with the input element and display the {{error}} next to it. There are several ways of getting the error into the directive template, but I'd suggest isolate scope and pass the data into the directive. This helps make directives more reusable.
If you've done things correctly then your backend 'Django REST' shouldn't have anything to do with this front-end functionality for the directive. All you need to do is change the data inside the controller and it will automatically change the data in the directive. So it gives a good level of abstraction as well.