AngularJS custom datepicker directive - javascript

I would like to make a custom datepicker directive with a custom template.
But I have no idea how to start building it...
How to include date data for my directive?
I appreciate your guide or give me some advice to work on this more precisely.

It might help you! follow the below steps
HTML code sample:
<label>Birth Date</label>
<input type="text" ng-model="birthDate" date-options="dateOptions" custom-datepicker/>
<hr/>
<pre>birthDate = {{birthDate}}</pre>
<script type="text/ng-template" id="custom-datepicker.html">
<div class="enhanced-datepicker">
<div class="proxied-field-wrap">
<input type="text" ui-date-format="yy-mm-dd" ng-model="ngModel" ui-date="dateOptions"/>
</div>
<label>
<button class="btn" type="button"><i class="icon-calendar"></i></button>
<span class="datepicker-date">{{ngModel | date:'d MMM yyyy'}}</span>
</label>
</div>
</script>
JS code sample:
angular
.module('App',['ui.date'])
.directive('customDatepicker',function($compile){
return {
replace:true,
templateUrl:'custom-datepicker.html',
scope: {
ngModel: '=',
dateOptions: '='
},
link: function($scope, $element, $attrs, $controller){
var $button = $element.find('button');
var $input = $element.find('input');
$button.on('click',function(){
if($input.is(':focus')){
$input.trigger('blur');
} else {
$input.trigger('focus');
}
});
}
};
})
.controller('myController',function($scope){
$scope.birthDate = '2013-07-23';
$scope.dateOptions = {
minDate: -20,
maxDate: "+1M +10D"
};
});
/*global angular */
/*
jQuery UI Datepicker plugin wrapper
#note If ≤ IE8 make sure you have a polyfill for Date.toISOString()
#param [ui-date] {object} Options to pass to $.fn.datepicker() merged onto uiDateConfig
*/
angular.module('ui.date', [])
.constant('uiDateConfig', {})
.directive('uiDate', ['uiDateConfig', '$timeout', function (uiDateConfig, $timeout) {
'use strict';
var options;
options = {};
angular.extend(options, uiDateConfig);
return {
require:'?ngModel',
link:function (scope, element, attrs, controller) {
var getOptions = function () {
return angular.extend({}, uiDateConfig, scope.$eval(attrs.uiDate));
};
var initDateWidget = function () {
var showing = false;
var opts = getOptions();
// If we have a controller (i.e. ngModelController) then wire it up
if (controller) {
// Set the view value in a $apply block when users selects
// (calling directive user's function too if provided)
var _onSelect = opts.onSelect || angular.noop;
opts.onSelect = function (value, picker) {
scope.$apply(function() {
showing = true;
controller.$setViewValue(element.datepicker("getDate"));
_onSelect(value, picker);
element.blur();
});
};
opts.beforeShow = function() {
showing = true;
};
opts.onClose = function(value, picker) {
showing = false;
};
element.on('blur', function() {
if ( !showing ) {
scope.$apply(function() {
element.datepicker("setDate", element.datepicker("getDate"));
controller.$setViewValue(element.datepicker("getDate"));
});
}
});
// Update the date picker when the model changes
controller.$render = function () {
var date = controller.$viewValue;
if ( angular.isDefined(date) && date !== null && !angular.isDate(date) ) {
throw new Error('ng-Model value must be a Date object - currently it is a ' + typeof date + ' - use ui-date-format to convert it from a string');
}
element.datepicker("setDate", date);
};
}
// If we don't destroy the old one it doesn't update properly when the config changes
element.datepicker('destroy');
// Create the new datepicker widget
element.datepicker(opts);
if ( controller ) {
// Force a render to override whatever is in the input text box
controller.$render();
}
};
// Watch for changes to the directives options
scope.$watch(getOptions, initDateWidget, true);
}
};
}
])
.constant('uiDateFormatConfig', '')
.directive('uiDateFormat', ['uiDateFormatConfig', function(uiDateFormatConfig) {
var directive = {
require:'ngModel',
link: function(scope, element, attrs, modelCtrl) {
var dateFormat = attrs.uiDateFormat || uiDateFormatConfig;
if ( dateFormat ) {
// Use the datepicker with the attribute value as the dateFormat string to convert to and from a string
modelCtrl.$formatters.push(function(value) {
if (angular.isString(value) ) {
return jQuery.datepicker.parseDate(dateFormat, value);
}
return null;
});
modelCtrl.$parsers.push(function(value){
if (value) {
return jQuery.datepicker.formatDate(dateFormat, value);
}
return null;
});
} else {
// Default to ISO formatting
modelCtrl.$formatters.push(function(value) {
if (angular.isString(value) ) {
return new Date(value);
}
return null;
});
modelCtrl.$parsers.push(function(value){
if (value) {
return value.toISOString();
}
return null;
});
}
}
};
return directive;
}]);
Here is a working fiddle http://jsfiddle.net/FVfSL/.

Related

Problem creating custom validator for an angularjs directive

So I have a custom directive which works fine as it is. This directive is being used at multiple places. This is an element directive.
This element directive has certain attributes. I have added a custom attribute for only 1 instance of this directive i.e. only at 1 particular usage of this directive I have added an extra attribute for this element.
Here is the directive being used in the HTML:
<attribute-types target-model="patient" attribute="::attribute"
field-validation="::fieldValidation"
is-auto-complete="isAutoComplete"
get-auto-complete-list="getAutoCompleteList"
get-data-results="getDataResults" is-read-only="isReadOnly"
handle-update="handleUpdate" validate-autocomplete="true">
</attribute-types>
The validate-autocomplete is the extra attribute I have used at 1 place use of this directive.
Here is the template for the directive:
<div class="left" data-ng-switch-when="org.openmrs.Concept" ng-if="attribute.name == 'PATIENT_OCCUPATION'" style="position: absolute">
<input type="text"
class="ui-autocomplete-input"
id="{{::attribute.name}}"
name="{{::attribute.name}}"
ng-model="targetModel[attribute.name].value"
ng-keyup="suggest(targetModel[attribute.name])"
ng-required="{{::attribute.required}}">
<ul class="ui-front ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all" ng-if="showTag" ng-hide="hideList"
style="position:absolute; top:30px; width:192px">
<li class="ui-menu-item" role="presentation" ng-repeat="info in filterOcuppation"
ng-click="hideSuggestions(info)">
<a class="ui-corner-all" tabindex="-1">{{info.description}}</a>
</li>
</ul>
</div>
And this is the directive definition:
angular.module('bahmni.common.attributeTypes', [])
.directive('attributeTypes', [function () {
var link = function (scope, element, attrs, ngModelCtrl) {
var formElement = element[0];
if (attrs.validateAutocomplete) {
ngModelCtrl.$setValidity('selection', true);
}
};
return {
link: link,
scope: {
targetModel: '=',
attribute: '=',
fieldValidation: '=',
isAutoComplete: '&',
handleLocationChange: '&',
handleSectorChange: '&',
getAutoCompleteList: '&',
getDataResults: '&',
handleUpdate: '&',
isReadOnly: '&',
isForm: '=?'
},
templateUrl: '../common/attributeTypes/views/attributeInformation.html',
restrict: 'E',
controller: function ($scope) {
var dateUtil = Bahmni.Common.Util.DateUtil;
$scope.getAutoCompleteList = $scope.getAutoCompleteList();
$scope.getDataResults = $scope.getDataResults();
$scope.today = dateUtil.getDateWithoutTime(dateUtil.now());
// to avoid watchers in one way binding
$scope.isAutoComplete = $scope.isAutoComplete() || function () { return false; };
$scope.isReadOnly = $scope.isReadOnly() || function () { return false; };
$scope.handleUpdate = $scope.handleUpdate() || function () { return false; };
$scope.handleLocationChange = $scope.handleLocationChange() || function () { return false; };
$scope.handleSectorChange = $scope.handleSectorChange() || function () { return false; };
$scope.suggestions = $scope.attribute.answers;
$scope.showTag = false;
$scope.itisinvalid = true;
$scope.appendConceptNameToModel = function (attribute) {
var attributeValueConceptType = $scope.targetModel[attribute.name];
var concept = _.find(attribute.answers, function (answer) {
return answer.conceptId === attributeValueConceptType.conceptUuid;
});
attributeValueConceptType.value = concept && concept.fullySpecifiedName;
};
$scope.suggest = function (string) {
$scope.hideList = false;
$scope.showTag = true;
var output = [];
angular.forEach($scope.suggestions, function (suggestion) {
if (suggestion.description.toLowerCase().indexOf(string.value.toLowerCase()) >= 0) {
output.push(suggestion);
}
});
$scope.filterOcuppation = output;
};
$scope.hideSuggestions = function (object) {
$scope.targetModel[$scope.attribute.name] = object;
$scope.targetModel[$scope.attribute.name].value = object.description;
$scope.targetModel[$scope.attribute.name].conceptUuid = object.conceptId;
$scope.hideList = true;
};
}
};
}]);
When running this I get TypeError: ngModelCtrl.$setValidity is not a function
What I'm basically doing it validating whatever is entered into the input text is valid or not. For that I would also need the ng-model, how would I access that in my link function?
If I have written some wrong, feel free to correct me. I'm still in the process of learning AngularJS
You should use directive like that:
directive('attributeTypes', [function() {
return {
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
...
ngModel.$setValidity(...

passing ngModel and ngChange with custom directive under md-select

I am making a custom directive on top of md-select. I am having issues with default behavior of ngModel and ngChange. I can't seem to make them both work together.
Currently I have this http://next.plnkr.co/edit/X34DUWtkyYhbwJP4?open=lib%2Fscript.js
The ngModel is being updated, but the ngChange doesnt seem to work.
I also tried a method shown in http://embed.plnkr.co/HZAHSyi9L8UQdE24zYYI/
but having issues when setting value with a timeout (assuming value comes from api).
app.controller("appCtrl", function($scope){
$scope.items = [1,2,3,4,5,6];
$scope.foo=2; // this works
$scope.bar = function(foo) {
$scope.aux = foo;
}
setTimeout(function(){
$scope.foo=5;
}, 0); // this doesnt work
});
I want to make these two attributes to work as default md-select does.
When working with ng-model and custom directives, you can specify ngModel as a require, and then automatically get access to other directives like ngChange and ngRequired. I've updated your plunkr: http://next.plnkr.co/edit/VzYpZ2elmzV6XkbM?open=lib
HTML
<md-custom-select
ng-model="vm.SelectItems"
ng-change="vm.onselectchange()"
list="vm.ItemList">
</md-custom-selector>
JavaScript
var app = angular.module("MaterialApp", ["ngMaterial"]);
app.directive("mdCustomSelect", ["$compile", mdCustomSelect]);
function mdCustomSelect($compile) {
return {
restrict: "E",
require: {
ngModelCtrl: '^ngModel'
},
scope: {
ngModel: "<",
list: "=",
options: "<",
},
replace: true,
link: function(scope, element, attrs, controllers) {
scope.ngModelCtrl = controllers.ngModelCtrl;
var searchTemplate = '<md-select-header aria-label="Select Header" class="demo-select-header"><input aria-label="InputSearchBox" ng-keydown="$event.stopPropagation()" ng-model="searchTerm" type="search" placeholder="Search items" class="md-text"></md-select-header>';
var selectAllTemplate = '<div style="padding: 0px 0px 15px 5px; background-color: #efefef;"><md-checkbox class="md-warn" title="Select All" ng-model="checkAllChecked" ng-change="toggleSelectAll()">Check/Uncheck All </md-checkbox></div>';
var multiSelectGroupTemplate = '<md-option ng-value="item.ItemID" ng-repeat="item in ItemList | filter: searchTerm">{{item.ItemName}}</md-option>';
var completeTemplate = "";
completeTemplate += '<md-select multiple ng-model="ngModel" ng-change="valChanged()" data-md-container-class="selectdemoSelectHeader">';
completeTemplate += searchTemplate; //2 begin and end
completeTemplate += selectAllTemplate; //3 begin and end
completeTemplate += multiSelectGroupTemplate; //4 begin and end
completeTemplate += " </md-select>"; //1 end
element.html(completeTemplate);
$compile(element.contents())(scope);
},
controller: ["$scope", function($scope) {
var defaultValueProperty = ($scope.options == undefined || $scope.options.Value === undefined) ? "value" : $scope.options.Value;
var defaultTextProperty = ($scope.options == undefined || $scope.options.Text === undefined) ? "name" : $scope.options.Text;
$scope.isMultipleSelected = angular.isUndefined($scope.multiple) ? true : $scope.multiple;
$scope.checkAllChecked = false;
$scope.ItemList = [];
var rawItemList;
$scope.$watch("list", function(newValue) {
$scope.ItemList = newValue.map(item => {
return { ItemID: item[defaultValueProperty], ItemName: item[defaultTextProperty] };
});
}, true);
$scope.valChanged = function(){
$scope.ngModelCtrl.$setViewValue($scope.ngModel);
}
$scope.toggleSelectAll = function() {
if ($scope.checkAllChecked == false) {
$scope.ngModelCtrl.$setViewValue([]);
} else {
$scope.ngModelCtrl.$setViewValue($scope.ItemList.map(item => item.ItemID));
}
};
}]
};
}

Angularjs:http post is not working

I am designing the simple page which will take the start and end date from the user,validate it and post it.
Below is my HTML code,
<body>
<div ng-app="appTable">
<div ng-controller="Allocation">
Select start date:
<input type="text"
datepicker
ng-model="start_date" />
<br>
<br>
Select end date:
<input type="text"
datepicker
ng-model="end_date" />
<br>
<br>
<button ng-click="Submit()"> Submit </button>
{{msg}}
{{test1}}
{{test2}}
</div>
</div>
</body>
Below is the aj script:
<script>
var app = angular.module("appTable", []);
app.controller("Allocation", function($scope) {
$scope.start_date = "2017-05-01";
$scope.end_date = "2017-05-19";
$scope.Submit = function() {
var start = new Date($scope.start_date);
var end = new Date($scope.end_date);
if (start > end) {
$scope.msg = "Start date must be less than the end date."
} else {
$scope.msg = "";
$scope.test = "";
$scope.postData($scope.start_date, $scope.end_date);
}
};
$scope.postData = function(s_date, e_date) {
var data = {
s_date: s_date,
e_date: e_date,
};
$scope.test1 = "Post called 1";
$http.post('/view_status/', data).then(function(response) {
$scope.test2 = "Post called 2";
if (response.data)
$scope.msg = "Post Data Submitted Successfully!";
}, function(response) {
$scope.msg = "Service not Exists";
$scope.statusval = response.status;
$scope.statustext = response.statusText;
$scope.headers = response.headers();
});
};
});
app.directive("datepicker", function() {
function link(scope, element, attrs, controller) {
// CALL THE "datepicker()" METHOD USING THE "element" OBJECT.
element.datepicker({
onSelect: function(dt) {
scope.$apply(function() {
// UPDATE THE VIEW VALUE WITH THE SELECTED DATE.
controller.$setViewValue(dt);
});
},
dateFormat: "yy-mm-dd" // SET THE FORMAT.
});
}
return {
require: 'ngModel',
link: link
};
});
</script>
For the debugging purpose I have taken the 2 flags test1,test2 which will print the message before and after the call of the POST service.
Flag test1 is printing the message but test2,msg are not printing anything.
Please help if I am missing something.
Inject $http to your controller to access $http.post.
like: app.controller("Allocation", function($scope, $http) {
You need to inject $http into your controller

how to bind a directive var to controller

i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;

alternative method instead $watch in angularjs

I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];

Categories

Resources