I came across this nice datepicker on this site: http://luisfarzati.github.io/ng-bs-daterangepicker/. It works fine here : http://plnkr.co/edit/qmj5urjBb4TdtUYCuwap?p=preview
However I would like to make a directive to reuse in my site:
app.directive('dateRange', function () {
return {
restrict: 'E',
templateUrl: 'picker.html'
};
});
picker.html:
<link rel="stylesheet" href="http://luisfarzati.github.io/ng-bs-daterangepicker/bower_components/bootstrap-daterangepicker/daterangepicker-bs3.css"/>
<script src="http://luisfarzati.github.io/ng-bs-daterangepicker/bower_components/momentjs/moment.js"></script>
<script src="http://luisfarzati.github.io/ng-bs-daterangepicker/bower_components/bootstrap-daterangepicker/daterangepicker.js"></script>
<script src="http://luisfarzati.github.io/ng-bs-daterangepicker/ng-bs-daterangepicker.js"></script>
<input type="daterange" ng-model="dates4" ranges="ranges">
I used my directive in an index.html:
<date-range></date-range>
Even though it works in the 1st plunkr I cant get it too work as a directive. Why am I getting this error:
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.3.0-beta.14/$injector/modulerr?p0=filtersampl….com%2Fajax%2Flibs%2Fangularjs%2F1.3.0-beta.14%2Fangular.min.js%3A18%3A139)
plunkr:http://plnkr.co/edit/nr8iOIpIDSf26DxiWEJF?p=preview
I have another code for datepicker try this if you found helpful,
datepicker.directive('datepicker', function($compile,$modal) {
return {
restrict: "A",
link: function(scope, element, attr) {
element.addClass("datepicker-input");
element.after("<span class='datepicker-icon'><i class='fa fa-calendar'></i></span>");
if (attr.withmore) {
element.addClass("withMore");
element.next(".datepicker-icon").after('<i class="fa fa-search-plus"></i>');
$compile(element.next(".datepicker-icon").next('.more-datepicker-icon'))(scope);
}
element.datepicker({
format: attr.format ? attr.format : 'dd-mm-yyyy',
autoclose: true,
todayHighlight: true
});
scope.selectdaterange = function(){
scope.modalInstance = $modal.open({
template : '<div class="modal-header">'+
'<h3 class="modal-title">Select Daterange</h3>'+
'</div>'+
'<div class="modal-body">'+
'You have not selected any News to delete. Please select at least one News to delete.'+
'</div>'+
'<div class="modal-footer">'+
'<button class="btn btn-important" ng-click="deleteCancel()">OK</button>'+
'</div>',
scope : scope
});
}
}
};
});
Related
I'm trying to write a custom directive to replace similar buttons on my page. But when I move ng-class into directive's template, it's not working anymore. Is it wrong to include ng-class within custom directive? Should I use addClass and removeClass in link function instead?
html:
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
directive
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
link: function(scope, ielem, iattrs) {
},
template:
'<button ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
}
})
try use this. change class to ng-class in your template.
you pass a model to directive for text in view while it is not 2 way data binding.
template:
'<button class="active-button" ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
// Code goes here
var app = angular
.module('MyApp', [])
.controller('Main', ['$scope',
function($scope) {
var vm = this;
vm.selectedRows = 4;
vm.deleteDialog = function() {
console.log(vm.selectedRows);
vm.selectedRows = 0;
}
}
])
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
controller: "Main as ctrl",
link: function(scope, ielem, iattrs) {
},
template: '<button ng-class="ngclass" >' +
'<p>{{text}}</p>' +
'</button>'
}
});
.active-button {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="main-content" ng-app="MyApp" ng-controller="Main as ctrl">
<div>
<dt-button ngclass="{'active-button':ctrl.selectedRows >=1}" ng-click="ctrl.deleteDialog()" text="delete"></dt-button>
</div>
</div>
I think nothing wrong with your approach to put ng-class at template of directive. I have tried to reproduce your code snippet at this plunk it is give the correct class name active-button which i defined at style.css with background color blue. But because i don't know much about expression selectedRows.length >=1 on your ngclass attribute, i make it just to true value which will always give active-button class to the element. When you change it to false, it will remove the active-button class.
My guess is seem something wrong with your expression selectedRows.length >=1. At following element declaration :
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
Maybe you can check by bind those expression return value to the element with double curly brace or any other way.
Small correction for your code, you may need to put semicolon ( ; ) at the end of return keyword inside .directive().
Try This
jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.selectedRows = [0];
$scope.tablebuttons = {delete:"Delete"};
$scope.deleteDialog = function(){
$scope.selectedRows = [];
}
});
jimApp.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
myClass: '#'
},
link: function(scope, ielem, iattrs) {
console.log(scope.myClass);
},
template:
'<button class="{{myClass}}">' +
'<span class="{{icon}}"></span>' +
'{{text}}' +
'</button>'
}
})
.active-button{
background:red;
}
.inactive-button{
background:#ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
<dt-button my-class="{{selectedRows.length?'active-button':'inactive-button'}}" text="{{tablebuttons.delete}}" icon="v-delete" ng-click="deleteDialog()"></dt-button>
</div>
Question is simple - why this work like this work?
angular.module('test1', [])
.directive('myDir', function() {
return {
replace:true,
restrict: 'E',
template: '<input type="text">',
};
}).directive('nogo', function() {
return {
replace:true,
restrict: 'E',
template: '<div><input type="text"></div>',
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="test1">
<my-dir ng-model="test"></my-dir>
<nogo ng-model="test"></nogo>
<input type="text" ng-model="test"/>
</body>
The only difference between two directive is that template for second wrapped with 'div'.
But one work, another doesn't.
Really - I don't understand - why working one is on. But it is.
the problem is ng-model does not work on div.
change :
.directive('nogo', function() {
return {
replace:true,
restrict: 'E',
template: '<div><input type="text"></div>',
};
});
to
.directive('nogo', function() {
return {
replace:true,
restrict: 'E',
template: '<div><input ng-model='test' type="text"></div>',
};
});
dont forget to remove ng-model from <nogo>*remove ng-model*</nogo>
it is working..
Your problem here is that you are applying the model to the <div>, here is another question with more information about this problem.
And in this particular case you can change the bind into the template in order to make it work.
angular.module('test1', [])
.directive('myDir', function() {
return {
replace:true,
restrict: 'E',
template: '<input type="text">',
};
}).directive('nogo', function() {
return {
replace:true,
restrict: 'E',
template: '<div><input type="text" ng-model="test"></div>',
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="test1">
<my-dir ng-model="test"></my-dir>
<nogo ng-model="test"></nogo>
<input type="text" ng-model="test"/>
</body>
Also on the ng-model docs says:
The ngModel directive binds an input,select, textarea (or custom form control)
I have validation set up, and am using ngMessages to show errors. However I want to create a wrapper directive around ngMessages that adds some HTML (to avoid a lot of code duplication).
So instead of having to write this on each input:
<div class="help-block validator">
<div ng-messages="registerForm.email.$error" ng-if="registerForm.email.$touched">
<p ng-message="required">This field is required.</p>
</div>
</div>
I'd just write something like this:
<error-message messages='{"required": "This field is required."}' error="registerForm.email.$error" touched="registerForm.email.$touched"></error-message>
The issue with my directive is that error and touched come up as strings, they don't get evaluated as JS expressions. I've tried to $eval them, but that throws an error.
Here's my directive:
angular
.module('myApp')
.directive("errorMessage", function () {
return {
restrict: "E",
replace: true,
scope: {
'messages': '=',
'error': '=',
'touched': '='
},
template: '<div class="help-block validator">' +
'<div ng-messages="error" ng-if="touched">' +
'<div ng-repeat="(key, message) in messages">' +
'<p ng-message="key">{{message}}</p>' +
'</div>' +
'</div>' +
'</div>',
link: function (scope, elem, attrs) {
scope.error = attrs.error;
scope.touched = attrs.touched;
scope.messages = scope.$eval(attrs.messages); // this works
}
};
});
Any idea how I should go about doing this?
Found the issue. Looks like attrs wasn't what I needed. The properties were already in the scope. The code I ended up with is:
angular
.module('myApp')
.directive("errorMessage", function () {
return {
restrict: "E",
replace: true,
scope: {
'messages': '=',
'error': '=',
'touched': '='
},
template: '<div class="help-block validator">' +
'<div ng-messages="error" ng-if="touched">' +
'<div ng-repeat="(key, message) in messages">' +
'<p ng-message="{{key}}">{{message}}</p>' +
'</div>' +
'</div>' +
'</div>',
link: function (scope, elem, attrs) {
}
};
});
I think ng-message-include meets your requirements. we can create new html file and place all of our messages in this file and just call it with ng-messages-include.
hope my answer is usable for you.
I write my custom directive for bootstrap popover, but face some trouble.
This is the code:
angular.module('CommandCenterApp')
.directive('bzPopover', function($compile,$http, $commandHelper) {
return{
restrict: "A",
replace: false,
scope: {
currencies:"=data",
selected:"=selected"
},
link: function (scope, element, attrs) {
var html = '<div class="currency-popup">' +
'<span class="select-label">Select currency:</span>'+
'<select class="custom-select" ng-model="selected" ng-options="currency.CurrencyName for currency in currencies track by currency.CurrencyId">' +
'</select>' +
'<button class="btn btn-green" ng-click="saveCurrency()">Save</button>'+
'</div>';
var compiled = $compile(html)(scope);
$(element).popover({
content:compiled,
html: true,
placement:'bottom'
});
scope.saveCurrency = function () {
var obj = {
Currency:scope.selected,
venueId: $commandHelper.getVenueId()
}
$http.post("/api/currencyapi/changecurrency", obj).success(function() {
scope.$emit('currencySaved', scope.selected);
});
//$(element).popover('hide');
}
scope.$watch('selected', function() {
console.log(scope.selected);
});
}
}
});
When I first time invoke popover all works fine, I click on button and it trigger scope.saveChanges function. Then I close popover and invoke it again, and directive doesnt work anymore.
In markup popover present as:
<a bz-popover data="controller.currencies" selected="controller.selectedCurrency" class="change-currency hidden-xs hidden-sm" href>Change currency</a>
Can anyone help me with this?
UPDATE: it looks like all bindings(scope.saveCurrency,watched on selected property) stop working after popover hidding.
Not really sure if this is the problem you're describing because in my fiddle I had to click twice on the button to show the popover after closing the popover.
I don't know what's the problem but with trigger: 'manual' and binding to click event it is working as expected.
Please have a look at the demo below or in this jsfiddle.
I've commented some of your code because it's not needed to show the popover behaviour and also the ajax call is not working in the demo.
angular.module('CommandCenterApp', [])
.controller('MainController', function() {
this.currencies = [{
CurrencyId: 1,
CurrencyName: 'Dollar'},{
CurrencyId: 2,
CurrencyName: 'Euro'}];
})
.directive('bzPopover', function($compile,$http) { //, $commandHelper) {
return{
restrict: "A",
replace: false,
scope: {
currencies:"=data",
selected:"=selected"
},
link: function (scope, element, attrs) {
var html = '<div class="currency-popup">' +
'<span class="select-label">Select currency:</span>'+
'<select class="custom-select" ng-model="selected" ng-options="currency.CurrencyName for currency in currencies track by currency.CurrencyId">' +
'</select>' +
'<button class="btn btn-green" ng-click="saveCurrency()">Save</button>'+
'</div>';
var compiled = $compile(html)(scope);
$(element).popover({
content:compiled,
html: true,
placement:'bottom',
trigger: 'manual'
});
$(element).bind('click', function() {
$(element).popover('toggle');
});
scope.saveCurrency = function () {
var obj = {
Currency:scope.selected,
venueId: 1//$commandHelper.getVenueId()
}
$http.post("/api/currencyapi/changecurrency", obj).success(function() {
scope.$emit('currencySaved', scope.selected);
});
$(element).popover('hide');
}
scope.$watch('selected', function() {
console.log(scope.selected);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css" rel="stylesheet"/>
<div ng-app="CommandCenterApp" ng-controller="MainController as controller">
<button bz-popover data="controller.currencies" selected="controller.selectedCurrency" class="change-currency hidden-xs hidden-sm">Change currency</button>
</div>
Shameless self-promotion here, but you may want to take a look at the Angualr UI Bootstrap library as we've already done this for you. And even if you don't want to use it, you can just grab the code you need...
I have a form based on twitter bootstrap, each field have it's own configuration
// controller (the template shows this in ng-repeat
$scope.fields = [{name:"f1", label:"Field 1", with_button: false},
{name:"f2", label:"Field 2", with_button: true}]
I'm trying to make a "conditional directive" that customize the template according to "field.with_button"
// Without button
<div class="controls">
<input type="text" id="i_{{field.name}}">
</div>
// With button
<div class="controls">
<div class="input-append">
<input type="text" id="i_{{field.name}}">
<span class="add-on">bt</span>
</div>
</div>
I searched a lot and didn't find any solution, I tried to create only one div and put contents inside with a compiler function but it didn't parse, and if I call $apply it crashes.
How could I make this directive?
wrong My last try:
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls">{{innerContent}}</div>',
controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
$scope.$eval('$scope.innerContent = \'<input type="text" id="input_{{field.name}}" placeholder="{{field.name}}" class="input-xlarge">\'');
}]
};
});
//<ss-field field="{{field}}"></ss-field>
You can use the $http and $compile services to do what you're after.
http://plnkr.co/edit/Xt9khe?p=preview
This plnkr should demostrate what needs to be done, but basically:
Use $http to load the template depending on the condition.
Compile the loaded template against the current scope with $compile.
angular.module('mymodule',[]).directive('ssField', ['$http', '$compile', function($http, $compile) {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls"></div>',
link: function(scope, element, attrs) {
var template;
var withButtonTmpl = 'with_button.html';
var withoutButtonTmpl = 'without_button.html';
if (scope.field.with_button) {
$http.get(withButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
} else {
$http.get(withoutButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
}
}
};
}]);
You can change the directive to be more robust so the URLs aren't directly embedded in the directive for re-usability, etc., but the concept should be similar.
Just to further expand on Cuing Vo's answer here is something similar to what I use(without using external partials and additional $http calls):
http://jsfiddle.net/LvUdQ/
myApp.directive('myDirective',['$compile', function($compile) {
return {
restrict: 'E',
template: '<hr/>',
link: function (scope, element, attrs, ngModelCtrl) {
var template = {
'templ1':'<div>Template 1</div>',
'templ2':'<div>Template 2</div>',
'default':'<div>Template Default</div>'
};
var templateObj;
if(attrs.templateName){
templateObj = $compile(template[attrs.templateName])(scope);
}else{
templateObj = $compile(template['default'])(scope);
}
element.append(templateObj);
}
};
}]);
However Im not quite sure its by the bible from performance perspective.
In AngularJS, directly manipulate the DOM must only be a last resort solution. Here, you can simply use the ngSwitch directive :
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template:
'<div class="controls" data-ng-switch="field.with_button">' +
'<input type="text" id="i_{{field.name}}" data-ng-switch-when="false">' +
'<div class="input-append" data-ng-switch-default>' +
'<input type="text" id="i_{{field.name}}">' +
'<span class="add-on">bt</span>' +
'</div>' +
'</div>',
};
});