Parent scope update by angularjs datepicker directive - angularjs

I am developing a datePicker directive comprising 3 x selects (views/datetime.html)
<select class="form-control input-group-sm w75 float-left"
ng-model="mth"
ng-options="x for x in ['Jan','Feb','Mar','Apr','May','Jun']">
</select>
<select class="form-control input-group-sm w75 float-left"
ng-model="day"
ng-options="x for x in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]">
</select>
<select class="form-control input-group-sm w75"
ng-model="year"
ng-options="x for x in [2014,2015,2016,2017,2018]">
</select>
The directive can be used one or more time in each html view (startdate, enddate, etc)
<div class="input-group-sm" date-picker ng-model="startdate"></div>
My directive is coded as follows:
App.directive('datePicker', function () {
var date = {};
var objLink = {
restrict: 'A',
templateUrl: 'views/datetime.html',
scope: {
ngModel: '=',
},
link: function($scope, element, attr) {
$scope.$watch("mth", function(value) {
if ( typeof value != 'undefined' ) {
date.mth = value;
updateTarget($scope, attr);
}
});
$scope.$watch("day", function(value) {
if ( typeof value != 'undefined' ) {
date.day = value;
updateTarget($scope, attr);
}
});
$scope.$watch("year", function(value) {
if ( typeof value != 'undefined' ) {
date.year = value;
updateTarget($scope, attr);
}
});
}
};
return objLink;
function updateTarget( scope, attr ) {
var d = date.mth+'-'+date.day+'-'+date.year;
scope[attr.date] = d;
}
});
My issue is that function UpdateTarget() does NOT update the $scope.startdate in the controller.
Any ideas...anyone? Thanks in advance.

scope[attr.date] = d;
Here attr.date is undefined. To update the $scope.startdate in the controller, you need to pass attr.ngModel as stated below
scope[attr.ngModel] = d;

...Worked it all out myself! Thx for any feedback.
App.directive('datePicker', function () {
var mths = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var dirLink = {
restrict: 'A',
templateUrl: 'views/datetime.html',
scope: {
data : '='
},
link: function($scope, element, attr) {
// watch bound data item for change
// - ajax load delay from view render
$scope.$watch("data", function(value) {
if ( typeof $scope.data == 'undefined' ) {
return;
}
// initialise view controls
if ( typeof $scope.mth == 'undefined' ) {
var a = $scope.data.split(' ');
var dt = a[0].split('-');
$scope.mth = mths[dt[1]-1];
$scope.year = dt[0];
$scope.day = dt[2];
}
});
// watch view controls
$scope.$watch("mth", function(value) {
if ( typeof value != 'undefined' ) {
var mth = padl(mths.indexOf(value)+1);
updateTarget($scope, 'mth', mth);
}
});
$scope.$watch("day", function(value) {
if ( typeof value != 'undefined' ) {
updateTarget($scope, 'day', value);
}
});
$scope.$watch("year", function(value) {
if ( typeof value != 'undefined' ) {
updateTarget($scope, 'year', value);
}
});
}
};
return dirLink;
function updateTarget( scope, seg, val ) {
// init date if empty
if ( scope.data.length < 8 ) {
var d = new Date();
var dt = d.getYear()+'-'+padl(d.getMonth())+'-'+padl(d.getDate());
scope.data = dt;
}
// split existing data
var a = scope.data.split(' ');
var dt = a[0].split('-');
// change selected segment
var d = ['year','mth','day'];
dt[d.indexOf(seg)] = val;
// reassemble date
var d = dt[0]+'-'+dt[1]+'-'+dt[2];
scope.data = d;
}
function padl( n ) {
return ("0"+n).slice(-2);
}
});

Related

Directive Parameter Not Initializing on First Load In AngularJs

I'va made a Dropdown directive, i'm trying to assign methods on passing parameter to directive and i'll call these method from controller.
but on first load i'm not getting the assign method in controller but when i'm assigning it on second load (i.e on dropdown change event)and it's working fine.
how can i get the methods on first load of directive in the calling controller after first load.
here is the Directive:
"use strict";
myApp.directive("selectDirective", [function () {
return {
restrict: "E",
template: '<select class="form-control input-sm dropdown" data-ng-model="model.args.selectedItem" data-ng-options="item[model.args.displayField] for item in model.args.source" data-ng-change="model.itemChange(model.args.selectedItem)"><option value="">Select Any Item</option></select>',
scope: {
},
bindToController: { args: "=" },
controller: function () {
var self = this;
var initializeControl = function () {
if (self.args == undefined) {
self.args = {};
}
if (self.args.method == undefined) {
self.args.method = {};
}
if (self.args.isDisabled == undefined) {
self.args.isDisabled = false;
}
if (self.args.displayField == undefined) {
self.args.displayField = '';
//alert('Display Field is blank for dropdown control.')
}
if (self.args.valueField == undefined) {
self.args.valueField = '';
//alert('Value Field is blank for dropdown control.')
}
if (self.args.source == undefined) {
self.args.source = {};
}
if (self.args.hide == undefined) {
self.args.hide = false;
}
}
//here i'm assigning the methods in passing parameter
var assignMethod = function () {
self.args.method =
{
setEnable: function (args) {
self.args.isDisabled = !args;
},
setVisible: function (args) {
self.args.hide = !args;
},
getText: function () {
return self.args.selectedText;
},
getValue: function () {
return self.args.selectedValue;
},
setItem: function (item) {
debugger;
if (item != undefined) {
var index = self.args.source.indexOf(item);
self.args.selectecText = item[self.args.displayField];
self.args.selectecValue = item[self.args.valueField];
self.args.selectedItem = item;
self.args.selectedIndex = index;
}
}
}
}
self.itemChange = function (item) {
debugger;
if (item != undefined) {
var index = self.args.source.indexOf(item);
self.args.selectecText = item[self.args.displayField];
self.args.selectecValue = item[self.args.valueField];
self.args.selectedItem = item;
self.args.selectedIndex = index;
}
}
initializeControl();
assignMethod();
},
controllerAs: 'model'
}
}]);
Here is the Calling Controller Code:
"use strict";
myApp.controller("homeController", [function () {
var self = this;
var initializeControl = function () {
var myList = [{ id: 1, name: 'List1', value: 'List1' },
{ id: 2, name: 'List2', value: 'List2' }];
self.argsParam = {
displayField: 'name',
valueField: "value",
source: myList,
selectecText: '',
selectecValue: ''
};
self.clickMe = function () {
debugger;
var item = { id: 2, name: 'List2', value: 'List2' };
self.argsParam.method.setItem(item);
}
};
initializeControl();
}]);
View where i used the directive:
<div class="cold-md-12" ng-controller="homeController as model">
<h1>Home Page</h1>
<select-directive args="model.argsParam"></select-directive>
<input type="button" value="Click" ng-click="model.clickMe()" />
</div>
Scenario:
If assigned method called second time inside directive on dropdown-change event then i can get these method on passing param.
i.e
self.itemChange = function (item) {
debugger;
if (item != undefined) {
var index = self.args.source.indexOf(item);
self.args.selectecText = item[self.args.displayField];
self.args.selectecValue = item[self.args.valueField];
self.args.selectedItem = item;
self.args.selectedIndex = index;
// here i'm assigning these method on change event then it's working fine after changing the value otherwise no success
assignMethod();
}
}
So, How i can i get the methods assign in the passing parameter on
First load of the directive?
I've moved the Controller Content to Link function in Directive and
it's working fine, but I still didn't get any idea how my previous code
not worked as expected.
Directive Code:
'use strict';
var testApp = angular.module('TestApp', []);
testApp.directive('sampleDirective', ['$http', function () {
return {
restrict: "E",
scope: {},
bindToController: { args: '=' },
template: '<div class="row">' +
'<select class="form-control"' +
'data-ng-model="model.args.selectedItem"' +
'data-ng-options="item[model.args.displayField] for item in model.args.source"' +
'data-ng-change="model.itemChange(model.args.selectedItem)">' +
'<option value="">Select Any Item</option>' +
'</select>' +
'</div>',
link: function (scope, element, attrs) {
var self = scope.model;
debugger;
var initializeControl = function () {
if (self.args == undefined) {
self.args = {};
}
if (self.args.method == undefined) {
self.args.method = {};
}
if (self.args.isDisabled == undefined) {
self.args.isDisabled = false;
}
if (self.args.displayField == undefined) {
self.args.displayField = '';
alert('Display Field is blank for dropdown control.')
}
if (self.args.valueField == undefined) {
self.args.valueField = '';
alert('Value Field is blank for dropdown control.')
}
if (self.args.source == undefined) {
self.args.source = {};
}
if (self.args.hide == undefined) {
self.args.hide = false;
}
}
var assignMethod = function () {
self.args.method =
{
setEnable: function (args) {
self.args.isDisabled = !args;
},
setVisible: function (args) {
self.args.hide = !args;
},
getText: function () {
return self.args.selectedText;
},
getValue: function () {
return self.args.selectedValue;
},
setItem: function (item) {
var index = self.args.source.indexOf(item);
self.args.selectecText = item[self.args.displayField];
self.args.selectecValue = item[self.args.valueField];
self.args.selectedItem = item;
self.args.selectedIndex = index;
}
};
}
self.itemChange = function (item) {
if (item != undefined) {
var index = self.args.source.indexOf(item);
self.args.selectecText = item[self.args.displayField];
self.args.selectecValue = item[self.args.valueField];
self.args.selectedItem = item;
self.args.selectedIndex = index;
}
}
initializeControl();
assignMethod();
},
controller: function () {
},
controllerAs: 'model'
}
}]);

How to call $setValidity in directive only if required attribute exists

I have a directive where I call this:
ngModel.$setValidity('required', isFinite(scope.inputValue) && scope.inputValue != null);
to set whether the value has been filled by the user or not. I'm pretty sure this used to only make the form invalid if my directive had required or ng-required="trueExpression" on it, but since upgrading to Angular v1.5.7 the form appears to become invalid regardless of whether the required attributes exist or not.
So how can I detect if required or ng-required="trueExpression" are on my directive so I only call this line then?
Here is my entire directive for reference:
(function () {
'use strict';
angular.module('app').directive('bigNumberInput', bigNumberInput);
bigNumberInput.$inject = [];
function bigNumberInput() {
return {
restrict: 'EA',
require: 'ngModel', // get a hold of NgModelController
transclude: true,
scope: {
label: '#'
},
templateUrl: 'app/directives/bigNumberInput.html',
link: function (scope, element, attrs, ngModel) {
//scope.inputValue = null;
ngModel.$formatters.push(function (modelValue) {
var inputValue = null;
var sizeValue = "";
if (modelValue != null) {
if (modelValue / 1000000000 >= 1.0) {
inputValue = modelValue / 1000000000;
sizeValue = "B";
} else if (modelValue / 1000000 >= 1.0) {
inputValue = modelValue / 1000000;
sizeValue = "MM";
} else if (modelValue / 1000 >= 1.0) {
inputValue = modelValue / 1000;
sizeValue = "K";
} else {
inputValue = modelValue;
sizeValue = "";
}
}
return { inputValue: inputValue, sizeValue: sizeValue };
});
ngModel.$render = function () {
scope.inputValue = ngModel.$viewValue.inputValue;
scope.sizeValue = ngModel.$viewValue.sizeValue;
};
scope.$watch('inputValue + sizeValue', function () {
ngModel.$setViewValue({
inputValue: scope.inputValue,
sizeValue: scope.sizeValue
});
ngModel.$setValidity('required', isFinite(scope.inputValue) && scope.inputValue != null);
});
ngModel.$parsers.push(function (viewValue) {
var inputValue = viewValue.inputValue;
if (inputValue != null) {
if (viewValue.sizeValue === 'K') {
inputValue = inputValue * 1000;
} else if (viewValue.sizeValue === "MM") {
inputValue = inputValue * 1000000;
} else if (viewValue.sizeValue === "B") {
inputValue = inputValue * 1000000000;
}
}
return inputValue;
});
}
};
}
}());
And here is the HTML template for it:
<div layout="row">
<md-input-container flex>
<label>{{label}}</label>
<input type="number" step="any" ng-model="inputValue"/>
<div ng-transclude></div>
</md-input-container>
<md-input-container>
<label>Type</label>
<md-select ng-model="sizeValue" md-on-open="valuationTypeOpen = true" md-on-close="valuationTypeOpen = false">
<md-option value="">{{valuationTypeOpen ? 'Verbatim (=)' : '='}}</md-option>
<md-option value="K">{{valuationTypeOpen ? 'Thousands (K)' : 'K'}}</md-option>
<md-option value="MM">{{valuationTypeOpen ? 'Millions (MM)' : 'MM'}}</md-option>
<md-option value="B">{{valuationTypeOpen ? 'Billions (B)' : 'B'}}</md-option>
</md-select>
</md-input-container>
</div>
I think I found the answer on my own. I believe it's just a matter checking if the "required" attribute returns true like this:
ngModel.$setValidity('required', !attrs.required || (isFinite(scope.inputValue) && scope.inputValue != null));
It appears to be working for hard-coded required attributes as well as using ng-required attributes.

How to get selected value from ng-autocomplete directive to controller

I am using a directive for auto complete / auto suggest in angular Js taken from http://demo.jankuri.com/ngAutocomplete/. It is working fine getting the data from server and filtering it. But I am facing problem into select and use that select item from the auto complete.
Here is the code of directive what I am using for this...
app.factory('ngAutocompleteService', ['$http', function($http)
{
var self = this;
self.getData = function (url, keyword) {
return $http.get(url, { query: keyword });
};
return self;
}])
app.directive('ngAutocomplete', ['$timeout','$filter','ngAutocompleteService',
function($timeout, $filter, ngAutocompleteService)
{
'use strict';
var keys = {
left : 37,
up : 38,
right : 39,
down : 40,
enter : 13,
esc : 27
};
var setScopeValues = function (scope, attrs) {
scope.url = base_url+attrs.url || null;
scope.searchProperty = attrs.searchProperty || 'skills';
scope.maxResults = attrs.maxResults || 10;
scope.delay = parseInt(attrs.delay, 10) || 300;
scope.minLenth = parseInt(attrs.minLenth, 10) || 2;
scope.allowOnlyResults = scope.$eval(attrs.allowOnlyResults) || false;
scope.placeholder = attrs.placeholder || 'Search...';
};
var delay = (function() {
var timer = 0;
return function (callback, ms) {
$timeout.cancel(timer);
timer = $timeout(callback, ms);
};
})();
return {
restrict: 'E',
require: '?ngModel',
scope: true,
link: function(scope, element, attrs, ngModel) {
setScopeValues(scope, attrs);
scope.results = [];
scope.currentIndex = null;
scope.getResults = function () {
if (parseInt(scope.keyword.length, 10) === 0) scope.results = [];
if (scope.keyword.length < scope.minLenth) return;
delay(function() {
ngAutocompleteService.getData(scope.url, scope.keyword).then(function(resp) {
scope.results = [];
var filtered = $filter('filter')(resp.data, {skills: scope.keyword});
for (var i = 0; i < scope.maxResults; i++) {
scope.results.push(filtered[i]);
}
scope.currentIndex = 0;
if (scope.results.length) {
scope.showResults = true;
}
});
}, scope.delay);
};
scope.selectResult = function (r) {
scope.keyword = r.skills;
ngModel.$setViewValue(r.skills);
scope.ngModel = r.skills;
ngModel.$render();
scope.showResults = false;
};
scope.clearResults = function () {
scope.results = [];
scope.currentIndex = null;
};
scope.hoverResult = function (i) {
scope.currentIndex = i;
}
scope.blurHandler = function () {
$timeout(function() {
if (scope.allowOnlyResults) {
var find = $filter('filter')(scope.results, {skills: scope.keyword}, true);
if (!find.length) {
scope.keyword = '';
ngModel.$setViewValue('');
}
}
scope.showResults = false;
}, 100);
};
scope.keyupHandler = function (e) {
var key = e.which || e.keyCode;
if (key === keys.enter) {
scope.selectResult(scope.results[scope.currentIndex]);
}
if (key === keys.left || key === keys.up) {
if (scope.currentIndex > 0) {
scope.currentIndex -= 1;
}
}
if (key === keys.right || key === keys.down) {
if (scope.currentIndex < scope.maxResults - 1) {
scope.currentIndex += 1;
}
}
if (key === keys.esc) {
scope.keyword = '';
ngModel.$setViewValue('');
scope.clearResults();
}
};
},
template:
'<input type="text" class="form-control" ng-model="keyword" placeholder="{{placeholder}}" ng-change="getResults()" ng-keyup="keyupHandler($event)" ng-blur="blurHandler()" ng-focus="currentIndex = 0" autocorrect="off" autocomplete="off">' +
'<input type="hidden" ng-model="skillIdToBeRated">'+
'<div ng-show="showResults">' +
' <div ng-repeat="r in results | filter : {skills: keyword}" ng-click="selectResult(r)" ng-mouseover="hoverResult($index)" ng-class="{\'hover\': $index === currentIndex}">' +
' <span class="form-control">{{ r.skills }}</span>' +
' </div>' +
'</div>'
};
}]);
I am unable to get value which is selected by ng-click="selectResult(r)" function. The value is showing into text field but not getting it into controller.
I was also using the same directive for showing the auto complete text box. I have tried the following for getting the selected value from auto complete.
in HTML
<div ng-controller="Cntrl as cntrl">
<ng-autocomplete ng-model="cntrl.selectedValue" url="url" search-property="keyword" max-results="10" delay="300" min-length="2" allow-only-results="true"></ng-autocomplete>
</div>
in JavaScript
app.controller('Cntrl', function($scope, $http) {
var self = this;
self.selectedValue = '';
$scope.getSelectedValue = function(){
console.log(self.selectedValue);
}
});
I hope this may help you.
I ran into the same issue. I ended up just watching the property from the details attribute in the ng-autocomplete input and it works pretty well.
$scope.$watch(function() {
return vm.location_result;
}, function(location) {
if (location) {
vm.location_list.push(location);
vm.location = '';
}
});
Fiddle Example: http://jsfiddle.net/n3ztwucL/
GitHub Gist: https://gist.github.com/robrothedev/46e1b2a2470b1f8687ad

Decorating a directive by adding a function that will call the directive's controller

I use a directive that is declared like this :
(function (directives) {
var FilterDirective = (function () {
function FilterDirective() {
var directive = {};
directive.restrict = 'A';
directive.scope = true;
directive.controller = elasticui.controllers.FilterController;
directive.link = function (scope, element, attrs, filterCtrl) {
scope.$watch(element.attr('eui-filter') + " | euiCached", function (val) { return scope.filter.filter = val; });
var enabled = false;
var enabledAttr = element.attr('eui-enabled');
if (enabledAttr) {
scope.$watch(enabledAttr, function (val) { return scope.filter.enabled = val; });
enabled = scope.$eval(enabledAttr);
}
scope.filter = {
filter: scope.$eval(element.attr('eui-filter') + " | euiCached"),
enabled: enabled
};
filterCtrl.init();
};
return directive;
}
return FilterDirective;
})();
directives.FilterDirective = FilterDirective;
directives.directives.directive('euiFilter', FilterDirective);
})
The controller of the directive is :
(function (controllers) {
var FilterController = (function () {
function FilterController($scope) {
this.scope = $scope;
}
FilterController.prototype.init = function () {
var _this = this;
if (this.scope.filter.filter) {
var isEnabled = this.scope.filters.contains(this.scope.filter.filter);
if (!isEnabled && this.scope.filter.enabled) {
this.scope.filters.add(this.scope.filter.filter);
isEnabled = true;
}
}
this.scope.filter.enabled = isEnabled;
this.scope.$watch('filter.enabled', function (newVal, oldVal) {
if (newVal !== oldVal) {
_this.updateFilter();
}
});
this.scope.$watch('filter.filter', function (newVal, oldVal) {
if (!elasticui.util.EjsTool.equals(oldVal, newVal)) {
if (oldVal) {
_this.scope.filters.remove(oldVal);
}
_this.updateFilter();
}
});
};
FilterController.prototype.updateFilter = function () {
if (!this.scope.filter.filter) {
return;
}
if (this.scope.filter.enabled) {
this.scope.filters.add(this.scope.filter.filter);
}
else {
this.scope.filters.remove(this.scope.filter.filter);
}
};
FilterController.$inject = ['$scope'];
return FilterController;
})();
controllers.FilterController = FilterController;
})
Actually, the directive has a scope containing a filter object which contains two attributes filter : { enabled : boolean, filter : object} and the directive is used like this :
<label class="btn" ng-model="filter.enabled"
eui-filter="ejs.TermFilter('field','value')" btn-checkbox>
when the button is clicked the filter.enabled is set. My purpose is to add a behavior that will permit to change filter.enabled value via a function external to the directive.
The directive will look like this :
<label class="btn" ng-model="filter.enabled"
eui-filter="ejs.TermFilter('field','value')" eui-enable-fn="fn(somevariable)" btn-checkbox>
where fn will take the somevariable and set it to the filter.enabled.
Thanks in advance,
If you want to enable/disable a filter through the pressure of a button why not declare a filter with the property eui-enabled set to a custom toggling variable?
In other words it would result as:
HTML:
<label class="btn" eui-filter="..." eui-enabled="my_toggling_variable">
<button type=button ng-click="toggleVar()"></button>
JS:
myApp.controller('myCtrl', ['$scope', function($scope) {
$scope.my_toggling_variable = false;
$scope. toggleVar = function(){
$scope.my_toggling_variable = !$scope.my_toggling_variable;
};
}]);
Hope to have understood well the topic.

Returning hidden object with Angular ng-repeat filter

I am trying to show the first value of a group and hide all other similar values within an ng-repeat. I am getting the if/else to work but it's literally returning "false" as a string. Here is what I have so far.
app.filter('dateSort', function() {
var prevVal = null;
return function(input) {
if (prevVal !== input.uniquedate) {
prevVal = input.uniquedate;
return moment(input.jsdatetime).format('dddd,\n MMMM Do');
} else {
return false;
}
};
});
I've also tried this as a directive, but with no luck.
app.directive('hideon', function() {
var prevVal = $index[-1].uniquedate;
return function(scope, element, attrs) {
scope.$watch(attrs.hideon, function(value, oldValue) {
if(element.uniquedate !== prevVal) {
element.show();
} else {
element.hide();
}
}, true);
}
});
Any help is appreciated. I am assuming that the best way to do this is with a directive. For the filter itself with the returned objects (including the false), the values don't hold after I sort any of the data. That's when I started trying to use it with $watch.
Here's a Codepen with what I've started- http://codepen.io/drewbietron/pen/dKjhe
Here is the fix to blank box display.
app.filter('uniqueDate', function() {
var prevVal = null, prevSeq = null;
return function(input, seq) {
input.show = false;
if (prevVal !== input.month || prevSeq == seq-1) {
prevVal = input.month;
input.show = true;
return input.day + " " + input.month;
}
};
});
Have made code changes to filter the events on selecting types.
var selectedTypes = ["Math", "Science", "Writing"];
$scope.updateSelectedTypes = function(type){
var found = _.some(selectedTypes, function(selType){
return selType == type;
});
if(!found){
selectedTypes.push(type);
}
else{
selectedTypes = _.difference(selectedTypes, type);
}
filterEvents();
};
var filterEvents = function(){
var result = _.filter(Events, function(event){
return _.some(selectedTypes, function(type){
return event.type == type;
});
});
result = _.each(result, function(item){
item.show = false;
});
$scope.events = angular.copy(result);
};
HTML:
<li><b>Filter By:</b></li>
<li class="filter" ng-click="updateSelectedTypes('Math')" ng-class="{ active: Math }">Math</li>
<li class="filter" ng-click="updateSelectedTypes('Science')" ng-class="{ active: Science }">Science</li>
<li class="filter" ng-click="updateSelectedTypes('Writing')" ng-class="{ active: Writing}">Writing</li>
<input type="search" ng-model="instructor.instructor" placeholder="Search By Instructor..." />
Here is the updated codepen link

Resources