Angular js creating a generic directive - angularjs

I am trying to create a generic directive in angular, for example, take this html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Testing directives</title>
<script src="../scripts/angular.js"></script>
<script type="text/javascript">
angular.module('ngDirTest', [
//controllers
])
</script>
<script src="../scripts/populate.js"></script>
<script src="../Directive/GenericDirective.js"></script>
</head>
<body ng-app="ngDirTest">
<div ng-controller="PopulateCtrl">
<div generic-directive ng-model="nameone"></div>
<div generic-directive ng-model="nametwo"></div>
<div generic-directive ng-model="namethree"></div>
<div generic-directive ng-model="namefour"></div>
<div generic-directive ng-model="namefive"></div>
<div generic-directive ng-model="namesix"></div>
</div>
</body>
</html>
and take this controller:
angular.module("ngDirTest").controller('PopulateCtrl', ['$scope', function($scope) {
//
}]);
and take this directive:
angular.module('ngDirTest').directive('genericDirective', [ function() {
return {
restrict: 'A',
scope: {
fn: "&"
},
template: '<input type="text" ng-model=name placeholder="type a name" />',
link: function(scope) {
scope.$watch('name', function(val) {
//
})
}
}
}]);
the idea is to have a template that can has different placeholders for the different text input tags. Is there a good way to do this?
I was thinking about getting the model from the input text being watched and then do something with it, but that won't help me set the placeholders and initialise the the input text fields.

Add the 'placeholder' to the directive model (see Change value of input placeholder via model?), or use a template function to set from the directive attribute.
I wasn't able to get it to bind from the isolate scope (I'm still learning this stuff), but here's an example using a template function: http://embed.plnkr.co/R8uq77sA1bGPNLUnVLW5/preview
Specifically, set template: templateFactory, then just put the placeholder in the directive markup:
var templateFactory = function($el, attrs) {
return '<input type="text" ng-model="model" placeholder="' + (attrs.placeholder || 'default placeholder') + '" />'
}

Related

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

Angular JS. What the order of actions in HTML when creating a directive?

I have just a problem rightly ask a question).
I created angularJS app 'myApp'. And app get list of two groups. And app should to represent on browser two checkboxes: first - checked, second - empty.
But maybe I didn't count the order of action of building of angular's directives.
This is example
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{group.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function(){
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
}
};
});
</script>
</body>
</html>
I get a right 'id' of element on front-end when I inspected code on browser. If I put on template number '769' instead {{info.id}}, the first checkbox will checked.
But I think that controller of directive acts before assignment 'id' of elements.
But I need realize this by that method. Because I try to explain on scratches the situation which I don't understand.
Thanks a lot.
Two issues :
First point
Your template is :
<input type='checkbox' id='{{group.id}}'> {{info.id}}
It should be :
<input type='checkbox' id='{{info.id}}'> {{info.id}}
Second point
You must call your dom modification in a $timeout function. On the first execution of your controller, your html does not exists yet.
$timeout will trigger the code at the next digest. So, your html will exists.
Of course, do not modify your html in your controller (even in a directive). The link function is the place to modify the html.
Corrected snippet
Here is the snippet with the correction. There is a jquery error, I didn't watch what it is. Do not hesitate to edit to throw it away.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MainController">
<div ng-repeat="group in groups">
<mygroups info='group'></mygroups>
</div>
</div>
<script type="text/javascript">
var groups = [
{
"id":"769", "spam_status":"checked"},
{
"id":"1262", "spam_status":""}
];
var app = angular.module("myApp", []);
app.controller("MainController", function($scope){
$scope.groups = groups;
});
app.directive("mygroups", function(){
return{
scope: {info: "="},
template: "<input type='checkbox' id='{{info.id}}'> {{info.id}}",
//WORK FOR FIRST CHECKBOX IF TYPE: //BUT IT IS NO SENSE//
// template: "<input type='checkbox' id='769'> {{info.id}}",
controller: function($timeout){
$timeout(function() {
for (var i = 0; i < groups.length; i++){
$('#' + groups[i].id).attr(groups[i].spam_status,'');
}
});
}
};
});
</script>
</body>
</html>
Looks like you want to hook the checkbox up
template: "<input type='checkbox' ng-model='info.spam_status' ng-true-value='\'checked\'' id='{{group.id}}'> {{info.id}}"

Carrying a parent element's controller into an HTML template that is inserted into a directive's template via ng-bind-html

I've created an angular directive with a template similar to this:
<div>
<header class='header'></header>
<div class='subheader'>
<div ng-bind-html='subheaderTemplate'></div>
</div>
<div class='content' ng-transclude></div>
</div>
This directive will always be used within a parent div that has a controller on it. Each usage of the directive will pass in a different HTML snippet as the subheaderTemplate, from the parent controller. Some instances need this HTML snippet to use the controller from the parent container. But testing it out, the subheader template doesn't seem to know about the parent controller at all.
Here's a typical usage of the directive:
<div ng-controller="MyController as mc">
<my-directive subheader-template="mc.subheaderTemplate">
<div class="content">Content</div>
</my-directive>
</div>
Am I misunderstanding some behavior from ng-bind-html or something?
I think I understand what you are after and I think I got it to work in this plunker based on the example in the Angular docs. So it should work. Maybe you can provide more code if this example does not solve your problem.
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.header = '<h1>I am an <code>HTML</code>string with ' +
'links! and other <em>stuff</em></h1>';
}])
.directive('test', function() {
return {
restrict: 'E',
template: "<div><header class='header'></header><div class='subheader'><div ng-bind-html='subheaderTemplate'></div></div><div class='content' ng-transclude></div></div>",
scope: {
subheaderTemplate: '='
},
transclude: true
}
});
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example62-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
<test subheader-template="header">
<h3>subheader</h3>
</test>
</div>
</body>
</html>
Plunkr

AngularJS : how to get directive input field model value in controller?

I created a custom directive name kid. In that I have one input field with having usermodel object. I need to get its value in my controller. Can we get user model object in my controller. Actually I used to same directive in my view. I need to get both directive input values in my controller .
Here is my Plnkr
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
I updated your plunkr: updatedMyPlunker
I am passing the usermodel to the kid directive via its isolated scope.
The = sign makes sure that the two models will update through two way data binding
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid usermodel="usermodel"></kid>
<kid usermodel="usermodelSecondKid"></kid>
</div>
</body>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function($scope){
$scope.usermodel = '';
$scope.usermodelSecondKid = '';
$scope.$watch("usermodel", function(newvalue,oldvalue){
console.log(newvalue);
})
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{ usermodel: "=usermodel"
},
template:"<input type='text' ng-model='usermodel'/>{{usermodel}}",
}
})
Forked your plnkr. Passed two way data model from controller to directive. kid1 and kid2 are controller variable. Which will value you enter in text box.
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#*" data-semver="2.0.0" src="scruipt"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
</head>
<body ng-app="Testappp">
<div ng-controller="testcontroller">
<kid ng-model="kid1"></kid>
<kid ng-model="kid2"></kid>
</div>
</body>
<script>
var app =angular.module('Testappp',[]);
app.controller('testcontroller',function(){
})
app.directive('kid',function(){
return {
restrict:"E",
scope:{
ngModel: '=ngModel'
},
template:"<input type='text' ng-model='ngModel'/>{{ngModel}}",
}
})
</script>
</html>

AngularJS - Create a directive that adds a sibling element

I'm creating a my-validate directive that looks something like this
<input my-validate="customValidation" ng-model="model" />
What I want to do is to attach a sybling element to the directive like this
Error template:
<ul class"errors">
<li ng-repeat="for error in errors">{{error}} not valid</li>
</ul>
errors is defined in the scope of the directive.
I've added the error template in the compile function, but the problem I have is that the scope in the link function is not the same as the attached template.
Here is a plunker to illustrate the issue: http://plnkr.co/edit/ghdtdYruQaaO0Yxxlrt1?p=preview
'world' is seen in the directive template, but not on the added element :S.
That's because your div "2 hello" is outside the container where your scope is visible.
you can use element.append() instead of element.after() to have the scope available.
Directive
var app = angular.module('plunker', []);
app.directive('myValidate', function($compile) {
return {
template: '<span>1. Hello {{world}} my scope is {{$id}} (parent: {{$parent.$id}})<span/>',
replace: true,
restrict: 'A',
scope: true,
compile: function (element) {
element.append('<div>2. Hello {{ world }}, my scope is {{$id}} (parent: {{$parent.$id}})</div>');
return function(scope) {
scope.world = 'World';
//$compile()(scope);
};
}
};
});
HTML
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script data-require="angular.js#1.1.5" data-semver="1.1.5" src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body>
<input my-validate="" />
</body>
</html>
http://plnkr.co/edit/dU3holBCePKe0ZAwQKh1?p=preview
I was reading and checking the example because I was in the same situation to display validation messages but under the input field and the message can change according to what kind of validation is required.
So I came up with this solution
var app = angular.module('app', []);
app.controller('ctrl', function($scope, CONSTANTS) {
$scope.title = "title";
$scope.CONSTANTS = CONSTANTS;
});
app.constant('CONSTANTS', {
LENGHT_1: 3,
LENGHT_2: 4
});
app.directive('dir', function($compile) {
return {
scope: true,
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attrs, ngModel) {
scope.maxLength = false;
scope.required = false;
scope.max = scope.$eval(attrs['ngMaxlength']);
var tpl = '<div ng-if="maxLength" ng-include="\'length.tpl.html\'"></div>' +
'<div ng-if="required" ng-include="\'required.tpl.html\'"></div>';
var el = $compile(tpl)(scope);
elem.after(el);
scope.$watch(attrs['ngModel'], function(newValue, oldValue, scope) {
if (ngModel.$error !== null && ngModel.$error.maxlength) {
scope.maxLength = true;
} else {
scope.maxLength = false;
}
if (ngModel.$error !== null && ngModel.$error.required && ngModel.$dirty) {
scope.required = true;
} else {
scope.required = false;
}
});
}
}
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script type="text/ng-template" id="length.tpl.html">
max length {{max}}
</script>
<script type="text/ng-template" id="required.tpl.html">
required
</script>
</head>
<body ng-controller="ctrl">
<h1>Input Validation</h1> {{title}}
<br><br>
<form name="form" novalidate>
<input dir name="input_one" ng-model="bar" ng-maxlength="CONSTANTS.LENGHT_1" required>
<br>
input one: {{form.input_one.$error}}
<br>
<br>
<input dir name="input_two" ng-model="foo" ng-maxlength="CONSTANTS.LENGHT_2">
</form>
<br>
input two: {{form.input_two.$error}}
</body>
</html>
On Plunkr
Hope it helps.
I think you're on the right track by using the form errors to toggle display. That's exactly how it's recommended in the standard Angular documentation.
If you'd like to show multiple errors for a single input, however, and possible even control the error messages from there, I'd recommend utilizing a service, such as implemented at http://plnkr.co/edit/iNcNs2ErrOnYf9I7whdu?p=preview.
Right now you can have one message per token, but as many tokens as you want per input. If you want multiple messages per token, just use an array of messages instead of single string value (note, unset does become more complicated with that method).
Hope that helps,
Alex

Resources