Validate dynamically generated inputs inside a div - javascript

I'm trying to validate dynamically generated inputs, but I do not know how to do this.
When I add the div that triggers the directive and inserts the inputs dynamically the div adds the 'has-error' class but does not apply the input style, anyone knows the best way to do this that I'm trying to do?
Here is the markup:
<div ng-if="conditionItem.field.id"
ng-class="{true: 'has-error'}[conditionItem.field.hasError]"
dynamic
input-router
source="conditionItem.field"
ng-click="FieldConditionsCtrl.valueTest(conditionItem.field.hasError)"
ng-required="true"
ng-model="conditionItem.situation[$index]">
</div>
Here is the directive how generate the inputs:
(function() {
'use strict';
angular
.module('applicationInputs', ['rzModule', 'focus-if', 'ui.utils.masks'])
.directive('inputRouter', inputRouter);
/** #ngInject */
function inputRouter($compile){
return {
restrict: 'EA',
scope: {
ngModel: '=',
source: '=',
placeholder: '#',
tabIndex: '='
},
link: function(scope, element, attrs) {
var canvas = angular.element(element[0]);
scope.source.editable = angular.isUndefined(scope.source.editable) ? true : scope.source.editable === true;
//used by setting to override selected field
if (angular.isDefined(attrs.dynamic)) {
scope.$watch('source', function () {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.children().detach();
canvas.append($compile(html)(scope));
});
} else {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.append($compile(html)(scope));
}
}
}
}
})();
Here is my style:
.has-error {
border-color: red
}

Try border: 1px solid red; instead of just setting border-color. Border width is 0 by default, so just setting a color isn't enough
Just a couple nitpicky-subjective style comments also:
element is already a jqLite/jquery element, so no need to call angular.element
the scope.source.editable === true assignment can be shortened to !!scope.source.editable if you really want it to be a boolean.
While clever, this sort of jquery style element building is generally a code smell in angular js. If you really want to go this route, I would build self-contained directives for your inputs and use inputRouter's template to choose. It's easier to understand, so your future self will thank you
{true: 'has-error'}[conditionItem.field.hasError] took me a minute. Just write it as {conditionItem.field.hasError: 'has-error'}
I'm a big fan of these style guides for AngularJS:
John Papa - ES5, but good advice
Todd Motto - ES6+
There's overlap, but take what you like from each.

Related

angular directive: switch between two templates dynamically

I am trying to create a directive named availableTo that can switch between two different templates depending on some message. For example, if the field is an input with the ng-model directive, I would first need to change it to read-only using the <span> tag. So far, my code can switch the view to read-only, but I cannot seem to switch it back to input:
var directive = {
restrict: 'A',
require: '?ngModel',
link: linkerFn,
replace: true
};
function linkerFn(scope, element, attrs, ngModelCtrl) {
var clonedElement = angular.copy(element);
var preOuterHTML = clonedElement[0].outerHTML; //this can save the <input> field html code
scope.$on('mode_changed', function() {
var curUserRole = userservices.getUserRole();
if (attrs.availableTo == curUserRole) {
var e = $compile(preOuterHTML)(scope);
element.replaceWith(e);
} else {
var template = '<span>' + ngModelCtrl.$viewValue + '</span>';
var e = $compile(template)(scope);
element.replaceWith(e);
}
}); //scope.$on
} //linkerFn
For an input field:
<input name="test1" class="form-control" ng-model="name" placeholder="Name 1" available-to="ADMIN"/>
I also noticed that once I change the template in the else block above, the element re-renders, and the preOuterHTML does not contain the original element html any more. This seems to be mission impossible to me, but I would like to hear some expert opinions. Thanks
element.replaceWith(e); Don't do that. In Angular, if you find yourself attempting to modify the DOM directly, you are by definition doing it wrong. You gotta sit back and let Angular do the work.
If you need to replace a directive's entire template, a fairly straightforward approach is to use ng-include with a scope variable containing the desired conditional templateUrl, e.g.
var directive = {
// ...
template: '<div ng-include="myTemplateUrl"></div>',
link: function(scope, el) {
if (/* whatever */) {
scope.myTemplateUrl="templates/foo.html";
} else {
//...etc
}
},
};
(This does add an extra DOM node to the tree, but that's generally harmless.)
It sounds like in your case you may not need to go that far, though; a simple ng-if inside your template is probably enough to swap between your read-only <span> and <input>.

angularJS how to change attrs in directive's link

I set a progress in my app
I want to controll The progress in angular's directive
but how can I change data-value and data-total in directive's link func?
app.html
<div class="ui indicating small progress" data-value="39" data-total="50" plan-progress>
<div class="bar">
<div class="progress"></div>
</div>
</div>
In this html, I want change data-value and data-total
I try this:
app.js
todoApp.directive('planProgress', function() {
return {
link: function(scope, elem, attrs) {
attrs.value = 10
attrs.total = 20
elem.progress();
}
};
});
But it doesn't work
so I want to know how to change it in my directive?
Use attrs.$set() in your link function and recompile the element. Also, don't forget to inject the $compile service to your directive.
In your html you've added the directive as an attribute but didn't mention it in the restrict value in your directive definition. You need to mention it in directive definition.
See the code bellow:
todoApp.directive('planProgress', function($compile) {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
attrs.$set('value', 10);
attrs.$set('total', 20);
$compile(elem)(scope);
}
};
});
Simply use :
attrs["data-value"] = 10;
attrs["data-total"] = 20;
You don't want to use attrs.data-total = 20 because the - will force a subtraction.
It's always legal in javascript to use x[keyName] instead of x.keyName, and you must use this second notation when keyName is a strange key such as **$^ùjsls* or data-value. A more useful case is when the key is a variable.
On last thing : as you do, you will always rewrite the coder's inputs. It may have sense, but it's not very elegant.

Inline tags in AngularJS

So I'm doing something like this:
{{someFlag ? "<b>Bold Text</b>" : "<i>Italic Text</i>"}}
But as everyone knows, things don't always go that smoothly. When I included a "tag" in the inline code, AngularJS seems to completely ignored the whole thing and rendered the source code.
I tried
"\<b>.....
and
"<b>...
but they both didn't work. Any idea?
As posted in the comments, you have a few options, from worse to better imho :
First is to use ngBindHtml
<div ng-bind-html="italicOrBold('With ngBindHtml', someFlag)"></div>
$scope.italicOrBold = function(text, bold){
return $sce.trustAsHtml(bold ? '<b>Test</b>' : '<i>Test</i>');
}
Second is to use ngClass, which is not a too bad design
<div ng-class="{'text-bold' : someFlag, 'text-italic' : !someFlag}">With ngClass</div>
.text-bold{
font-weight:bold;
}
.text-italic{
font-style:italic;
}
And third and better, make a directive
<div bold-me-up="someFlag">Or even better with a directive</div>
.directive('boldMeUp', function(){
return {
template: '<div ng-class="{\'text-bold\' : boldMeUp, \'text-italic\' : !boldMeUp}" ng-transclude></div>',
restrict: 'A',
replace: true,
transclude: true,
scope: {
boldMeUp: '='
},
link: function postLink(scope, element, attrs) {
}
};
})
Plunker demo
And to answer your comment, I don't think there's a way to create tag with mustache syntax it's just not the way it has been designed, expressions (the thing between curly braces) are basically calls to controller and controllers shouldn't be used to manipulate DOM.

AngularJS - inject string from model to HTML's tag name

is it possible to inject string into html's tag name with angular?
Something like this:
<div ng-repeat="type in types">
<bettype-{{type.id}}></bettype-{{type.id}}>
</div>
The output I need is:
<bettype-1></bettype-1>
<bettype-2></bettype-2>
I am also using polymer (this way I am creating the custom html tags).
I think the best solution would be to create a directive which creates custom elements, something like:
.directive('bettype', function($compile) {
return {
restrict: 'E',
compile: function($element, $attr) {
return function($scope, $element, $attr) {
// Create new element here with $attr.number
var number = $attr.number,
element = angular.element('<bettype-'+number+'></bettype-'+number+'>');
// Replace newly created element
$element.replaceWith(element);
$compile($element)($scope);
}
}
}
});
Not sure if that will work, but probably that's the way to go...
Note: I don't think it\s a good idea to have dashed separated elements like bettype-1..

How to manipulate styles of directive in AngularJS?

I'm writing a component using AngularJS and AngularJS directives.
I'm doing something like this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return { /* Some logic here*/ }
});
I want to be able to change style of my component (using CSS), something like this:
<my-tag class="MyClass"></my-tag>
Besides this I want to be able to manipulate all elements style inside my
component (HTML markup inside of my-tag).
Do you have any advice or useful examples how to manipulate the style properties of custom tags using AngularJS?
This should do the trick.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attributes){
element.addClass('MyClass');
}
}
});
This is how AngularJS adds core CSS styles:
angular.element(document).find('head').prepend('<style type="text/css">#charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
You can find this code in angular.js v1.2.0-rc.2.
EDIT
In a custom directive, I use this solution to bundle CSS stylesheets in the directive:
var outputColorCSS = {
selector: 'span.ouput-color',
rules: [
'display: inline-block',
'height: 1em',
'width: 5em',
'background: transparent',
'border: 3px solid black',
'text-align: center',
'font-weight: bold',
'font-size: 0.8em'
]
};
var outputColorStyleSheet = outputColorCSS.selector + outputColorCSS.rules.join(';');
angular.element(document).find('head').prepend('<style type="text/css">' + outputColorStyleSheet + '</style>');
Then you can use class="ouput-color" in your directive templates.
I found it very clean and useful.
I'm a little late to the party, but why aren't you all using the built in .css() method?
just use:
link: function(scope, elem, attr, ctrl)
{
elem.css({'display': 'block', 'height': '100%', 'width': '100%'});
}
or whatever css you desire.
You can put custom styles in a directive's declaration with a parameter, just like you exemplified.
In order to declare a style like that, you have to define a variable to hold the custom styles:
scope: {
myClass: '#myClass'
},
And then set that parameter in the directive's template, like this:
<my-tag my-class="CustomClass"></my-tag>
Finally, in the template of the directive itself, reference that class:
<h1 class="{{myClass}}">{{myContent}}</h1>
I made a plunker that shows how you can customize styles in a directive, check it out here .
Plunker
To manipulate the css style through an attribute directive, you could do something like this:
var app = angular.module('colorSwap', []);
app.directive('styleChanger', function() {
return {
'scope': false,
'link': function(scope, element, attrs) {
var someFunc = function(data)
{
/* does some logic */
return 'background-color:' + data;
}
var newStyle = attrs.styleChanger;
scope.$watch(newStyle, function (style) {
if (!style) {
return ;
}
attrs.$set('style', someFunc(style));
});
}
};
});
Some html:
<div ng-app="colorSwap">
<input type="txt" ng-init="colorName= 'yellow'" ng-model="colorName" />
<div style-changer="colorName">this is the div content</div>
</div>
To make an element directive, change it's own style, something like this:
app.directive('elementWithStyle', function() {
return {
'restrict' : 'E',
'scope': {},
'controller': function($scope) {
$scope.someStyle = 'Cyan';
$scope.someFunc = function() { $scope.someStyle = 'purple' };
},
'template': '<div style="background: {{someStyle}}" ng-click="someFunc()"> click me to change colors </div>'
}
});
And the html:
<div ng-app="colorSwap">
<element-with-style>123</element-with-style>
</div>
I hope this helps. The rest of the answers cover class manipulation more or less.
For css manipulation inside of the childs of your directive try this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attr){
// For your tag
element.addClass('MyClass');
// For elements inside your directive tag
var tag_childs = element[0].childNodes;
for(var i = 0; i < element[0].childElementCount; i++){
tag_childs[i].style.height = '70px';
}
}
}
});
Here is an example, please note that this is probably not the best use of AngularJS, being declarative, you would likely want to just put the classes on the markup. However, just so you understand what's going on, let me demonstrate a simple directive to do what you first asked.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function($compile) {
return {
restrict: 'E', // this means it will be an element
link: function(scope, element, attrs, ctrl) {
// First, I included the $compile service because it will be needed
// to compile any markup you want to return to the element.
// 1. Add the class, as you wanted
element.addClass('MyClass');
// 2. Add markup
var html = '<div>Hello World</div>';
//Compile it and add it back
$compile(html)(scope);
element.html(html);
}
};
});
Finally, on your markup, you just put this in:
<my-tag />
app.directive('bookslist', function() {
return {
scope: true,
templateUrl: 'templates/bookslist.html',
restrict: "E",
controller: function($scope){
},
link: function(scope, element, attributes){
element.addClass('customClass');
}
}
});
.customClass table{
background: tan;
}
.customClass td{
border: 1px solid #ddd;
}
<!DOCTYPE html>
<html>
<head>
<link href="app.css" rel="stylesheet">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<title>Task</title>
</head>
<body ng-app="app">
<div ng-controller="myCtrl">
<bookslist></bookslist>
</div>
</body>
</html>
Angular
app.directive("time",function(){
var directive={};
directive.restrict="A";
directive.link=function(scope,element,attr,ctrl){
element.css({
backgroundColor:'#ead333'
});
}
var time=new Date().toTimeString();
directive.template=time;
return directive;
});
HTML
The times is <span time></span>
I didn't found the perfect solution just yet, but I'm following John Papa's styling of controllers even with directives:
the directive is a folder (directiveName.directive)
3 files inside: directiveName.directive.js, directiveName.template.html, directiveName.styles.css
use templateUrl when declaring the directive. The template has the link to the css file, as usual
I found it to be very clean and follows a pattern. The bad side of it is that you create several <link> tags near the directives in the rendered HTML (not seem to be a issue still, though). Check out this comment too.
That being said, take a look at Angular 1.5 component's. It's relatively new and has a much better approach. Now I use directives only for DOM manipulation (not reusability as components).

Categories