Angular directive issue - angularjs

For few days I was trying to debug why my directive doesn't work. No event has been fired when I press a button. Finally I found which line breaks everything!
Inside template html I have line datepicker-popup ng-model="{{model}}" min-date="{{minDate}}" is-open="{{isOpened}}" if I remove it everything works well. But it is essential part in my custom directive and I want to keep it. I assume problem is that I am using directive inside custom directive?
Could you please help me to identify problem and find a correct solution?
Thanks for any help!
Directive:
(function(){
function directive(){
return {
scope :{
model:'=model',
minDate:'=minDate',
isOpened:'=isOpened'
},
restrict: 'E',
templateUrl: 'templates/datepicker/datepicker.html',
controller: 'Ctrl'
};
};
app.directive('myDirective', directive);
})();
controller:
(function(){
function Controller($scope) {
$scope.open = function() {
alert('HELLO');
};
app.controller('Ctrl', Controller);
})();
template html:
<fieldset>
<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="open()" class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>

As mentioned in the other answer, you don't need the curly braces in your template because it's a two way binding.
The problem with your click function is probably the isolated scope of your custom directive.
If you'd like to have the open method in your main controller you could pass it to the isolated scope.
Please find below a demo of your directive or here at jsfiddle.
angular.module('demoApp', ['ui.bootstrap'])
.controller('mainController', Controller)
.directive('customDir', Directive);
function Directive() {
return {
scope: {
model: '=',
minDate: '=',
isOpened: '='
},
transclude: true,
restrict: 'E',
templateUrl: 'templates/datepicker/datepicker.html',
//controller: 'Ctrl'
controller: function($scope) {
$scope.open = function() {
console.log('open popup now!!');
$scope.isOpened = true;
};
}
};
}
function Controller($scope) {
$scope.open = function () {
alert('HELLO'); // not called becasue of isolated scope of custom directive
};
$scope.dateModel = {
date: new Date(),
min: new Date()
};
$scope.isOpened = false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.2/ui-bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.2/ui-bootstrap-tpls.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-app="demoApp" ng-controller="mainController">
<script type="text/ng-template" id="templates/datepicker/datepicker.html">
<fieldset>
<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="open()" class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>
</script>
<custom-dir model="dateModel.date" min-date="dateModel.min" is-opened="isOpened"></custom-dir>
</div>

When you define your directive's scope properties with a = symbol, you don't need to use {{}} in your views.
Remove the {{}} from your view:
<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" />
Here's more info regarding directive's scope properties using =, & and #.

Related

Angular transcluded directive - can't access a model thats inside of it

This is my directive code:
'use strict';
demo.directive('myModal', function($parse) {
return {
restrict: 'A',
transclude: true,
scope: '#',
template: '<div ng-transclude><h4>Please enter value</h4></div>'
}
});
Usage is as follows:
<!-- myModal directive -->
<div my-modal>
<input type="text" ng-model="myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>
And my main controller, which wraps the whole application, includes this:
demo.controller('MainCtrl', function($scope) {
$scope.getMyTest = function(){
alert($scope.myTest);
}
});
Any ideas why can't I access myTest?
JsFiddle: http://jsfiddle.net/sZZEt/679/
You should use the dot-notation:
demo.controller('MainCtrl', function($scope) {
$scope.data = {};
$scope.getMyTest = function(){
alert($scope.data.myTest);
}
});
and
<div my-modal>
<input type="text" ng-model="data.myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>
JSFIDDLE
Transclusion creates a child scope, that's why you should use the dot-notation for ng-model.
try this. add directive to model element.
<div>
<input my-modal type="text" ng-model="myTest" />
<input type="button" ng-click="getMyTest()" value="Get Value" />
</div>

ng-Template CSS not working

Using AngularJS ngDialog.
Expecting that the CSS in the document is applied to the ngDialog, but it hwas not applied.
Code:
<script type="text/ng-template" id="attach_account_template">
<div id="attach_account_div" ng-controller="Opportunity_Controller" style="width:250px">
<input type="text" class="form-control" id="account_name" style="width:200px;height:35px" />
<input id="btn_account" type="button" style="margin-left:10px;height:35px;width:50px" value="GO" />
</div>
</script>
$scope.attach_account = function () {
//attach_account_template
ngDialog.open({ template: 'attach_account_template', scope: $scope });
};

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

Angular JS pass attributes to directive template

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>

Angular-UI multiple datepickers insider form controller

I am creating a form with multiple angular-ui datepickers and some input data.
For the datepickers I have created a controller and a parent form controller like the sample given below. The form controller has the model which includes the datepicker dates.
JS:
var app = angular.module('app', ['ui.bootstrap']);
app.controller('dateCntrl', function($scope,$timeout){
$scope.open = function() {
$timeout(function() {
$scope.opened = true;
});
};
});
app.controller('formCntrl', function($scope, $http){
$scope.model = {name:'', startDate:'', endDate:''};
});
HTML:
<form ng-controller="formCntrl">
<input type="text" id="name" placeholder="Name" ng-model="model.name" />
<div ng-controller="dateCntrl">
<input datepicker-popup="dd-MMMM-yyyy" ng-model="model.startDate" id="startDate" type="text" />
<button class="btn" ng-click="open()"><i class="icon-calendar"></i></button>
</div>
<div ng-controller="dateCntrl">
<input datepicker-popup="dd-MMMM-yyyy" ng-model="model.endDate" id="endDate" type="text" />
<button class="btn" ng-click="open()"><i class="icon-calendar"></i></button>
</div>
</form>
Am I going the right way in having a separate controller for the datepicker. This will act as a common controller for all the date inputs
If yes, is it possible to have a generic way of binding the data in the datepicker controller back to the model dates(model.startDate,model.endDate in this case) in the parent controller.
Is there a alternative way to go about this.
Thanks and regards.
Should have read more about the scope inheritance
The parent scope values can be accessed using $parent
<form ng-controller="formCntrl">
<input type="text" id="name" placeholder="Name" ng-model="model.name" />
<div ng-controller="dateCntrl">
<input datepicker-popup="dd-MMMM-yyyy" ng-model="$parent.model.startDate" id="startDate" type="text" />
<button class="btn" ng-click="open()"><i class="icon-calendar"></i></button>
</div>
<div ng-controller="dateCntrl">
<input datepicker-popup="dd-MMMM-yyyy" ng-model="$parent.model.endDate" id="endDate" type="text" />
<button class="btn" ng-click="open()"><i class="icon-calendar"></i></button>
</div>
</form>
I took all the code from the verbose example here: http://angular-ui.github.io/bootstrap/#/datepicker & wrapped it into my own directive. This way I can just drop unlimited datepickers into my page, and specify the model for each one to bind to. I do not have to manage passing repetitive settings, or setting up unique variables for tracking the "open" status anymore, I just put 1 line of code:
<div my-date-picker my-date-picker-model="myDate1"></div>
<div my-date-picker my-date-picker-model="myDate2"></div>
<div my-date-picker my-date-picker-model="myDate3"></div>
The user can then toggle each date picker open/closed, and the values will be updated into myDate1, myDate2, & myDate3 appropriately. The open/closed status is now encapsulated within the directive, and out of mind.
To implement the directive, I copied the 'JS' tab's code into it's controller, and I copied the 'Markup' tab's code into it's template. At the end I added 1 bit of code to update the value to the parent scope:
$scope.$watch('dt', function(newVal, oldVal) {
$scope.myDatePickerModel = newVal;
});
At the start of the controller, I changed $scope.today to initialize the value from the parent scope, instead of using the system clock:
$scope.init = function() {
$scope.dt = $scope.hxDatePickerModel;
};
$scope.init();
The directive uses an isolate scope, and 2-way binding on the attribute which defines the parent scope's model:
scope: {
myDatePickerModel: '='
}
Here's the full code of the directive:
app.directive('myDatePicker', function() {
function link(scope, element, attrs) {
}
function controller($scope) {
$scope.init = function() {
$scope.dt = $scope.myDatePickerModel;
};
$scope.init();
$scope.clear = function () {
$scope.dt = null;
};
// Disable weekend selection
$scope.disabled = function(date, mode) {
return ( mode === 'day' && ( date.getDay() === 0 || date.getDay() === 6 ) );
};
$scope.toggleMin = function() {
$scope.minDate = $scope.minDate ? null : new Date();
};
$scope.toggleMin();
$scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1
};
$scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
$scope.format = $scope.formats[0];
$scope.$watch('dt', function(newVal, oldVal) {
$scope.myDatePickerModel = newVal;
});
}
return {
restrict: 'A',
templateUrl: 'datepicker.html',
link: link,
controller: controller,
scope: {
myDatePickerModel: '='
}
}
});
And here is the full code of datepicker.html, the template for this directive:
<p class="input-group">
<input type="text" class="form-control" datepicker-popup="{{format}}" ng-model="dt" is-open="opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<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>
other solution , declare the datepicker's open() method in $rootScope and it is available entire application .
Html :
<div ng-app="myApp">
<div ng-controller="DemoController">
<div>
<input type="text" name="salesEndDate" id = "salesEndDate" datepicker-popup="dd-MM-yyyy" ng-model="salesEndDate" datepicker-options="dateOptions"/>
<button id="salesEndDateCal" ng-click="datePickerOpen('salesEndDate')"><i class="icon-calendar"></i></button>
</div>
<div>
<input type="text" name="salesStartDate" id = "salesStartDate" datepicker-popup="dd-MM-yyyy" ng-model="salesStartDate" datepicker-options="dateOptions"/>
<button id="salesEndDateCal" ng-click="datePickerOpen('salesStartDate')"><i class="icon-calendar"></i></button>
</div>
</div>
</div>
Javascript :
var myApp = angular.module('myApp',['ui.bootstrap','ui.bootstrap.datepicker']);
function DemoController($scope,$timeout,$rootScope) {
$rootScope.datePickerOpen = function(id) {
$timeout(function() {
$("#"+id).focus();
});
};
}
jsfiddle link http://jsfiddle.net/angles_k/s7yZm/21/

Resources