validation of a form added with a directive - angularjs

I would like to access the content of the validation variables provided from AngularJS for the forms.
I need to add forms using a directive like in the code, but if I do that I can't access the $dirty,$error,$invalid variables anymore and I need to access them.
JS:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="nameinput" ng-model="data.text" placeholder="type here..." required />');
}
};
});
HTML:
<test nameform="valform" nameinput="valinput" type="text"/>
{{valform.valinput.$invalid}}

I think that you can't. Because you are using isolated scope for building your directive, so you don't have acces to the information. You can try to build you directive using share scope, and I think that you would be able to access this information.

Change your directive to be ng-form instead of form (because ng-form is nestable). Then wrap your directive inside another form element and give that new form element a name. The outer form element will bind to your outer scope and you can access the properties that way.
Directive template:
"<ng-form name="valform"></ng-form>"
HTML:
<body ng-view>
<div>Valid: {{outerform.$valid}}</div>
<div>Errors: {{outerform.$error}}</div>
<form id="outerform" name="outerform">
<test type="text"/>
</form>
</body>
Side note, form names don't play nice with dynamic names. The plunkr below uses static names so I could help you with your current problem. I found a solution to the dynamic name problem on a different SO post...I think this one...
Dynamic validation and name in a form with AngularJS
Here's a plnkr with your working form...
http://plnkr.co/edit/RFrRXp2kWkP1Mefwl3Kn?p=preview

When you build your HTML for your input controls, make sure you append the name attribute correctly based on 'nameinput' passed in as an attribute:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="' + attr.nameinput +'" ng-model="data.text" placeholder="type here..." required />');
}
};
});

Related

Dynamic add attributes on component in Angular pre compile

I have component that is used multiple times, 58 times to be exakt. The only thing that differs between them is unique attributes to add validations. What I want to do is to add a array of attributes to my template before is compiled. Is this possible to achieve when working with a Angular component?
component.js
(function () {
'use strict';
angular
.module('upaApp')
.component('component', {
bindings: {
inputAttributes: '#',
},
controller: controller,
restrict: 'E',
templateUrl: 'app/component/component.html'
});
function controller() {
var $ctrl = this;
}
})();
component.html
<input {{ $ctrl.inputAttributes }}
class="form-control"
type="text" />
When I use the component <component input-attributes="directive1, directive2"></component> it doesn't render out my string and even if it did I would not be sure that it would work. So is there a way to dynamically be able to set the attributes in AngularJS?
Is this angular 1 or 2?
Ill assume the former.
I dont know of a way to place a string as an attribute. What you could do as a workaround is conditionally insert attributes with the ng-attr- attribute. This will insert the attribute if the variable is not undefined.
maybe something like this:
$scope.ctrl.inputAttributes = {
directive1:undefined, //this one wont show
directive2:"this one will show"// as directive2="this one will show"
}
then in your markup:
<input ng-attr-directive1="ctrl.inputAttributes.directive1"
ng-attr-directive2="ctrl.inputAttributes.directive2"
class="form-control"
type="text" />
https://www.thinkingmedia.ca/2015/03/conditionally-add-a-html-element-attribute-value-in-angularjs/
EDIT: it may not be clean, but you could create a directive that compiles html.
app.directive('dynamicAttributes', function ($compile) {
return {
restrict: 'E',
scope: {
attributes: '#'
},
link: function (scope, elem, attrs) {
var h = '<input '+scope.attributes + ' class="form-control" type="text" />';
elem.replaceWith($compile(h)(scope));
}
}
});
then in your DOM
<dynamic-attributes attributes="1 2 3"></dynamic-attributes>
fiddle: http://jsfiddle.net/brhardwick/nx16zdrL/1/
There was actually a very simple solution on my problem. You could use ng-model to send the value to the component. And when I placed my directives on the component it validates accordingly since it can access the value from ng-model.

Use ng-model with a primitive value inside ng-transclude

In a legacy project, I want to create a new directive that uses transclude.
A trimmed down version of the directive code is:
app.directive('controlWrap', function() {
return {
restrict: 'E',
transclude: true,
scope: { label: "#" },
templateUrl: "control-wrap-template.html"
}
})
And the template is:
<div>
<label>{{label}}</label>
<div>
<ng-transclude></ng-transclude>
</div>
</div>
This directive is used like this
<control-wrap label="Just a example">
<input type="text" ng-model="input" />
</control-wrap>
Test: {{input}}
I know that the workaround is to use a object in the scope instead of primitive value (ng-model inside ng-transclude). But that is no option for me. It is a ugly, poorly coded, legacy code that relies in those attributes directly on the scope.
Is there a something I can do in the directive to make that html works without change?
You can manually transclude (instead of using ng-transclude) and apply whatever scope (which is, in your case, scope.$parent) you need to the transcluded content:
transclude: true,
scope: { label: "#" },
template: '<div>\
<label>{{label}}</label>\
<placeholder></placeholder>\
</div>',
link: function(scope, element, attrs, ctrls, transclude){
transclude(scope.$parent, function(clone){
element.find("placeholder").replaceWith(clone);
});
}
Demo
The cleanest solution is to do some refactoring and passing an object instead of a primitive value, but if for some reason you cannot do that, you're not out of the options.
However, I wouldn't recommend any of these options
1) Bind input from the parent scope, that prevents creating a new value on the child scope upon write - butt keep in mind that accessing the parent scope hurts reusability of your directive.
Angular 1.2:
<input type="text" ng-model="$parent.input" />
Angular 1.3:
<input type="text" ng-model="$parent.$parent.input" />
(The difference is because the parent of the transcluded scope is the directive scope from 1.3)
2) Create some kind of wrapper object and pass that instead of the primitive value
$scope.inputWrapper = {};
Object.defineProperty($scope.inputWrapper, 'input', {
get: function() { return $scope.input },
set: function(newValue) { $scope.input = newValue; }
})
and pass this to the directive. But again, I would do some refactoring instead.

Angular - validation messages for a form input directive with isolated scope

I think I am slowly going insane. What seems like a simple problem is giving me a headache :(
I have a form with a custom input element directive using isolate scope.
I simply want to able to display a error message based on the validity of the input elements "required" attribute but I seem to be going round in circles. I am not quite understanding the binding in this scenario.
Please take a look at my fiddle here:
http://jsfiddle.net/brogueady/zwbbLggL/
I would expect the error message "Invalid" to appear to the right of the input field because it is empty.
The HTML is
<div ng-app="UIComponents">
<form ng-submit="formSubmit()" name="vrmForm" >
<at-input name="registration" label="Registration" form="vrmForm" model="vrmLookup.registration" minlength="3" required>
</at-input>
</form>
</div>
The JS is
uiComponents.directive('atInput', function () {
return {
// use an inline template for increased
template: '<div>{{label}}</div><input name="{{name}}" required type="text" ng-model="model"/> <span class="error" ng-show="form.{{name}}.$error.required">Invalid</span>',
// restrict directive matching to elements
restrict: 'E',
scope: {
name: '#',
form: '=',
model: '=',
label: '#'
},
compile: function(element, attr) {
var input = element.find('input');
if (!_.isUndefined(attr.required)) {
input.attr("required", "true");
}
}
};
});
Thank you.
Your $scope.form.name property cannot be an angular binding expression. Return a template function instead from your directive and build the template string:
template: function($element, $attr) {
return '<div>{{label}}</div><input name="' + $attr.name + '" required type="text" ng-model="model"/> <span class="error" ng-show="form.' + $attr.name + '.$error.required">Invalid</span>';
},
Demo

Declaring and using angular directives that map id, name, and ng-model to a single field

I'd like to create/use an AngularJS directive to avoid creating some redundant HTML and also to simplify how I use Bootstrap elements. I'd like to use a directive this way:
<myDirective id="person.lname" label="Last Name"></myDirective>
The template I'd like AngularJS to write is:
<label for="person.lname">Last Name</label>
<input id="person.lname" name="person.lname" ng-model="person.lname">
How would I declare a directive to be able to create this template and have the binding with ng-model still work? Are there any reasons why this would not be a good idea?
Thanks.
UPDATED
I added the label tag to reflect how the id/name would be used for the input element. The generated template would allow you to click on the label and have the focus be placed on the input element.
This should do what you want:
.directive('myDirective', function() {
return {
scope: {
model: '=identifier',
id: '#identifier',
label: '#',
},
restrict: 'E',
template: '<label for="{{id}}">{{label}}</label><input id="{{id}}" name="{{id}}" ng-model="model">'
};
})
View:
<my-directive label='Last name' identifier="person.lname"></my-directive>
Fiddle

Angular.js setting the value of ng-show by parameter inside a directive

Sorry if the title isn't clear, here is what I'm trying to do:
I have multiple signup forms and every one of them has a password field. Now, I want to set some requirements to the passwords, ie. I want to get a password that is longer than 5.
I have:
<form name="myForm">
<!-- some elements -->
<input type="password" required ng-model="user.password" name="password" ng-minlength="5">
and right after that:
<div ng-show="myForm.password.$error.minlength">
Password is too short.
</div>
<!-- some other elements -->
</form>
I thought I would refactor this error message into a directive, the only problem is that I can't seem to correctly pass the form's name to the directive.
The directive looks like this:
myApp.directive('passwordLengthError', [function () {
return {
restrict: 'E',
replace: true,
template:'<div ng-show="{{form}}.password.$error.minlength">Password is too short.</div>',
scope: {
form: '#'
}
};
}]);
and I call it like this:
<div>
<password-length-error form="myForm"/>
</div>
If I check in Chrome's web inspector, I see that the parameter is there, I see
<div ng-show="myForm.password.$error.minlength">
however, it doesn't actually work, I don't see the message pop up if the password is shorter than 5 characters.
Is there a way to make this work, or is this not possible? Thanks in advance.
The # in your isolate scope is trying to evaluate an angular expression. You are just passing a string, so you can just set the scope variable directly to the attribute value in your directive, without any isolate scope or evaluation of the attribute.
So:
scope.form = attrs.form;
And the entire directive would be:
app.directive('passwordLengthError', [function () {
return {
restrict: 'E',
replace: true,
template:'<div ng-show="{{form}}.password.$error.minlength">Password is too short.</div>',
link: function(scope, element, attrs){
scope.form = attrs.form // the attribute is a string, so, YAY
}
};
}]);
YOUR DEMO

Resources