Angular JS pass attributes to directive template - angularjs

I have custom directive for datepicker. I want to reuse it in several different places. But in order to reuse current directive I have to dynamically pass and change different attributes into my-datepicker directive.
If you look inside datepicker.html I am using following attributes: ng-model="departureDate" min-date="minDateDeparture" is-open="departureOpened".
Question: How do I set this attributes on the my-datepicker element level and pass all the way down to my directive html template? I want to achieve something like that:
<my-datepicker ng-model="departureDate1" min-date="minDateDeparture1" is-open="departureOpened1"></my-datepicker>
<my-datepicker ng-model="departureDate2" min-date="minDateDeparture2" is-open="departureOpened2"></my-datepicker>
Thanks for any help!
datepicker-contoller.js
app.directive('myDatepicker', function() {
return {
restrict: 'E',
templateUrl: 'templates/datepicker/datepicker.html'
};
});
datepicker.html
<fieldset>
<div class='input-group'>
<input type="text" class="form-control" datepicker-popup ng-model="departureDate" min-date="minDateDeparture" is-open="departureOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span ng-click="open1($event)" class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>
Datepicker usage
<div ng-controller="MyController">
<div class="col-md-2">
<my-datepicker></my-datepicker>
</div>
<div class="col-md-2">
<my-datepicker></my-datepicker>
</div>
</div>

Update: See this jsFiddle: http://jsfiddle.net/j31ky7c2/
You can pass the data as well as functions as attribute in your directive.
<my-datepicker min-date="minDateDeparture2" is-open="departureOpened2" some-function="testFunction()"></my-datepicker>
You can receive this data in your directive's scope.
directive('myDatepicker', [function() {
return {
restrict: 'E',
scope: {
minDate: '#',
isOpen: '#',
someFunction: '&'
},
link: function(scope, elm, attrs) {
}
}
}]);
Then you can simply use minDate and isOpen and someFunction in your directive template like:
<div ng-bind="{{::minDate}}"></div>
<div ng-bind="{{::isOpen}}"></div>
<Button ng-click="someFunction()">Click me</Button>

Related

Creating a wrapper directive for Bootstrap-UI's uib-datepicker-popup

I'm trying to create a wrapper AngularJS directive for Bootstrap-UI's uib-datepicker-popup so I don't have to recreate a bunch of boilerplate each time I need to select a date. I've been working off an example I found here which was written for an earlier version of Angular, and am running into some oddities getting this working.
I've gotten the directive to a point where it displays a popup, however the two-way data binding seems to be broken; the date value in the field's model doesn't propagate into the directive, and when you click on the popup and select a date, it doesn't propagate back out. Does anyone have any ideas on what's going on here?
I've created a Plunker demonstrating the issue here.
Directive code:
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope: {
model: "=",
myid: "#"
},
templateUrl: 'datepicker.html',
require: 'ngModel',
link: function(scope, element) {
scope.popupOpen = false;
scope.openPopup = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.popupOpen = true;
};
scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.opened = true;
};
}
};
});
Template code:
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" id="{{myid}}" uib-datepicker-popup ng-model="model" is-open="opened" ng-required="true" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
You're using ng-model in the directive code, but you're looking for model in the directive template.
<my-datepicker ng-model="selected" myid="someid"></my-datepicker>
And here you are looking for an attribute called model:
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope: {
//this line should be ngModel
model: "=",
myid: "#"
},
Here's a working plunker: https://plnkr.co/edit/s5CT4xGqXtUxgCH8vw8q?p=preview
In general, I try to avoid using names like "model" and "ng-model" on custom directives, as built-in angular attributes should be distinguished from custom attributes.

Angular modal directive - issue when having more than 1 in a page

I made a modal directive for my angular app.
modal-directive.js
'use strict';
backyApp.directive('appModal', function() {
return {
restrict: 'A',
transclude: true,
link: function($scope, elem, attr){
$scope.modalClass = attr.appModal;
},
scope: '#',
templateUrl: './components/modal/modal.html'
};
});
and the template looks like this: (modal.html)
<!-- Modal -->
<div class="app-modal" ng-class="modalClass">
<div ng-transclude></div>
</div>
Now, let's pretend we have 2 modals in a page:
<div app-modal="firstModal">
<div class="form-group">
<input type="text" />
<input type="submit" value="Submit" />
</div>
</div>
</div>
<div app-modal="secondModal">
<div class="form-group">
<input type="text" />
<input type="submit" value="Submit" />
</div>
</div>
</div>
Problem: I end up with 2 modals having the same class (in my example above, secondModal will be attached to 2 of my modals)
Why does this happen? I need the value of my directive to be attached to each modal because thats the only way I can open the one I want.
I know this is horrible explanation Let me know if you have any question
Edit:
I want to have 2 app-modal divs, each one having its directive value as a class attached to it. Hope it's more clear now.
Use an isolated scope in the directive
backyApp.directive('appModal', function() {
return {
restrict: 'A',
transclude: true,
link: function($scope, elem, attr){
$scope.modalClass = attr.appModal;
},
scope: {},
templateUrl: './components/modal/modal.html'
};
});
Here is a plunker I did for this
https://embed.plnkr.co/UwjBIqTh5fNlAcbIs6TS/

Input value from a specialized controller isn't being sent back to the main controller

I'm building an application that's making use of Angular UI Bootstrap's datepicker in several spots. I've included some other enhancements to this, such as validation styling, and wanted to propagate it everywhere so I put it in a directive.
app.controller('DateController', function () {
var vm = this;
vm.status = {
opened: false
};
vm.open = open;
function open($event) {
vm.status.opened = true;
}
});
app.directive('customDatePicker', function () {
return {
restrict: 'E',
scope: {
name: '=',
bindingProperty: '=',
minDate: '=',
maxDate: '=',
maxMode: '=',
format: '=',
isRequired: '=',
form: '='
},
templateUrl: "/Scripts/SharedAngular/Templates/datePicker.html"
};
});
With the template URL code looking like:
<div ng-controller="DateController as datePicker">
<p class="input-group">
<input type="text" id="{{ name }}_datePicker" name="{{ name }}_datePicker" class="form-control" max-mode="maxMode" datepicker-popup="{{format}}" ng-model="bindingProperty" min-date="minDate" max-date="maxDate" ng-required="isRequired" close-text="Close" is-open="datePicker.status.opened" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="datePicker.open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
<span ng-show="form.{{ name }}_datePicker.$invalid && form.{{ name }}_datePicker.$dirty" class="glyphicon glyphicon-exclamation-sign form-control-feedback datepicker-error-icon"></span>
</p>
</div>
I'm including this directive in a couple places. It is placed on a page within the scope of a different controller like this:
<div ng-controller="myController as ctrl">
<custom-date-picker name="'somePrefix'" form="myForm" binding-property="ctrl.date" max-mode="'month'" max-date="ctrl.currentDate" format="'yyyy/MM/dd'" is-required="true"></custom-date-picker>
</div>
So, I want to take a property from myController and two-way bind it to the directive's date picker. However, what ends up actually happening is that the current value for ctrl.date property is passed to the datepicker, but any changes to that value that happen in the directive are not sent back. So, it seems that myController and DateController only pass the value one-way, and only when the directive is loaded onto the page. To be clear, the directive doesn't seem to be the source of my issue, since putting the HTML code from the template directly onto my page results in the same behavior. So, it appears that the issue is specifically due to the use of two different controllers.
Previously, I was using ng-model both in the template code and the directive on my primary page. Neither option seems to work. I'm open to any ideas on how to get the input from my directive to be sent back to the myController.
I figured out how to ditch the DateController entirely. I put my expressions directly into the template elements via ng-click and ng-init. The input creates a property called opened and uses ng-init initializes it to false. It then uses that property for the is-open attribute of the datepicker. Then the button just uses ng-click="opened=true". Definitely simplifies things and removes the nesting of controllers.
Directive:
app.directive('customDatePicker', function () {
return {
restrict: 'E',
scope: {
name: '=',
bindingProperty: '=ngModel',
minDate: '=',
maxDate: '=',
maxMode: '=',
format: '=',
isRequired: '=',
form: '='
},
require: 'ngModel',
templateUrl: "/Scripts/SharedAngular/Templates/datePicker.html"
};
});
Template:
<p class="input-group">
<input type="text" id="{{ name }}_datePicker" name="{{ name }}_datePicker" class="form-control" max-mode="maxMode" datepicker-popup="{{format}}" ng-model="bindingProperty" min-date="minDate" max-date="maxDate" ng-required="isRequired" close-text="Close" is-open="opened" ng-init="opened=false" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="opened=true"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
<span ng-show="form.{{ name }}_datePicker.$invalid && form.{{ name }}_datePicker.$dirty" class="glyphicon glyphicon-exclamation-sign form-control-feedback datepicker-error-icon"></span>
</p>
Using the directive:
<div ng-controller="MyController as ctrl">
<custom-date-picker name="'somePrefix'" form="myForm" ng-model="ctrl.Date" max-mode="'month'" max-date="ctrl.currentDate" format="'yyyy/MM/dd'" is-required="true"></custom-date-picker>
</div>

Angular invoke controller fn from directive isolated template

I want on-click event from directive invoke some function from my controller. But for some reason it doesn't work. I want my datepicker to expand when I event is fired. Could you please help me to investigate what is wrong my in my current build?
app.js
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope :{
model:'=model',
minDate:'=minDate',
isOpened:'=isOpened',
openFunction: '&'
},
templateUrl: 'templates/datepicker/datepicker.html',
link: function(scope, elm, attrs) {
}
};
});
app.controller('FlightDatePickerController', function ($scope) {
$scope.openFunction = function($event, isDepart) {
$event.preventDefault();
$event.stopPropagation();
$scope.departureOpened = true;
};
};
datepicker.html
<fieldset>
<pre>{{model}}</pre>
<div class='input-group'>
<input type="text" class="form-control" datepicker-popup ng-model="{{model}}" min-date="{{minDate}}" is-open="{{isOpened}}" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span ng-click='openFunction({event:event}, {isDepart:isDepart})' class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>
index.html
<div ng-controller="FlightDatePickerController">
<div class="col-md-2">
<my-datepicker model="departureDate" minDate="minDateDeparture" isOpened="departureOpened" open-function="openFunction($event, isDepart)"></my-datepicker>
</div>
</div>
You can add a controller attribute to your directive, in order to bind some function to your template.
In your case, you can do :
Directive
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope :{
model:'=model',
minDate:'=minDate',
isOpened:'=isOpened'
},
templateUrl: 'templates/datepicker/datepicker.html',
controller: 'FlightDatePickerController'
};
});
Datepicker.html
<div ng-controller="FlightDatePickerController">
<div class="col-md-2">
<my-datepicker model="departureDate" minDate="minDateDeparture" isOpened="departureOpened"></my-datepicker>
</div>
</div>
Your overall implementation is correct, but you made couple of mistakes.
ng-click should be like adding parameter in JSON like structure.
ng-click='openFunction({event:$event, isDepart:isDepart})'
& then your directive element should have
open-function="openFunction($event, isDepart)"

How to access a dom element in a Angular directive

I am trying to attach a keyup event to a directive in my Angular project. Here is the directive:
angular.module('clinicalApp').directive('chatContainer', function() {
return {
scope: {
encounter: '=',
count: '='
}
templateUrl: 'views/chat.container.html',
link: function(scope, elem, attrs) {
scope.count = 500;
}
};
});
And here is the html from the template:
<div class="span4 chat-container">
<div class="chat-body">
<form accept-charset="UTF-8" action="#" method="POST">
<div class="text-area-container">
<textarea id="chatBox" class="chat-box" rows="2"></textarea>
</div>
<div class="button-container btn-group btn-group-chat">
<input id="comment" class="btn btn-primary btn-small btn-comment disabled" value="Comment" ng-click="addMessage()"/>
</div>
</form>
</div>
</div>
</div>
I want to access the chatbox in my link function and attach the keyup event to it. I know I can get it with jQuery, but that cannot be the Angular way. What is the proper way to grab that element from the dom?
You can easily do it with Angular' element' find() method:
var chatbox = elem.find("textarea"); // Finding
chatbox.bind("keyup",function(){ // Binding
console.log("KEYUP!")
})
Live example: http://jsfiddle.net/cherniv/S7XdK/
You can use element.find(yourSelector) as previously mentioned, but it is better to use ngKeyUp, similar to how you would use ngClick:
https://docs.angularjs.org/api/ng/directive/ngKeyup

Resources