Pass ng-model and place-holder value into directive - angularjs

I have a segment of code needs to be reuse a lot, there for I want to just create a directive for it.
<div class="btn-group">
<div class="input-group">
<div class="has-feedback">
<input type="text" class="form-control" placeholder="BLAH BLAH" ng-model="model">
<span class="times form-control-feedback" ng-click="model=''" ng-show="model.length > 0"></span>
</div>
</div>
</div>
I want to use this code as template in directive.
Create a directive used as follow:
<div search-Field ng-model="model" placeholder="STRING"></div>
to replace to old html, ng-model and placeholder will be as variables.
angular.module('searchField', [])
.directive('searchField', [function () {
return {
scope: {
placeholder: '#',
ngModel: '='
},
templateUrl: 'Partials/_SearchInputGroup.html'
}
}]);
Is it the way of doing it?

That looks fine.
Here is a sample for you -http://plnkr.co/edit/LCWHRj6xc9bxwrgpaAb4
corrected few typos and binded placeholder and ngModel data in the directive.
Template:
<div class="btn-group">
<div class="input-group">
<div class="has-feedback">
<input type="text" class="form-control" placeholder="{{placeholder}}" ng-model="ngModel">
<span class="times form-control-feedback" ng-click="model=''" ng-show="ngModel.length > 0">Show</span>
</div>
</div>
</div>

Related

AngularJS form in custom directive not updating after data binding

This is my code :
Template :
<div ng-repeat="framework in frameworks" id="{{framework.id}}"
role="tabpanel" class="tab-pane show active" >
<div class="progress" >
<div ng-click="display_framework_formulaire(framework)"
class="progress-bar" role="progressbar"
style="width: {{framework.level}}%"
aria-valuenow="{{framework.level}}"
aria-valuemin="0" aria-valuemax="100">
<div data-toggle="tooltip" data-placement="top"
data-title="{{framework.version}}">
</div>
{{framework.nom}}
</div>
</div>
</div>
This is the function in my controller :
$scope.display_framework_formulaire = function(framework)
{
$scope.frameworkCourant = framework;
$scope.vueCourante = 'VUE_FORMULAIRE_FRAMEWORK'
}
This is the directive in template:
<formulaire-framework ng-show="vueCourante == 'VUE_FORMULAIRE_FRAMEWORK'"
categories="categories" framework="frameworkCourant" >
</formulaire-framework>
And the directive :
app.directive("formulaireFramework", function() {
return {
restrict: "E",
templateUrl: 'formulaireFramework.html',
scope: {
framework : '=framework',
categories : '=categories'
},
}
})
So it's working after loading homepage.
But when i do :
restfulService.getFrameworksByCategorieValue(categorie_value)
.then(function(frameworks){
$scope.frameworks = [];
angular.forEach(frameworks, function(framework, key) {
$scope.frameworks.push(framework);
});
});
Which update the list of frameworks, when i click on a framework, the value of element of formulare is this of last action and not the value of the framework current.
PS : my form :
<form name="frameworkForm" id="frameworkForm" method="POST" class="spacer"
ng-submit="submit()" ng-controller="frameworkFormulaireController" >
<div class="input-group">
<select ng-model="framework.categorie.id">
<option ng-repeat="categorie in categories"
ng-value="categorie.id"
ng-selected="categorie.id == framework.categorie.id" >
{{categorie.label}}
</option>
</select>
</div>
<div class="input-group" >
<input type="text" class="form-control" ng-value="id"
ng-model="framework.id">
</div>
<div class="input-group">
<span class="input-group-addon">Nom</span>
<input type="text" class="form-control" ng-value="nom"
ng-model="framework.nom">
</div>
<div class="input-group">
<span class="input-group-addon">Version</span>
<input type="text" class="form-control" ng-value="version"
ng-model="framework.version">
</div>
<div class="input-group">
<span class="input-group-addon">Niveau</span>
<input type="text" class="form-control" ng-value="level"
ng-model="framework.level">
</div>
<input type="submit" class="btn btn-success pull-right" >
</form>
Remove the ng-controller directive from the form:
<form name="frameworkForm" id="frameworkForm" method="POST" class="spacer"
ng-submit="submit()" ̶n̶g̶-̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶=̶"̶f̶r̶a̶m̶e̶w̶o̶r̶k̶F̶o̶r̶m̶u̶l̶a̶i̶r̶e̶C̶o̶n̶t̶r̶o̶l̶l̶e̶r̶"̶ >
<!-- input controls -->
<!-- input controls -->
<!-- input controls -->
<!-- input controls -->
</form>
Add the controller to the directive:
app.directive("formulaireFramework", function() {
return {
restrict: "E",
templateUrl: 'formulaireFramework.html',
controller: 'frameworkFormulaireController',
scope: {
framework : '=framework',
categories : '=categories'
},
}
})
The ng-controller directive adds a new child scope which is unneccessary and can have prototypal inheritance variable hiding problems.
For more information, see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?.

Share code between controllers and directives

In my angular app I have two input fields on most views that have a start and end date. These are updated by the user and the result is stored in the URL to maintain state or allow people to share links and start exactly where they left off.
I am trying to extract this into a directive that updates the url on every change to one of those two fields. Additionally I want each controller to be able to access the dates as they are used as query parameters for other endpoints.
Here is my directive:
angular
.module('dateDemo')
.directive('DateFields',['$location', DateFields]);
function DateFields($location) {
return {
templateUrl: 'shared/partials/_dateFields.html',
scope: false,
bindToController: {
start_date : '=',
end_date : '='
},
controller: DateFieldCtrl,
controllerAs: 'dateCtrl'
};
function DateFieldCtrl() {
var dateCtrl = this;
dateCtrl.refreshUrl = refreshUrl;
function refreshUrl() {
var searchString = '';
searchString += '&start_dt=' + moment(dateCtrl.start_dt).format('YYYY-MM-DD');
searchString += '&end_dt=' + moment(dateCtrl.end_dt).format('YYYY-MM-DD');
$location.search(searchString);
}
}
}
My Partial:
<div class="col-md-3">
<label><i class="glyphicon glyphicon-calendar"></i> <b>Date</b></label>
<div class="form-group">
<input type="date" class="form-control input-sm" ng-model="vm.data.start_dt" ng-change="refreshURL()">
<p class="text-right"><small class="help-block">From Date</small></p>
</div>
</div>
<div class="col-md-3">
<label> </label>
<div class="form-group">
<input type="date" class="form-control input-sm" ng-model="vm.data.end_dt" ng-change="refreshURL()">
<p class="text-right"><small class="help-block">To Date</small></p>
</div>
What I'm attempting to do is bind the vm.data.end_dt and vm.data.start_dt variables so that the directive manages the updating, while the controllers have access to these values. I can't get the function in my directive to trigger, and I've tried different combinations of which variable goes in the partial to no avail. What am I doing wrong?
Since you are using controllerAs: 'dateCtrl', you need to change your ng-change code to call dateCtrl.refreshUrl()
Also, you have vm.data referenced in your ng-model, which I think should also be dateCtrl
<div class="col-md-3">
<label><i class="glyphicon glyphicon-calendar"></i> <b>Date</b></label>
<div class="form-group">
<input type="date" class="form-control input-sm" ng-model="dateCtrl.start_dt" ng-change="dateCtrl.refreshURL()">
<p class="text-right"><small class="help-block">From Date</small></p>
</div>
</div>
<div class="col-md-3">
<label> </label>
<div class="form-group">
<input type="date" class="form-control input-sm" ng-model="dateCtrl.end_dt" ng-change="dateCtrl.refreshURL()">
<p class="text-right"><small class="help-block">To Date</small></p>
</div>
</div>
I was able to fix it by including bindToController: true.

How do I build a directive in angular that adds a wrapper around an input and shows message if input is invalid?

I'm a total beginner in Angular, so maybe I am thinking about it totally wrong.
What I'm trying to do is create a directive that wraps an input in some boilerplate html. It should bind the input's ng-model to the parent scope (the myForm controller's scope) but should have access to the validation state of the input.
Here's my setup:
parent_form.html
<form name="myForm">
<div fieldContainer label="'Field 1'" model="'field1'">
<input type="text" ng-model="field1" required/>
</div>
<div fieldContainer label="'Field 2'" model="'field2'">
<input type="text" ng-model="field2" required/>
</div>
</form>
field_container.html
<div class="row">
<div class="col-md-5">{{label}}</div>
<div class="col-md-5" ng-transclude></div>
<div class="col-md-2" ng-show="valid">REQUIRED!</div>
</div>
parent_form.js
angular.module('myApp')
.controller('myForm', function ($scope) {
$scope.field1 = '';
$scope.field2 = '';
})
.directive('fieldContainer', function () {
return {
restrict: 'A',
templateUrl: 'field_container.html',
transclude: true,
scope: {
label: '=label',
model : '=model'
}
}
});
I got it working by:
Adding a name to the field (so that Angular's Form validation takes over)
Setting ng-show on the validation message to be: $parent.myForm[model].$valid
So apparently the transcluded input still uses the parent (myForm) scope and the directive has access to the parent's scope via scope.$parent.
Not sure if this is the cleanest way to do it...
Final code looks like:
parent_form.html
<form name="myForm">
<div fieldContainer label="'Field 1'" model="'field1'">
<input type="text" name="field1" ng-model="field1" required/>
</div>
<div fieldContainer label="'Field 2'" model="'field2'">
<input type="text" name="field2" ng-model="field2" required/>
</div>
</form>
field_container.html
<div class="row">
<div class="col-md-5">{{label}}</div>
<div class="col-md-5" ng-transclude></div>
<div class="col-md-2" ng-show="$parent.myForm[model].$valid">REQUIRED!</div>
</div>

Using ng-repeat in directive causing form validation not working properly

In html,
<form name="textform" novalidate>
<cms-radio name="color" require="true" option="[{title:'Red', value:'red'},{title:'Orange', value:'orange'},{title:'Green', value:'green'}]" ng-model="color"></cms-radio>
</form>
In JS,
angular.module('cmsRadio',[]).directive('cmsRadio', function(){
'use strict';
return {
restrict: 'E',
scope: {
name:'#',
require:'=',
option:"=",
bindedModel: "=ngModel"
},
replace:true,
templateUrl: 'radio.html'
};
});
In radio.html
<div class="form-group" ng-form="{{name}}" ng-class="{'has-error':{{name}}.$invalid && {{name}}.$dirty}" >
<div class="radio" ng-repeat='item in option'>
<label><input type="radio" name="{{name}}" ng-model="bindedModel" value="{{item.value}}" ng-required="require">{{item.title}}</label>
</div>
<span class="has-error" ng-show='{{name}}.$dirty && {{name}}.$invalid' ng-message='{{name}}.$error'>
<p class="control-label" ng-messsage='require'>{{name}} is required.</p>
</span>
</div>
When I click on the first radio button, it shows the error as follow.
The error disappeared only if I clicked on the three radio buttons. How to prevent the error appeared when only one of the radio button is clicked instead of three? Anyone could help?
EDIT: MY SOLUTION
In html,
<cms-radio label="Color" name="color" require="true" option="[{'answers':[{'title':'Red', 'value':'red'},{'title':'Orange', 'value':'orange'},{'title':'Green', 'value':'green'}],'selectedAnswer':null}]" id="color" class=""></cms-radio>
In JS,
angular.module('cmsRadio',[]).directive('cmsRadio', function(){
'use strict';
return {
restrict: 'E',
scope: {
name:'#',
require:'=',
option:"="
},
replace:true,
templateUrl: 'radio.html'
};
});
In radio.html
<div class="form-group" ng-form="{{name}}" ng-class="{'has-error':{{name}}.$invalid && {{name}}.$dirty}" >
<div ng-repeat="item in option">
<div class="radio" ng-repeat="answer in item.answers">
<label><input type="radio" name="{{name}}" ng-model="item.selectedAnswer" value="{{answer.value}}" ng-required="require">{{answer.title}}</label>
</div>
</div>
<span class="has-error" ng-show='{{name}}.$dirty && {{name}}.$invalid' ng-message='{{name}}.$error'>
<p class="control-label" ng-messsage='require'>{{name}} is required.</p>
</span>
</div>
I would not be interpolating the field names in the ng-show and ng-class directives.
Rather than duplicating scope.name for both the form AND the input names, try giving the form a fixed name (e.g. 'radioList') e.g.:
<div class="form-group" ng-form="radioList" ng-class="{'has-error':radioList[name].$invalid && radioList[name].$dirty}" >
<div class="radio" ng-repeat='item in option'>
<label><input type="radio" name="{{name}}" ng-model="bindedModel" value="{{item.value}}" ng-required="require">{{item.title}}</label>
</div>
<span class="has-error" ng-show='radioList[name].$dirty && radioList[name].$invalid' ng-message='radioList[name].$error'>
<p class="control-label" ng-messsage='require'>{{name}} is required.</p>
</span>
</div>
UPDATE
The answer above was misguided. The issue had nothing to do with the interpolation of name. It was simply that the ng-repeat was creating a child scope and the ng-model did not have a '.' in it, and therefore each child scope was getting its own copy of bindedmodel.
If the directive used the controllerAs option this would not be a problem. However, the simplest solution here where we are using scope directly is to compensate for the child scope by using $parent as below:
<div class="form-group" ng-class="{'has-error':{{name}}.$invalid && {{name}}.$dirty}" >
<div class="radio" ng-repeat='item in option'>
<label><input type="radio" name="{{name}}" ng-model="$parent.bindedModel" value="{{item.value}}" ng-required="require && !bindedModel">{{item.title}}</label>
</div>
<span class="has-error" ng-show="{{name}}.$invalid && {{name}}.$dirty" ng-messages='{{name}}.$error'>
<p class="control-label" ng-messsage='require'>{{name}} is required.</p>
</span>
</div>
Updated plunkr

Bootstrap form validation with Angular directive

I am able to get the form-control class from Bootstrap to behave well within a single HTML file. But when the form-control is defined from within a directive, the flags like $dirty are not being recognized. The class ng-dirty does appear.
HTML using directive:
<form class="form-horizontal" role="form" name="formExpense">
<div frm-item ng-repeat="f in formdata2"></div>
</form>
Directive:
app.directive('frmItem', function() {
return {
restrict: 'A',
templateUrl: 'form.html'
};
});
Template:
<div class="form-group" ng-class="{'has-error':formExpense.{{f.name}}.$dirty}">
<div class="col-md-6 text-right">
<label for="{{f.name}}-entry">{{f.name}}</label>
</div>
<div class="col-md-6">
<input type="text" name="{{f.name}}" ng-model="f.amount"
class="form-control" placeholder="per month">
</div>
</div>
$scope:
$scope.formdata2 = [{'name': 'housing'},{'name': 'medical'}];
Plunk: http://plnkr.co/JcanBWWTphGdL18v6NxZ

Resources