AngularJS custom directive binding to model - angularjs

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.

Related

Angular Custom Directive .match is not a function

I am trying to create a simple angular radio-group directive. the component will be passed an array of strings, an id, and an attribute to bind the selected option to. When I run my code I get the following error
angular.js:13642TypeError: a.match is not a function
here is what I have:
app.js:
angular.module('atp', [])
.directive('appform', require('./application-directive'))
.directive('radioGroup', require('./radio-group/radio-group-directive'))
.directive('yesNoRadioGroup', require('./radio-group/yes-no-radio-group-directive'));
radio-group-directive.js:
function RadioGroupDirective(){
return{
restrict:'E',
transclude:true,
template:require('./radio-group-template.html'),
replace:true,
scope:{
id:'=',
selected:'=',
options:'='
}
}
}
module.exports = RadioGroupDirective;
radio-group-template.html:
<div>
<label for="{{radioGroupId}}"><ng-transclude></ng-transclude></label>
<fieldset id="{{radioGroupId}}">
<div ng-repeat="option in optionsArray track by $index">
<label for="{{radioGroupId}}-{{$index}}">{{option}}</label>
<input id="{{radioGroupId}}-{{$index}}" type="radio" value="option" ng-model="selectedOption">
</div>
</fieldset>
</div>
yes-no-radio-group-directive.js:
function YesNoRadioGroupDirective() {
return {
restrict: 'E',
transclude:true,
template: require('./radio-group-template.html'),
replace: true,
scope: {
RadioGroupId: '=',
selectedOption: '=',
optionsArray: ['Yes', 'No']
}
}
}
module.exports = YesNoRadioGroupDirective;
usage:
<form>
<yes-no-radio-group radio-group-id="test" selected-option="Controller.Form.SelectedYesNo">Select Yes or No:</yes-no-radio-group>
<radio-group radio-group-id="test-radio" options-array="['Hello World','Some Other Option']" selected="Controller.Form.SelectedYesNo">Select one of the options:</radio-group>
</form>
Why are you doing this? You don't need require at first instance and secondly if you are requiring a html, u need templateUrl instead of template
template:require('./radio-group-template.html'),

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);

Create custom input directive in angular

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

ng-model is not bind with controller's $scope when I am creating a form dynamically using directive.

view code:- mydir is my custom directive
<div ng-model="vdmodel" mydir="dataValue">
</div>
my directive :-
app.directive('mydir',['$translate',function($translate){
return {
restrict: 'A',
transclude: true,
scope: {dir:'=mydir'},
compile: function(element, attrs) {
return function(scope, element, attrs, controller){
var setTemplate = '';
var setOpt = '';
if(scope.dir.itemtype== 'NUMBER'){
setTemplate = '<input type="number" class="form-control form-font ng-animate ng-dirty"';
setTemplate +='" ng-model="dir[somevalue]" value="'+scope.sizing.somevalue+'" >';
element.html(setTemplate);
}
}
}
}
});
There are many more form element in directive, but when I am trying to submit and collect value in my controller function I get nothing.
What I am doing wrong and what is the best way to collect form values ?
there are quiet a few changes that you will need to do
1.as you are using isolate scope, pass ngModel as well to the directive
scope: {dir:'=mydir', ngModel: '='},
2.as per the best practise ngModel must always have a dot
ng-model="params.vdmodel"
3.make sure to initialize the params object in controller
$scope.params = {}
Usually, a directive would share the same scope as the parent controller but since you are defining a scope in your directive, it sets up it's own isolate scope. Now since the controller and directive have their seperate scope, you need a way to share the data between them which is now done by using data: "=" in scope.
The app code
var myApp = angular.module('myApp', []);
myApp.controller('myController', function ($scope, $http) {
$scope.vdmodel = {};
})
.directive("mydir", function () {
return {
restrict: "A",
scope:{
data:"=model",
dir:'=mydir'
},
templateUrl: 'test/form.html'
};
});
The form.html
<form>
Name : <input type="text" ng-model="data.modelName" /><br><br>
Age : <input type="number" ng-model="data.modelAge" /><br><br>
Place : <input type="text" ng-model="data.modelPlace" /><br><br>
Gender:
<input type="radio" ng-model="data.modelGender" value="male"/>Male<br>
<input type="radio" ng-model="data.modelGender" value="female"/>Female<br><br><br>
</form>
The page.html
<div ng-app="myApp" >
<div ng-controller="myController" >
<div model="vdmodel" mydir="dataValue"></div>
<h3>Display:</h3>
<div>
<div>Name : {{myData.modelName}} </div><br>
<div>Age : {{myData.modelAge}}</div><br>
<div>Place : {{myData.modelPlace}}</div><br>
<div>Gender : {{myData.modelGender}}</div><br>
</div>
</div>
</div>
You have to use $compile service to compile a template and link with the current scope before put it into the element.
.directive('mydir', function($compile) {
return {
restrict: 'A',
transclude: true,
scope: {
dir: '=mydir'
},
link: function(scope, element, attrs, controller) {
var setTemplate = '';
var setOpt = '';
if (scope.dir.itemtype == 'NUMBER') {
setTemplate = '<input type="number" class="form-control form-font ng-animate ng-dirty"';
setTemplate += '" ng-model="dir.somevalue" value="' + scope.dir.somevalue + '" >';
element.html($compile(setTemplate)(scope));
}
}
}
});
See the plunker below for the full working example.
Plunker: http://plnkr.co/edit/7i9bYmd8blPNHch5jze4?p=preview

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