Angular - Form validation issues when using form input directive - angularjs

I have been trying to build a form input directive which will generate a form input based on the model and the model attributes .
For example,
if the field is name and type is text, the directive will return a input html control,
if the field is a list, then it will return a select box
and so on
These inputs are generated using ng-repeat in the view. The inputs are bound to the model in the scope. This is working fine. However, the form validation fails; i.e if the input controls are invalid, the main form still shows the form is valid.
I have put up a simple plunkr to illustrate the issue - http://plnkr.co/edit/R3NTJK?p=preview
NOTE : I have actually nested the form, as the input name field is also dynamically generated from the scope model.
I have been trying to a get hold on this from the past 2 days and this is really driving me nuts.
I m not sure if I m missing something.
I would really appreciate if some one could help me out with this.

Update:
Use the following link function:
link: function linkFn(scope,elem,attr){
var jqLiteWrappedElement =
angular.element('<input type="url" name="socialUrl" ng-model="social.url">');
elem.replaceWith(jqLiteWrappedElement);
$compile(jqLiteWrappedElement)(scope);
}
Plunker.
For reasons I don't understand, the replaceWith() must be executed before the call to $compile. If someone can explain why this is so, we'd appreciate it!
Update2: in the comments below, Artem mentioned that the DOM must be modified before the linking function is called, so this also works:
var myElem = angular.element('some html');
var linkFn = $compile(myElem);
element.replaceWith(myElem);
linkFn(scope);
Original answer:
Instead of the link function, just use a template in your directive:
template: '<input type="url" name="socialUrl" ng-model="social.url">'

Related

Is $dirty, $pristine in AngularJS be used in forms only?

I am new to AngularJs and I know that I can use $dirty , $pristine, $ error in form validation. However is it necessary to explicitly create a form in
<form>..</form>
to use these properties ?
Hi since you are new to Angualar js nothing is better to start from the maker itself. As Sathish said AngularJsDocumentation is very good.
To answer your question is Yes you have to create a form explicitly because these are the properties of the form.
To understand the form better you can go to this link http://mrbool.com/the-concepts-of-angularjs-forms/29117
is it necessary to explicitly create a form
The answer to that is no.
While using a form tag is very helpful for creating sets of input tags to process as a whole (usually by sending to a server) it is not necessary for angular's purposes.
You can easily use the ng-form directive as an attribute on other tag types (not just form).
From the source code you can see that if you are using the form tag or ng-form as Element, Attribute or Class you will get the same behavior
restrict: isNgForm ? 'EAC' : 'E'
link

AngularJS typeahead select on blur

I'm using typeahead through in my AngularJS project and I would like to have it select the entry if I type the full value and click out of the field.
I've put together an example of what I mean
http://plnkr.co/edit/NI4DZSXofZWdQvz0Y0z0?p=preview
<input class='typeahead' type="text" sf-typeahead options="exampleOptions" datasets="numbersDataset" ng-model="selectedNumber">
If I type in 'two' and click on 'two' from the drop down then I get the full object {id: 2, name: 'two'}. This is good, if however I type 'two' and click to the next field without selecting is there a way to accept the top of the list on loss of focus on a text field?
I'm not sure if I'd want to have that sort of functionality in my app. The user hasn't actually selected anything. So selecting something for them would introduce frustrations.
But I do understand that often odd requirements are needed. In this case, I'd attack it using ngBlur. Assign a function to be called on blur. You can grab the contents of ng-model and then loop through your data (assuming static & not being sent via server) to find a match.
You can most likely just look at the source code of your typeahead directive and strip out the part does the comparison and then choose the first item in the array.
Unfortunately the underlying component does not emit any events for this condition. This will make the solution more complex. However when the value is being entered and the Typehead magic has happened you can supplement those events and catch them to update your ngModel.
I have created a plnkr based on your plnkr and although have not cleaned up but it is a working plnkr doing by far what you need.
The gist of this is following code however you can put this code wherever best suited
The explanation below:
//Crux - this gets you the Typeahead object
var typeahead = element.data('ttTypeahead');
//This gets you the first
var datum = typeahead.dropdown.getDatumForTopSuggestion();
if (datum){
//you can do lot of things here however
//..I tried to - fill in the functionality best suited to be provided by Typeahead
//for your use case. In future if Typeahead gets this
//..feature you could remove this code
typeahead.eventBus.trigger("hasselections", datum.raw, datum.datasetName);
}
In the above code you can also save the datum somewhere in the scope for doing whatever you like with it later. This is essentially your object {num: 'Six'} Then you may also use ngBlur to set it somewhere (however the plnkr I created doe snot need these gimmicks.)
Then further down - ngModel's value is set as below
element.bind('typeahead:hasselections', function(object, suggestion, dataset) {
$timeout(function(){
ngModel.$setViewValue(suggestion);
}, 1);
//scope.$emit('typeahead:hasselections', suggestion, dataset);
});
I'm with EnigmaRM in that ngBlur seems to be the way to do what you want. However, I agree with the others that this could be somewhat strange for the end users. My implementation is below (and in plnkr). Note that I trigger on ngBlur, but only apply the model if and only if there is only one match from Bloodhound and the match is exact. I think this is probably the best of both worlds, and hope it should give you enough to go on.
$scope.validateValue = function() {
typedValue = $scope.selectedNumber;
if(typedValue.num !== undefined && typedValue.num !== null)
{
return;
}
numbers.get(typedValue, function(suggestions) {
if(suggestions.length == 1 && suggestions[0].num === typedValue) {
$scope.selectedNumber = suggestions[0];
}
});
};

how can I bind an input to a property deep within a complicated data structure?

I have an angular-rails resource with a property that consists of irregular data that is potentially quite complicated-- something like:
{ foo: [ { bar: 'baz', lol: [ { 'omg': ... etc
I built a directive which takes this data and drills down into it, dynamically rendering form fields for each object... I've got the data displaying perfectly, however the piece of the puzzle that's missing is, how can I take advantage of Angular's binding so that changing the value on the form input will actually update that attribute in the model?
Originally I was thinking this should be simple, as my code drills through the data structure, it can just be maintaining a path, so I'd end up with something like: 'myObject.foo.bar'
Then I could just pass that to the form input's ng-model attribute...... however, I could not get angular to recognize ng-model="path" where $scope.path = "myObject.foo.bar"... ng-model="{{path}}" did not work either.
My directive is using angular.forEach to drill down into this datastructure, and someone had mentioned to me that I should perhaps be using ng-repeat instead, but I wasn't sure if this is the correct way to go or not? I still feel like there should just be a way to do ng-model="path" and have that work...
Any guidance would be greatly appreciated.
To use dynamic property names, use array notation. I.e. myObject["foo"]["bar"]. Plunkr: http://plnkr.co/edit/W60F75?p=preview
Can you try setting an property on the scope with the value of the object itself and then refer it in the form element? Like below:
// In your script
$scope.obj = myObject;
// In your form
<input ng-model="obj.foo.bar" type="text" />

Angular - on invalid field, fire method?

When my field becomes invalid, is there a way to fire a method in my js?
So for example, the user fails to fill out the name field, clicks submit, I want to:
console.log('they forgot');
Thanks
There is a $error object made available by AngularJS. This object contains all of the validations on a particular form and tells if they are valid or invalid.
To get access to this property, we can use the following syntax:
formName.inputfieldName.$error
Here is a jsfiddle sample
This document is a good reference for understanding form-validations using AngularJS.
Also, for dealing with custom validations, you can add your own directives. A sample of making such directives is given in the link above.

Form validation with AngularJS and dynamic fields

I'm stuck with strange problem regarding AngularJS form validation. If a dynamically added control (a textbox, for instance) requires validation and is removed from the form, the form will remain invalid if the removed control was invalid.
That last sentence is a bit confusing. See it in action with this plnkr preview (or see the plnkr editor).
I've checked the FormController API. Based on the documentation, there's no method to provoke any kind of form validation status refresh, although the AngularJS source code defines methods like $removeControl() and $setValidity() in the FormController.
Is there a standard way to circumvent the validation issue?
I came across this before, see this answer for more detail: https://stackoverflow.com/a/15192773/317180
Apparently this is an active bug.
A workaround is to provide a hidden counter that is incremented when elements are changed, this forces the form to revalidate:
In template:
<input type="hidden" ng-bind="abc" />
In controller:
$scope.remove = function(position) {
$scope.items.splice(position, 1);
$scope.abc += 1;
}

Resources