Create custom input directive in angular - angularjs

I would like to create a custom input that looks like that:
<my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title"></my-input>
my-input should receive any property that regular input can get (like placeholder and etc...).
the output should be like this (myInputTemplate.html):
<div class="my-input">
{{title}}
<input type="text" ng-model="text" />
</div>
I created a directive:
myApp.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
templateUrl: '/myInput/myInputTemplate.html',
replace: true,
scope: {
text: '=ngModel',
title: '=title'
},
}
});
the ng-model is bindded ok now,
my question is:
How can I pass the attributes (like placeholder and etc) from my-input to the inside input?
I think that I approached it the wrong way, maybe I need to do it like that:
<input my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title"></input>
and to wrap the input with:
<div class="my-input">
{{title}}
<here will be the original input>
</div>

directive call should be like
<my-input ng-model="aScopeProperty" placeholder="'Enter text'" title="'This is my title'"></my-input>
note the placeholder="'Enter text'" Enter text with in quotes ('), this indicate these values are string so angular will not search for scope variable.
and in the directive
myApp.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
templateUrl: '/myInput/myInputTemplate.html',
replace: true,
scope: {
text: '=ngModel',
title: '=title',
placeholder : '=placeholder'
},
}
});
and the template
<div class="my-input">
{{title}}
<input type="text" ng-model="text" placeholder="{{ placeholder }}" />
</div>
here is the demo Plunker

You can use ng-attr as the following:
<input type="text" ng-model="text" ng-attr-placeholder="{{placeholder}}"/>
And send placeholder as attribute in your scope as the following:
scope: {
text: '=ngModel',
title: '=title',
placeholder : '=placeholder'
}
I recommend to read about ng-attr-attrName, and this useful answer.
Dynamic attributes
Read my question, and accepted answer.

The second approach succeeded!
final code:
<input my-input ng-model="aScopeProperty" placeholder="Enter text"
data-title="This is my title">
The Directive:
app.directive('myInput', function () {
return {
restrict: 'A',
scope: {
title: '=title'
},
link: function ($scope, $element) {
var wrap = angular.element('<div class="my-input-wrapper" />');
$element.addClass('form-control').removeAttr('my-input');
wrap.insertBefore($element);
$element.appendTo(wrap);
if ($scope.title) {
var title = angular.element('<span class="my-title">' + $scope.title + '</span>');
title.appendTo(wrap);
}
},
}
});
I even created my first Plunker for it, unfortunately, the Plunker don't works because it doesn't recognize: insertBefore and appendTo
http://plnkr.co/edit/XnFM75vOBg4ifHUQzGOt?p=preview

Related

Decorating AngularJS Directive template inside the directive

I have a directive which is defined, say, as below:
angular.module('some-module').directive('someDirective', function() {
return {
restrict: 'E',
replace: 'true',
templateUrl: 'some-template.html',
link: link,
require: '^form',
transclude: true,
scope: {
decorate: '=',
}
};
});
Let's say this is how the some-template.html looks (there is more in the actual template though):
<div ng-transclude></div>
And this is how I will use the directive:
<some-directive decorate="true">
<input name="x" type="number" ng-model="x">
<input name="y" type="number" ng-model="y">
</some-directive>
<some-directive decorate="false">
<input name="a" type="number" ng-model="a">
<input name="b" type="number" ng-model="b">
</some-directive>
What I want the directive to do is to manipulate the DOM so that if decorate is true then, the two input fields should be decorated with some divs as below:
<div class="some-outer-class">
<div class="some-class-1">
<input name="x" type="number" ng-model="x">
</div>
<div class="some-class-2">
<input name="y" type="number" ng-model="y">
</div>
<div><i class="some-glyph-icon"></i></div>
</div>
If the decorate attribute is false, or absent, the directive shouldn't do any manipulation.
Couldn't figure out how to do this. Any help is appreciated.
You can simply modify the template in link function :
Demo
link: function(scope, elem, attrs){
if(scope.decorate || attrs.decorate != null){
elem.find('INPUT').wrap('<div class="decorate-class"></div>')
}
}
You can do this inside the directive. You first define a controller inside your directive as follows:
angular.module('some-module').directive('someDirective', function() {
var controller = function($scope) {
//The controller methods
};
return {
restrict: 'E',
replace: 'true',
templateUrl: 'some-template.html',
link: link,
require: '^form',
transclude: true,
scope: {
decorate: '=',
},
controller: controller,
controllerAs: 'myCtrl'
};
});
Inside the controller, you check the decorate value, and make the DOM manipulation accordingly. You can access the decorate value from your controller via the $scope.
var controller = function($scope) {
if($scope.decorate){
//Make the DOM manipulation
}
};
DOM manipulation is done as follows:
var initialInput = document.querySelector('query'); //You have to select your desired input elements here
var decoratedInput = document.createElement("div");
decoratedInput.className += " some-class-1";
decoratedInput.innerHTML = "<input name='x' type='number' ng-model='x'>";
initialInput.parentNode.replaceChild(decoratedInput, initialInput);

angular 1.4 directive toggle view

I want to show two different inputs depending on a toggle attribute.
No I have the problem that I should define each attribute/property of the input in my directive, but the binding doesn't work.
Directive:
angular.module('directive')
.directive('inputBlock', function () {
return {
restrict: 'AEC',
replace: true,
scope: {
model: '=',
modernStyle:'=',
name:'=',
type:'=',
label:'='
},
link: function (scope, elem, attrs, ctrl) {},
templateUrl: 'views/templates/inputBlockTemplate.html'
};
});
Template:
<div>
<div ng-if="!modernStyle">
<label>{{label}}</label>
<input ng-model="model" name="{{name}}" type="{{type}}"/>
</div>
<md-input-container ng-if="modernStyle">
<label>{{label}}</label>
<input ng-model="model" name="{{name}}" type=" {{type}}"/>
</md-input-container>
</div>
Usage:
<input-block model="name" label="'firstname'" modern-style="true" name="'firstname'" type="'text'">
</input-block>
Is it possible to do something like a toggle in directives?
Furthermore is it possible to redirect the bindings to directives?
ng-if creates a new child scope, try change by ng-show/ng-hide
Understanding Scopes
Other considerations:
As you use name,label and type as text inside your directive is not necesary that it will be binding, I recomend use # instead of =, or access it directly from the attrs link argument.
The scope.model could be set as ngModel to use the default angular directive.
Javascript:
angular.module("directive").directive("inputBlock", function () {
return {
restrict: "AEC",
replace: true,
scope: {
ngModel: "=",
modernStyle:"#",
//name:"#",
//type:"#",
//label:"#"
},
link: function (scope, elem, attrs, ctrl) {
scope.type=attrs.type;
scope.name=attrs.name;
scope.label=attrs.label;
},
templateUrl: "views/templates/inputBlockTemplate.html"
};
});
Template:
<div>
<div ng-show="!scope.modernStyle">
<label>{{scope.label}}</label>
<input ng-model="scope.model" name="{{scope.name}}" type="{{scope.type}}"/>
</div>
<md-input-container ng-show="scope.modernStyle">
<label>{{scope.label}}</label>
<input ng-model="scope.model" name="{{scope.name}}" type={{scope.type}}"/>
</md-input-container>
</div>
Usage:
<input-block ng-model="name" label="firstname" modern-style="true" name="firstname" type="text">
</input-block>

angularjs dynamic templateurl values

I am trying to create a "name" directive that can host both first and last names.
the code i currently have is:
index.html
<div data-dm-name label="First Name:" info="first_name"></div>
<div data-dm-name label="Last Name:" info="last_name"></div>
directive:
angular
.module('app.names.directive',[])
.directive('dmName', [ directive ])
function directive() {
return {
restrict: 'E',
scope: {
label: '=',
info: '='
},
templateUrl: '/assets/components/name_template.html'
};
}
name_template.html
<div class="wizard-form-group">
<div class="input-group">
<label class="wizard-form-label" for="info">
{{ label }}
</label>
<span class="input-field"
data-ng-class="{
error: form.info.$dirty &&
form.info.$invalid,
focused: form.info.$focused
}"
>
<input
type="text"
name="info"
id="info"
data-ng-model="info"
data-ng-required="true"
data-ng-focus="form.info.$focused = true"
data-ng-blur="form.info.$focused = false"
/>
</span>
</div>
</div>
My problem is that I don't seem to be able to pass in the values for label and info into the template file. What am I doing wrong?
I have just started out using angular so hopefully this has a simple solution.
Thanks in advance
in your directive function add a link function
function directive() {
return {
restrict: 'EA',
scope: {
label: '=',
info: '='
},
templateUrl: '/assets/components/name_template.html',
link : function($scope, element, attrs){
if(attrs.label){
$scope.label = attrs.label
}
if(attrs.info){
$scope.info = attrs.info
}
}
};
}
Your directive is restricted to element, but you're using it as attribute. So you're directive isn't acting upon the element.
You should modify the DDO to:
function directive() {
return {
restrict: 'A', // attribute allowed
scope: {
label: '=',
info: '='
},
templateUrl: '/assets/components/name_template.html'
};
}

AngularJS custom directive binding to model

I am trying to build an object like:
{"age":"21","name":"foo", "address":["address1", "address2"]}
For address I am using a custom directive, I don't know how to attach this to scope (person.address). If I give ng-model="person.address" in directive, it is taking same address for both the input texts. I have to isolate scope, but don't know where to put it.
Fiddle: http://codepen.io/goutham2027/pen/EagPZG
HTML
<div ng-controller="testCtrl">
<form>
Name: <input type="text", ng-model="person.name"> <br/>
Age: <input type="text", ng-model="person.age"> <br/>
Address-1 <address> </address>
Alternate-Address <address> </address>
</form>
{{person}}
</div>
JS
app.directive('address', function() {
return {
restrict: 'E',
template: '<input type="text">'
}
})
Edit: I found out how to do it.
Fiddle: http://codepen.io/goutham2027/pen/LEjaXP
Just add an isolated scope to your directive:
app.directive('address', function() {
return {
restrict: 'E',
scope: {
ngModel: '='
},
template: '<input ng-model="ngModel" type="text">'
}
})
http://codepen.io/anon/pen/raMxJo
Try adding a person attribute to your controller.
app.controller('testCtrl', function($scope) {
$scope.person = {age:"21",name:"foo", address:["address1", "address2"]};
});
As for using your custom directive to handle the two different address fields, go with the answer from #Rasalom.
You should give the directive a private scope, like so:
scope: {
model: '='
}
Then you'll be able to pass it to your directive:
<address model="address"></address>
In your directive the address is now available in the HTML template through the private scope.
You can use isolated scope for that:
app.directive('address', function() {
return {
restrict: 'E',
scope : {person:'='},
template: '<input type="text" ng-model="person">'
}
});
<div ng-controller="ctrl">
<form>
Name: <input type="text", ng-model="person.name">
Age: <input type="text", ng-model="person.age"> <br/>
Address-1: <address person="person.address1"> </address>
Alternate-Address: <address person="person.address2"></address>
</form>
</div>
CodePen example.

Angularjs: validation not working when control is based on directive

Being rather new to Angularjs, I am creating textbox-label combinations in Angularjs using directives. It's working very well, but I can't get validation to work. Here is a stripped-down example.
The Html:
<form name="form" novalidate ng-app="myapp">
<input type="text" name="myfield" ng-model="myfield" required />{{myfield}}
<span ng-show="form.myfield.$error.required">ERROR MSG WORKING</span>
<br>
<div mydirective FIELD="myfield2" />
</form>
The Javascript:
var myapp = angular.module('myapp', []);
myapp.directive('mydirective', function () {
return {
restrict: 'A',
scope: { ngModel: '=' },
template: '<input type="text" name="FIELD" ng-model="FIELD" />{{FIELD}}
<span ng-show="form.FIELD.$error.required">ERROR MSG NOT WORKING</span>'
};
});
The hard coded input - myfield - works, the other - myfield2 - doesn't (the binding does, just not the required-error message).
How do I tell the ng-show attribute to sort of "replace" FIELD in form.FIELD.$error.required by myfield2?
Here is a jsFiddle.
The problem is that your directive creates a new scope for the directive, this new scope does not have access to the form object in the parent scope.
I came up with two solutions, though I suspect there is a more elegant "Angular" way to do this:
Passing down the form object
Your view becomes:
<div mydirective FIELD="myfield2" form="form" />
And the scope definition object:
return {
restrict: 'A',
scope: {
ngModel: '=',
form: '='
},
template: '<input type="text" name="FIELD" ng-model="FIELD" required/>{{FIELD}}<span ng-show="form.FIELD.$error.required">ERROR MSG NOT WORKING</span>'
};
I've updated the fiddle with this code: http://jsfiddle.net/pTapw/4/
Using a controller
return {
restrict: 'A',
controller: function($scope){
$scope.form = $scope.$parent.form;
},
scope: {
ngModel: '='
},
template: '<input type="text" name="FIELD" ng-model="FIELD" required/>{{FIELD}}<span ng-show="form.FIELD.$error.required">ERROR MSG NOT WORKING</span>'
};

Resources