Issue using REST API call in Angular JS code - json

The below function is working fine with Static data but when i get it using a REST API Call it is throwing error
i an following the link http://angular-js.in/md-chips/
angular.module('mdChips', [])
.factory('chipsService', [function(){
alert("chipservice");
return {
helper: function(scope, collection, active, rule, chipsList){
if (active == -1 && collection.length > 0){
collection[0].active = true;
} else if (collection.length > 0){
if (collection[active+rule]){
collection[active].active = false;
collection[active+rule].active = true;
} else {
collection[active].active = false;
var index = rule == 1 ? 0 : (collection.length-1);
collection[index].active = true;
}
}
scope.$apply();
if (collection.length > 0)
chipsList.querySelector('.active').scrollIntoView({block: "start", behavior: "smooth"});
},
nextActive: function(scope, collection, active, chipsList){
this.helper(scope, collection, active, 1, chipsList);
},
prevActive: function(scope, collection, active, chipsList){
this.helper(scope, collection, active, -1, chipsList);
}
}
}])
.directive('mdChips', ['$compile','$timeout', '$document', 'chipsService', function($compile, $timeout, $document, chipsService){
return {
restrict: 'E',
replace:true,
template: '<div class="md-chips" ng-cloak> \
<div class="chips-input-field"> \
<div class="input-chips-elements"> \
<div ng-repeat="chips in ngModel track by $index" class="chips-mini-item"> \
<div class="chips-mini" ng-click="showMore($index, $event)"> \
\
<div class="chips-mini-title">{{chips[mdTitle]}}</div> \
</div> \
</div> \
<div class="chips-active" ng-style="{top: ytop}" ng-model="ytop" ng-click="closeActive($event)"></div> \
<input type="text" ng-model="chipsText[mdTitle]" ng-focus="clearActive()" ng-keydown="clearPrev($event)" class="chipsInput"/> \
</div> \
</div> \
</div>',
require: '?ngModel',
scope: {
collection: '=',
ngModel: '=',
text: '#',
//mdItem: '#',
mdTitle: '#',
//mdThumbnail: '#',
mdSubtitle: '#'
},
link: function (scope, element, attrs) {
scope.ytop = '10px';
scope.innerCollection = scope.collection.map(function(item){
if (!item[scope.mdTitle]){
alert(item[scope.mdTitle]);
return;
}
if (!item[scope.mdSubtitle] && !item[scope.mdThumbnail]){
if (item[scope.mdItem].length < 1){
return;
} else {
item[scope.mdSubtitle] = item[scope.mdItem][0][scope.mdSubtitle];
item[scope.mdThumbnail] = item[scope.mdItem][0][scope.mdThumbnail] ? item[scope.mdItem][0][scope.mdThumbnail] : '';
item[scope.mdItem].shift();
return item;
}
}
item['active'] = false;
return item;
});
element.bind('input', function(event) {
console.log("event started");
var self = scope;
scope.clearActive();
if (event.target.value) {
scope.$apply(function(){
var list = angular.element("<div id='chips-list' ng-show='true' ng-cloak> \
<div ng-repeat='item in (filteredCollection = (innerCollection | filter:chipsText))' class='chips-list-item' ng-click=addToInput(item) ng-class='{active: item.active}'> \
<div class='chips-item-wrapper'> \
<div class='chips-image'> \
<img ng-src='{{item[mdThumbnail]}}' ng-show='item[mdThumbnail] ? true : false'> \
<div class='image-default' ng-show='item[mdThumbnail] ? false : true'></div> \
</div> \
<span class='chips-title'>{{item[mdTitle]}}</span> \
\
</div> \
</div> \
</div>");
$compile(list)(scope);
$timeout(function() {
self.removeList();
element.append(list);
});
});
} else {
self.removeList();
}
});
$document.bind('click', function(evt){
scope.clearActive();
if(scope.chipsText){
scope.chipsText[scope.mdTitle] = '';
}
scope.removeList();
scope.$apply();
});
element.bind('click', function(evt){
evt.stopPropagation();
element[0].querySelector('.chipsInput').focus();
});
scope.removeList = function(){
this.innerCollection.forEach(function(item, index){
if(item.active){
item.active = false;
}
});
var chipsList = element[0].querySelector('#chips-list');
if (chipsList) {
chipsList.parentNode.removeChild(chipsList);
}
};
scope.addToInput = function(item){
console.log("add to input");
var chipsElement = JSON.parse(JSON.stringify(item));
console.log(chipsElement);
this.ngModel.push(chipsElement);
this.chipsText[this.mdTitle] = '';
this.removeList();
element[0].querySelector('.chipsInput').focus();
};
scope.showMore = function(index, event){
this.removeList();
scope.ytop = event.currentTarget.offsetTop + 'px';
var item = scope.ngModel[index],
chipsActive = element[0].querySelector('.chips-active');
var show = item[scope.mdThumbnail] ? true : false;
var thumb = item[scope.mdThumbnail]? item[scope.mdThumbnail] : '';
var htmlCode = '<div id ="chips-active-list" ng-cloak> \
<div class="chips-active-main"> \
\
<div class="boxclose" id="boxclose" ng-click=deleteChips(' + index + ')><a></a></div> \
<div class="chips-active-wrap"> \
<div class="chips-active-title" >' + item[scope.mdTitle] + '</div> \
\
</div> \
</div>';
if (item[scope.mdItem] && item[scope.mdItem].length > 0){
for(var i=0; i < item[scope.mdItem].length; i++){
var url = item[scope.mdItem][i][scope.mdThumbnail] ? item[scope.mdItem][i][scope.mdThumbnail] : '';
show = url ? true : false;
htmlCode += '<div class="md-chips-single-line" ng-click=setOtherEmail(' + index + ',"'+url+'","' + item[scope.mdItem][i][scope.mdSubtitle] + '",' + i + ')> \
<div class="chips-active-img"> \
<img src="' + url + '" ng-show=' + show + ' /> \
<div class="chips-active-image-default" ng-show=' + !show + '></div> \
</div> \
<div class="chips-active-wrap"> \
<p class="chips-active-description-only">' + item[scope.mdItem][i][scope.mdSubtitle] + '</p> \
</div> \
</div>';
}
}
htmlCode += '</div>';
var chips = angular.element(htmlCode);
if (chipsActive.hasChildNodes()){
this.clearActiveChildren(chipsActive);
}
$compile(chips)(scope);
$timeout(function() {
if (element[0].querySelector('#chips-list')){
this.chipsText[this.mdTitle] = '';
element[0].querySelector('#chips-list').remove();
}
chipsActive.appendChild(chips[0]);
});
};
element.bind('keydown', function(kEv){
var chipsList = element[0].querySelector('#chips-list');
if (chipsList){
var active = -1;
scope.filteredCollection.forEach(function(item, index){
if(item.active){
active = index;
}
});
switch(kEv.keyCode){
case 40:
chipsService.nextActive(scope, scope.filteredCollection, active, chipsList);
break;
case 38:
chipsService.prevActive(scope, scope.filteredCollection, active, chipsList);
break;
case 13:
if (active!==-1){
scope.addToInput(scope.filteredCollection[active]);
scope.removeList();
kEv.target.style.width = 20;
scope.$apply();
} else if (scope.chipsText[scope.mdTitle] ){
item = {};
item[scope.mdTitle] = scope.chipsText[scope.mdTitle];
item[scope.mdSubtitle] = scope.chipsText[scope.mdTitle];
scope.addToInput(item);
kEv.target.style.width = 20;
scope.$apply();
}
break;
default:
break;
}
} else {
return;
}
});
scope.deleteChips = function(index){
scope.ngModel.splice(index,1);
this.clearActive();
};
scope.clearPrev = function(event){
if(event.keyCode === 8 && event.target.value === '' && scope.ngModel.length !== 0){
scope.ngModel.pop();
}
if (scope.chipsText){
var length = scope.chipsText[scope.mdTitle].length * 15 + 15;
event.target.style.width = length ? length : 20;
}
return true;
};
scope.closeActive = function(event){
if (event.currentTarget.hasChildNodes()){
this.clearActiveChildren(event.currentTarget);
}
};
scope.clearActive = function(){
var chipsActive = element[0].querySelector('.chips-active');
this.clearActiveChildren(chipsActive);
};
scope.clearActiveChildren = function(active){
while (active.firstChild) {
active.removeChild(active.firstChild);
}
};
scope.setOtherEmail = function(index, url, email,i){
var old = {};
old.url = this.ngModel[index][scope.mdThumbnail];
old.subtitle = this.ngModel[index][scope.mdSubtitle];
if (url && url !== 'undefined'){
this.ngModel[index][scope.mdThumbnail] = url;
} else {
delete this.ngModel[index][scope.mdThumbnail];
}
this.ngModel[index][scope.mdSubtitle] = email;
this.ngModel[index][scope.mdItem][i][scope.mdSubtitle] = old.subtitle;
this.ngModel[index][scope.mdItem][i][scope.mdThumbnail] = old.url;
};
}
}
}]);
The error details are TypeError: Cannot read property 'map' of undefined
HTML CODE
<md-chips collection='itemsCollection' ng-model='selectedUsers' text='To' md-title='Name' md-subtitle='City' class='input' />
Below is the ANgular JS Code . The collection is showing undefined value when i use the REST API Call
angular.module('mdChipsDemo', ['mdChips'])
.controller('MainController', function($scope, $http) {
$http.get('http://www.w3schools.com/angular/customers.php')
.success(function(data) {
$scope.itemsCollection = JSON.stringify(data.records);
if ($scope.itemsCollection.length > 0) {
alert(123);
console.log($scope.itemsCollection);
}
})
.error(function(data, status, headers, config) {
$scope.errorMessage = "Couldn't load the list of customers, error # " + status;
});
$scope.selectedUsers = [];
console.log($scope.selectedUsers);
});

This line: $scope.itemsCollection = JSON.stringify(data.records); sets the item as a String but then passes it into your directive.
The first action on the directive's collection is: scope.innerCollection = scope.collection.map(...)
typeof JSON.stringify(data.records).map; // => undefined
See this plunkr.

Related

Not able to get ng-bind-html to work

Problem: I am trying to use ng-bind-html but I am getting the following errors on the console:
The following is the controller where I am calling ngSanitize:
and using the following file:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-sanitize.js"></script>
In my form, I do the following to use ng-bind-html. So when I try to see my results, it is still rendering the &rather then &:
<div ng-bind-html="e.Specialty"></div>
and the following is what occurs in the Specialty:
Any help would be appreciated.
#lealceldeiro: Here is the controller in which I am trying to implement your suggestion but not sure where I will add it:
(
function(){
var $scope, $location;
var indexApp = angular.module('indexApp',['ui.bootstrap', 'ngSanitize']);
indexApp.controller('IndexController',function($scope,$sce,$http,$location,anchorSmoothScroll){
$scope.Lang = 'initVal';
$scope.ShowResults = false;
$scope.ShowDesc = true;
$scope.NoResults = false;
$scope.currentPage = 1;
$scope.maxPageNumbersToShow = 10;
$scope.formModel = {};
$scope.searchMode = 0;
$scope.miles = [{'value':'5'},{'value':'10'},{'value':'15'},{'value':'20' }];
$scope.Specialties = [{'value':'Family practice'},{'value':'General practice'},{'value':'Internal medicine'},{'value':'Pediatrics'}];
$scope.Gender = [{'value':'Male'},{'value':'Female'}];
$scope.Languages = {};
$scope.Cities = {};
//$scope.lastAction = '';
$scope.searchParam = {};
$("input").removeAttr('disabled');
$scope.searchParam.Distance = $scope.miles[0];
$scope.GetCurrentZip = function (){
try{
var lon, lat;
// console.log('starting geoposition code.');
if("geolocation" in navigator){
window.navigator.geolocation.getCurrentPosition(function(pos){
lat = pos.coords.latitude.toFixed(3);
lon = pos.coords.longitude.toFixed(3);
// console.log(lat + ' ' + lon);
$http.get("/Brokers-en-us/includes/remote/ReturnCurrentZipcode.cfm?Lat=" + lat + "&Lon=" + lon)
.then(function(response){
$scope.searchParam.Zip = response.data;
})
})
}
else{ console.log('No geolocation'); }
}
catch(err) { console.log(err.message); }
}
$scope.GetCityList = function (){
try{
$http.get("/Brokers-en-us/includes/remote/ReturnCityList.cfm")
.then(function(response){
$scope.Cities = response.data.Cities;
})
}
catch(err){}
}
$scope.GetLangList = function (){
try{
$http.get("/Brokers-en-us/includes/remote/ReturnLangList.cfm")
.then(function(response){
$scope.Languages = response.data.Languages;
})
}
catch(err){}
}
$scope.SearchProvider = function(searchParam){
try{
//debugger;
$scope.searchMode = 1;
var queryString='';
if($scope.formModel && $scope.formModel !== searchParam){
$scope.resultsCount = 0;
currentPage = 1;
}
if(searchParam){
//debugger;
$scope.formModel = searchParam;
for(var param in searchParam){
if(searchParam.hasOwnProperty(param)){
var paramValue = searchParam[param].value ? searchParam[param].value : searchParam[param];
if (paramValue.length > 0)
queryString += param + '=' + paramValue + '&';
}
}
}
console.log(queryString);
queryString= '?' + queryString + 'currentpage=' + $scope.currentPage;
$http.get("/Brokers-en-us/includes/remote/ReturnProvidersList.cfm" + queryString)
.then(function(response){
$scope.providers = response.data.provider;
$scope.resultsCount = response.data.rowCount;
if (!$scope.providers){
$scope.NoResults = true;
$scope.ShowResults = false;
$scope.ShowDesc = false;
}
else{
$scope.NoResults = false;
$scope.ShowResults = true;
$scope.ShowDesc = false;
}
})
}
catch(err){ alert('No response.: ' + err.message); }
}
/*Testing purposes*/
$scope.clearTopForm = function(searchParam){
//console.log("I clicked this.")
}
/*Clears the drop downs and input fields*/
$scope.$watch('searchParam.Distance', function(newValue, oldValue) {
//debugger;
if(newValue != ''){
//$scope.lastAction = 'miles';
$scope.searchParam.City = '';
$scope.searchParam.Specialty = '';
$scope.searchParam.Gender = '';
}
});
$scope.$watch('searchParam.Zip', function(newValue, oldValue) {
if(newValue != ''){
//$scope.lastAction = 'miles';
$scope.searchParam.Gender = '';
$scope.searchParam.Specialty = '';
$scope.searchParam.City = '';
}
});
$scope.cityChange = function(){
//debugger;
if($scope.searchParam.City != ''){
//$scope.lastAction = 'city';
$scope.searchParam.Distance = '';
$scope.searchParam.Zip = '';
}
}
$scope.specialtyChange = function(){
//debugger;
if($scope.searchParam.Specialty != ''){
//$scope.lastAction = 'specialty';
$scope.searchParam.Distance = '';
$scope.searchParam.Zip = '';
}
}
$scope.genderChange = function(){
//debugger;
if($scope.searchParam.Gender != ''){
//$scope.lastAction = 'gender';
$scope.searchParam.Distance = '';
$scope.searchParam.Zip = '';
}
}
$scope.$watchGroup(['currentPage'], function(){
try{
if($scope.searchMode == 1){
$scope.SearchProvider($scope.formModel);
}
}
catch(err){}
});
$scope.GetCityList();
$scope.GetLangList();
$scope.GetCurrentZip();
$scope.gotoElement = function (eID){
//http://jsfiddle.net/brettdewoody/y65G5/
// set the location.hash to the id of
// the element you wish to scroll to.
//$location.hash('bottom');
// call $anchorScroll()
var browserWidth = screen.availWidth;
if (browserWidth < 768)
anchorSmoothScroll.scrollTo(eID);
};
});
indexApp.service('anchorSmoothScroll', function(){
this.scrollTo = function(eID) {
// This scrolling function
// is from http://www.itnewb.com/tutorial/Creating-the-Smooth-Scroll-Effect-with-JavaScript
var startY = currentYPosition();
var stopY = elmYPosition(eID);
var distance = stopY > startY ? stopY - startY : startY - stopY;
if (distance < 100) {
scrollTo(0, stopY); return;
}
var speed = Math.round(distance / 100);
if (speed >= 20) speed = 20;
var step = Math.round(distance / 25);
var leapY = stopY > startY ? startY + step : startY - step;
var timer = 0;
if (stopY > startY) {
for ( var i=startY; i<stopY; i+=step ) {
setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
leapY += step; if (leapY > stopY) leapY = stopY; timer++;
} return;
}
for ( var i=startY; i>stopY; i-=step ) {
setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
}
function currentYPosition() {
// Firefox, Chrome, Opera, Safari
if (self.pageYOffset) return self.pageYOffset;
// Internet Explorer 6 - standards mode
if (document.documentElement && document.documentElement.scrollTop)
return document.documentElement.scrollTop;
// Internet Explorer 6, 7 and 8
if (document.body.scrollTop) return document.body.scrollTop;
return 0;
}
function elmYPosition(eID) {
var elm = document.getElementById(eID);
var y = elm.offsetTop;
var node = elm;
while (node.offsetParent && node.offsetParent != document.body) {
node = node.offsetParent;
y += node.offsetTop;
} return y;
}
};
});
indexApp.directive('allowPattern',[allowPatternDirective]);
indexApp.directive('popPopup',[describePopup]);
indexApp.directive('pop', function pop ($tooltip, $timeout) {
var tooltip = $tooltip('pop', 'pop', 'event');
var compile = angular.copy(tooltip.compile);
tooltip.compile = function (element, attrs) {
var first = true;
attrs.$observe('popShow', function (val) {
if (JSON.parse(!first || val || false)) {
$timeout(function(){
element.triggerHandler('event');
});
}
first = false;
});
return compile(element, attrs);
};
return tooltip;
});
indexApp.filter('PhoneNumber', function(){
return function(phoneNumber){
var dash = '-';
var openParen = '(';
var closeParen = ') ';
if(phoneNumber){
var pn = phoneNumber;
pn = [pn.slice(0, 6), dash, pn.slice(6)].join('');
pn = openParen + [pn.slice(0, 3), closeParen, pn.slice(3)].join('');
return pn;
}
return phoneNumber;
}
});
indexApp.filter('Zip', function(){
return function(zipcode){
var dash = '-';
if(zipcode && zipcode.length > 5){
var zc = zipcode;
zc = [zc.slice(0, 5), dash, zc.slice(5)].join('');
return zc;
}
return zipcode;
}
});
function allowPatternDirective(){
return{
restrict: "A",
compile: function(tElement, tAttrs){
return function(scope, element, attrs){
element.bind("keypress", function(event){
var keyCode = event.which || event.keyCode;
var keyCodeChar = String.fromCharCode(keyCode);
if(!keyCodeChar.match(new RegExp(attrs.allowPattern, "i"))){
event.preventDefault();
return false;
}
});
}
}
}
}
function describePopup(){
return {
restrict: 'EA',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover.html'
};
}
})();
(function($) {
// #todo Document this.
$.extend($,{ placeholder: {
browser_supported: function() {
return this._supported !== undefined ?
this._supported :
( this._supported = !!('placeholder' in $('<input type="text">')[0]) );
},
shim: function(opts) {
var config = {
color: '#888',
cls: 'placeholder',
selector: 'input[placeholder], textarea[placeholder]'
};
$.extend(config,opts);
return !this.browser_supported() && $(config.selector)._placeholder_shim(config);
}
}});
$.extend($.fn,{
_placeholder_shim: function(config) {
function calcPositionCss(target)
{
var op = $(target).offsetParent().offset();
var ot = $(target).offset();
return {
top: ot.top - op.top,
left: ot.left - op.left,
width: $(target).width()
};
}
function adjustToResizing(label) {
var $target = label.data('target');
if(typeof $target !== "undefined") {
label.css(calcPositionCss($target));
$(window).one("resize", function () { adjustToResizing(label); });
}
}
return this.each(function() {
var $this = $(this);
if( $this.is(':visible') ) {
if( $this.data('placeholder') ) {
var $ol = $this.data('placeholder');
$ol.css(calcPositionCss($this));
return true;
}
var possible_line_height = {};
if( !$this.is('textarea') && $this.css('height') != 'auto') {
possible_line_height = { lineHeight: $this.css('height'), whiteSpace: 'nowrap' };
}
var isBorderBox = ($this.css('box-sizing') === 'border-box');
var isTextarea = $this.is('textarea');
var ol = $('<label />')
.text($this.attr('placeholder'))
.addClass(config.cls)
.css($.extend({
position:'absolute',
display: 'inline',
'float':'none',
overflow:'hidden',
textAlign: 'left',
color: config.color,
cursor: 'text',
paddingTop: !isTextarea && isBorderBox ? '0' : $this.css('padding-top'),
paddingRight: $this.css('padding-right'),
paddingBottom: !isTextarea && isBorderBox ? '0' : $this.css('padding-bottom'),
paddingLeft: $this.css('padding-left'),
fontSize: $this.css('font-size'),
fontFamily: $this.css('font-family'),
fontStyle: $this.css('font-style'),
fontWeight: $this.css('font-weight'),
textTransform: $this.css('text-transform'),
backgroundColor: 'transparent',
zIndex: 99,
}, possible_line_height))
.css(calcPositionCss(this))
.attr('for', this.id)
.data('target',$this)
.click(function(){
if (!$(this).data('target').is(':disabled')) {
$(this).data('target').focus();
}
})
.insertBefore(this);
$this
.data('placeholder', ol)
.on('keydown', function () {
ol.hide();
})
.on('blur change', function () {
ol[$this.val().length ? 'hide' : 'show']();
})
.triggerHandler('blur');
$(window).one("resize", function () { adjustToResizing(ol); });
}
});
}
});
})(jQuery);
jQuery(document).add(window).bind('ready load', function() {
if (jQuery.placeholder) {
jQuery.placeholder.shim();
}
});
When you use ng-bind-html, AngularJS sometimes consider some contents as unsafe (as your case), so you need to use the $sce service in order to "mark" this content as safe (to be used) like this:
$sce.trustAsHtml("CLINICAL & SOCIAL"); (See demo below)
From $sanitize
The input is sanitized by parsing the HTML into tokens. All safe
tokens (from a whitelist) are then serialized back to properly escaped
html string. This means that no unsafe input can make it into the
returned string.
In this case the "unsafe" part is &
angular
.module('app', [])
.controller('ctrl', ctrl);
function ctrl($scope, $sce) {
$scope.Specialty = $sce.trustAsHtml("CLINICAL & SOCIAL");
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-sanitize.js"></script>
<div ng-app="app" ng-controller="ctrl">
Specialty: <span ng-bind-html="Specialty"></span>
</div>

How to call a custom directive from angular controller

I have a directive which is depending on some input. I want to call that directive from angular controller when ever that input changed. Here is my directive.
var app = angular.module('App', ['ui.bootstrap']);
app.controller('fcController', function($scope, fcService, $uibModal) {
$scope.formatType = '1';
});
app.directive('fcsaNumber', function($filter) {
var addCommasToInteger, controlKeys, hasMultipleDecimals, isNotControlKey, isNotDigit, isNumber, makeIsValid, makeMaxDecimals, makeMaxDigits, makeMaxNumber, makeMinNumber;
isNumber = function(val) {
return !isNaN(parseFloat(val)) && isFinite(val);
};
isNotDigit = function(which) {
return which < 45 || which > 57 || which === 47;
};
controlKeys = [0, 8, 13];
isNotControlKey = function(which) {
return controlKeys.indexOf(which) === -1;
};
hasMultipleDecimals = function(val) {
return (val != null) && val.toString().split('.').length > 2;
};
makeMaxDecimals = function(maxDecimals) {
var regexString, validRegex;
if (maxDecimals > 0) {
regexString = "^-?\\d*\\.?\\d{0," + maxDecimals + "}$";
} else {
regexString = "^-?\\d*$";
}
validRegex = new RegExp(regexString);
return function(val) {
return validRegex.test(val);
};
};
makeMaxNumber = function(maxNumber) {
return function(val, number) {
return number <= maxNumber;
};
};
makeMinNumber = function(minNumber) {
return function(val, number) {
return number >= minNumber;
};
};
makeMaxDigits = function(maxDigits) {
var validRegex;
validRegex = new RegExp("^-?\\d{0," + maxDigits + "}(\\.\\d*)?$");
return function(val) {
return validRegex.test(val);
};
};
makeIsValid = function(options) {
var validations;
validations = [];
if (options.maxDecimals != null) {
validations.push(makeMaxDecimals(options.maxDecimals));
}
if (options.max != null) {
validations.push(makeMaxNumber(options.max));
}
if (options.min != null) {
validations.push(makeMinNumber(options.min));
}
if (options.maxDigits != null) {
validations.push(makeMaxDigits(options.maxDigits));
}
return function(val) {
var i, number, _i, _ref;
if (!isNumber(val)) {
return false;
}
if (hasMultipleDecimals(val)) {
return false;
}
number = Number(val);
for (i = _i = 0, _ref = validations.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
if (!validations[i](val, number)) {
return false;
}
}
return true;
};
};
addCommasToInteger = function(val) {
var commas, decimals, wholeNumbers;
decimals = val.indexOf('.') == -1 ? '.00' : val.replace(/^\d+(?=\.)/, '');
wholeNumbers = val.replace(/(\.\d+)$/, '');
commas = wholeNumbers.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
return "" + commas + decimals.substring(0, 3);
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
options: '#fcsaNumber',
},
link: function(scope, elem, attrs, ngModelCtrl) {
var isValid, options;
options = {};
if (scope.options != null) {
options = scope.$eval(scope.options);
}
isValid = makeIsValid(options);
ngModelCtrl.$parsers.unshift(function(viewVal) {
var noCommasVal;
noCommasVal = viewVal.replace(/,/g, '');
if (isValid(noCommasVal) || !noCommasVal) {
ngModelCtrl.$setValidity('fcsaNumber', true);
return noCommasVal;
} else {
ngModelCtrl.$setValidity('fcsaNumber', false);
return void 0;
}
});
ngModelCtrl.$formatters.push(function(val) {
if ((options.nullDisplay != null) && (!val || val === '')) {
return options.nullDisplay;
}
if ((val == null) || !isValid(val)) {
return val;
}
ngModelCtrl.$setValidity('fcsaNumber', true);
val = addCommasToInteger(val.toString());
if (options.key == 1) {
options.prepend = 'S/.';
}
if (options.key == 2) {
options.prepend = '$';
}
if (options.prepend != null) {
val = "" + options.prepend + val;
}
if (options.append != null) {
val = "" + val + options.append;
}
return val;
});
elem.on('blur', function() {
var formatter, viewValue, _i, _len, _ref;
viewValue = ngModelCtrl.$modelValue;
if ((viewValue == null) || !isValid(viewValue)) {
return;
}
_ref = ngModelCtrl.$formatters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
formatter = _ref[_i];
viewValue = formatter(viewValue);
}
ngModelCtrl.$viewValue = viewValue;
return ngModelCtrl.$render();
});
elem.on('focus', function() {
var val;
val = elem.val();
if (options.prepend != null) {
val = val.replace(options.prepend, '');
}
if (options.append != null) {
val = val.replace(options.append, '');
}
elem.val(val.replace(/,/g, ''));
return elem[0].select();
});
if (options.preventInvalidInput === true) {
return elem.on('keypress', function(e) {
if (isNotDigit(e.which && isNotControlKey(e.which))) {
return e.preventDefault();
}
});
}
}
};
});
HTML:
<input type ="text" ng-model ="currency" fcsa-number="{key : {{formatType}}}">
Here $scope.formatType = '1'; is the input. If this formatType changed then That directive need to call. One friend suggested me this
link: function(scope,elem,attr,ctrl){
scope.$watch('fcsaNumber', function(){
// do your stuff
});
but it is not calling my directive manually.
even I have tried with
.controller("ctrl", function($scope) {
$scope.$watch('formatType', function() {
$scope.$broadcast("call_dir")
})
})
return {
restrict: 'A',
require: 'ngModel',
scope: {
options: '#fcsaNumber',
},
link: function(scope, elem, attrs, ngModelCtrl) {
var isValid, options;
options = {};
scope.$on('call_dir', function(ev) {
//your code
})
};
You need to watch the value that is going to change in your controller. In your case it is formatType.
You should try to use this expression in your isolated scope:
scope: {
options: '=fcsaNumber',
}
And if you want the event broadcasting to work, i.e. the $scope.$bradcast approach, then make sure your directive is a child of the controller scope in the scope hierarchy. Else a easier way (but not always recommended) would be to broadcast your event from $rootScope.
So something like this should also work (with your event based solution):
.controller("ctrl", function($scope, $rootScope) {
$scope.$watch('formatType', function() {
$rootScope.$broadcast("call_dir")
})
})

Trying to reload a function on click of an image

I have a JSON file and I am trying to reload a function inside my code but I cant seem to get it to work. After you click an item from the page, it shows the items contents etc. At the bottom I have a similar product for each item. What i want to do is reload the page on click of the similar product image and show the correct item on the page but I cant seem to make it work. Any help or suggestions is very much appreciated thanks!
I included a working example below please visit the codepen link:
http://codepen.io/tcarp/pen/aBPLqX
'use strict';
$.ajax({
dataType: "jsonp",
url: '',
success: function(json){
//check for window hash and display product category
var categoryHash = window.location.hash;
switch(categoryHash)
{
case '#tomatoes':
displayTomatoes();
break;
default:
displayAll();
break;
}
//display product category based on click
$("#tomatoes").click(function(event){
displayTomatoes();
event.preventDefault();
window.location.hash = '#tomatoes';
});
$("#displayall").click(function(event){
displayAll();
});
//display all products function
function displayAll() {
var categoryImage = '';
$.each(json, function (i, item) {
categoryImage += '<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">' + '' + '<img class="img-responsive img-hover productImagesCategory" src="' + item.imageURL + '">' + '<h3>' + item.itemName + '</h3>' + '' + '</div>';
});
$('#imagesCategoryProducts').hide().html(categoryImage).fadeIn('slow');
//show individual product function on click
$(".showProduct").click(function(event){
//hide all current products
$('#productCategories').hide();
//get passed data from other function
var clickedItemName = '<h1>' + $(this).data('itemname') + '</h1>';
var clickedItemUPC = $(this).data('itemupc');
var clickedItemOZ = $(this).data('itemoz')
var clickedItemDescription = '<p>' + $(this).data('itemdescription') + '</p>';
var clickedItemImage = '<img class="img-responsive img-rounded center-block" src="' + $(this).data('itemimage') + '">';
var clickedItemGluten = $(this).data('itemgluten');
var clickedItemBPA = $(this).data('itembpa');
var clickedItemGMO = $(this).data('itemgmo');
var clickedItemLowSodium = $(this).data('itemlowsodium');
var clickedItemOrganic = $(this).data('itemorganic');
var clickedItemPageURL = $(this).data('itempageurl');
//check if clicked data equals correct item
$.each(json, function (i, item) {
if (item.itemName === clickedItemName) {
clickedItemName
}
if (item.itemFullUPC === clickedItemUPC) {
clickedItemUPC
}
if (item.itemPackSize === clickedItemOZ) {
clickedItemOZ
}
if (item.itemDescription === clickedItemDescription) {
clickedItemDescription
}
if (item.imageURL === clickedItemImage) {
clickedItemImage
}
if (item.itemGlutenFree === clickedItemGluten) {
clickedItemGluten
}
if (item.itemBPAFree === clickedItemBPA) {
clickedItemBPA
}
if (item.itemGMOFree === clickedItemGMO) {
clickedItemGMO
}
if (item.itemOrganic === clickedItemOrganic) {
clickedItemOrganic
}
if (item.itemLowSodium === clickedItemLowSodium) {
clickedItemLowSodium
}
//assign window hash to each product
if (item.itemFullUPC === clickedItemPageURL) {
event.preventDefault();
clickedItemPageURL = clickedItemPageURL.replace(/\s/g, '');
window.location.hash = clickedItemPageURL;
}
});
//remove extra characters from UPC
var originalUPC = clickedItemUPC;
var strippedUPC = '<h2>' + originalUPC.slice(1, -1); + '</h2>';
//remove characters after slash from Pack Size
var originalPackSize = clickedItemOZ;
var strippedPackSize = '<h2>' + clickedItemOZ.substring(clickedItemOZ.lastIndexOf("/") + 1); + '</h2>';
//show individual product information
$('#productSocialShare').show();
$('#individualProduct').show();
$('#relatedProducts').show();
//append product information to appropriate DIV
$('#productTitle').html(clickedItemName);
$('#productUPC').html(strippedUPC);
$('#productOZ').html(strippedPackSize);
$('#productDescription').html(clickedItemDescription);
$('#productImage').html(clickedItemImage);
//check if gluten free is true and show image
if (clickedItemGluten == "Y") {
clickedItemGluten = '<img class="img-responsive img-rounded img-margin" src="http://www.cento.com/DEV/images/misc/gluten_free_test.jpg">';
$('#productGlutenFree').html(clickedItemGluten);
$('#productGlutenFree').show();
} else {
$('#productGlutenFree').hide();
}
//check if bpa free is true and show image
if (clickedItemBPA == "Y") {
clickedItemBPA = '<img class="img-responsive img-rounded img-margin" src="http://www.cento.com/DEV/images/misc/bpa_free_test.jpg">';
$('#productBPAFree').html(clickedItemBPA);
$('#productBPAFree').show();
} else {
$('#productBPAFree').hide();
}
//check if gmo free is true and show image
if (clickedItemGMO == "Y") {
clickedItemGMO = '<img class="img-responsive img-rounded img-margin" src="http://www.cento.com/DEV/images/misc/gmo_test.jpg">';
$('#productGMOFree').html(clickedItemGMO);
$('#productGMOFree').show();
} else {
$('#productGMOFree').hide();
}
//check if organic is true and show image
if (clickedItemOrganic == "Y") {
clickedItemOrganic = '<img class="img-responsive img-rounded img-margin" src="http://www.cento.com/DEV/images/misc/organic_test.jpg">';
$('#productOrganic').html(clickedItemOrganic);
$('#productOrganic').show();
} else {
$('#productOrganic').hide();
}
//check if low sodium is true and show image
if (clickedItemLowSodium == "Y") {
clickedItemLowSodium = '<img class="img-responsive img-rounded img-margin" src="http://www.cento.com/DEV/images/misc/low_sodium_test.jpg">';
$('#productLowSodium').html(clickedItemLowSodium);
$('#productLowSodium').show();
} else {
$('#productLowSodium').hide();
}
//show random recipe for each item
var url = 'http://www.cento.com/DEV/recipes.json';
$.getJSON(url, function(json) {
var randomRecipe = json[Math.floor(Math.random() * json.length)];
randomRecipe = '<div>' + '' + '<img class="img-responsive img-hover" src="' + randomRecipe.recipeImageCategoryURL + '">' + '' + '' + '<h3 class="similarProductSubCategoryImgCaption">' + randomRecipe.recipeName + '</h3>' + '' + '</div>';
$('#featuredRecipe').append(randomRecipe);
});
//show similar product
var categoryItems = [];
$.each(json, function(i, item){
if(item.itemCommodity == '1120' && item.itemBrandLetter == "C") categoryItems.push(item);
if(item.itemCommodity == '2120' && item.itemBrandLetter == "C") categoryItems.push(item);
});
var similarProduct= '';
$.each(json, function(i,item){
similarProduct = categoryItems[Math.floor(Math.random()*categoryItems.length)];
similarProduct += '<div>' + '' + '<h3 class="similarProductSubCategoryImgCaption">' + similarProduct.itemName + '</h3>' + '' + '</div>';
});
$('#productSimilar').append(similarProduct);
});
closeNav();
}
}
});
Got this to work. I was able to add the function to the body on click method and pass in all the data from the href. Works!

Add search filter inside the select dropdown in AngularJS

I want to add a search filter inside a select dropdown in angularJS.
I have used ng-options to list down the options and used filter to filter out the data in the search box , but the problem is that the search box is not coming inside(or under) select dropdown. (When I click the select dropdown, it shows a search filter and below it has all the options)
Below is the code for your reference :
<div class="rowMargin">
<label class="control-label" for="entitySel">Entity:</label>
<div class="controls">
<select id="entityId" class="input-medium" type="text" name="entityId" ng-model="payment.entityId" ng-options="entityOpt for entityOpt in paymentEntityOptions">
<option value="">Select</option>
</select>
<span ng-show=" submitted && addPayment.entityId.$error.required">
<label class="error">Please provide entity Id </label>
</span>
<div ng-show="payment.entityId == \'Individual\'">
<span>
<select ng-model="payment.entity.individual" ng-options = "individual for individual in individualEntities | filter : filterEntity">
<option value="">Select Individual Entity</option>
<option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</option>
</select>
</span>
</div>
<div ng-show="payment.entityId == \'Group\'">
<span>
<select ng-model="payment.entity.group" ng-options = "group for group in groupEntities | filter : filterEntity">
<option value="">Select Group Entity</option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</select>
</span>
</div>
</div>
I have used the bootstrap button with class 'dropdown-toggle' and on click of the button I have appended an input search box as following :
<div class="dropdown pull-right makePaymentDropdownMainDiv" auto-close="outsideClick">
<button class="btn btn-default dropdown-toggle makePaymentDropdownBtn" type="button" id="individualDrop" data-toggle="dropdown">{{payment.entity}}<span class="caret pull-right"></span></button>
<span ng-show="submitted"><label class="error">Select an Individual</label></span>
<ul class="dropdown-menu makePaymentDropdownUlStyle" role="menu" aria-labelledby="individualDrop">
<input disable-auto-close type="search" ng-model="serchFilter" class="makePaymentDropdownSearchBox" placeholder="Search"></input>
<li role="presentation" ng-repeat="indi in individuals | filter: serchFilter"><a role="menuitem" ng-click="selectEntity(indi)">{{indi}}</a></li>
</ul>
</div>
Showing the 'li' using ng-repeat.
Remember to add auto-close="outsideClick" to your dropdown so that it doesn't close on filtering attempt.
Sorry, I'm rather late to the party, but to me it sounds like you need acute-select, an open source extension (MIT license) to Angular that does exactly this, without further dependencies.
They also have a demo page, which shows what it can do nicely.
you can use easy and best way to search filter inside the select dropdown in AngularJS
Working Demo : http://plnkr.co/edit/o767Mg6fQoyc7jKq77If?p=preview
(function (angular, undefined) {
'use strict';
// TODO: Move to polyfill?
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
/**
* A replacement utility for internationalization very similar to sprintf.
*
* #param replace {mixed} The tokens to replace depends on type
* string: all instances of $0 will be replaced
* array: each instance of $0, $1, $2 etc. will be placed with each array item in corresponding order
* object: all attributes will be iterated through, with :key being replaced with its corresponding value
* #return string
*
* #example: 'Hello :name, how are you :day'.format({ name:'John', day:'Today' })
* #example: 'Records $0 to $1 out of $2 total'.format(['10', '20', '3000'])
* #example: '$0 agrees to all mentions $0 makes in the event that $0 hits a tree while $0 is driving drunk'.format('Bob')
*/
function format(value, replace) {
if (!value) {
return value;
}
var target = value.toString();
if (replace === undefined) {
return target;
}
if (!angular.isArray(replace) && !angular.isObject(replace)) {
return target.split('$0').join(replace);
}
var token = angular.isArray(replace) && '$' || ':';
angular.forEach(replace, function (value, key) {
target = target.split(token + key).join(value);
});
return target;
}
var module = angular.module('AxelSoft', []);
module.value('customSelectDefaults', {
displayText: 'Select...',
emptyListText: 'There are no items to display',
emptySearchResultText: 'No results match "$0"',
addText: 'Add',
searchDelay: 300
});
module.directive('customSelect', ['$parse', '$compile', '$timeout', '$q', 'customSelectDefaults', function ($parse, $compile, $timeout, $q, baseOptions) {
var CS_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, controller) {
var customSelect = attrs.customSelect;
if (!customSelect) {
throw new Error('Expected custom-select attribute value.');
}
var match = customSelect.match(CS_OPTIONS_REGEXP);
if (!match) {
throw new Error("Expected expression in form of " +
"'_select_ (as _label_)? for _value_ in _collection_[ track by _id_]'" +
" but got '" + customSelect + "'.");
}
elem.addClass('dropdown custom-select');
// Ng-Options break down
var displayFn = $parse(match[2] || match[1]),
valueName = match[3],
valueFn = $parse(match[2] ? match[1] : valueName),
values = match[4],
valuesFn = $parse(values),
track = match[5],
trackByExpr = track ? " track by " + track : "",
dependsOn = attrs.csDependsOn;
var options = getOptions(),
timeoutHandle,
lastSearch = '',
focusedIndex = -1,
matchMap = {};
var itemTemplate = elem.html().trim() || '{{' + (match[2] || match[1]) + '}}',
dropdownTemplate =
'<a class="dropdown-toggle" data-toggle="dropdown" href ng-class="{ disabled: disabled }">' +
'<span>{{displayText}}</span>' +
'<b></b>' +
'</a>' +
'<div class="dropdown-menu">' +
'<div stop-propagation="click" class="custom-select-search">' +
'<input class="' + attrs.selectClass + '" type="text" autocomplete="off" ng-model="searchTerm" />' +
'</div>' +
'<ul role="menu">' +
'<li role="presentation" ng-repeat="' + valueName + ' in matches' + trackByExpr + '">' +
'<a role="menuitem" tabindex="-1" href ng-click="select(' + valueName + ')">' +
itemTemplate +
'</a>' +
'</li>' +
'<li ng-hide="matches.length" class="empty-result" stop-propagation="click">' +
'<em class="muted">' +
'<span ng-hide="searchTerm">{{emptyListText}}</span>' +
'<span class="word-break" ng-show="searchTerm">{{ format(emptySearchResultText, searchTerm) }}</span>' +
'</em>' +
'</li>' +
'</ul>' +
'<div class="custom-select-action">' +
(typeof options.onAdd === "function" ?
'<button type="button" class="btn btn-primary btn-block add-button" ng-click="add()">{{addText}}</button>' : '') +
'</div>' +
'</div>';
// Clear element contents
elem.empty();
// Create dropdown element
var dropdownElement = angular.element(dropdownTemplate),
anchorElement = dropdownElement.eq(0).dropdown(),
inputElement = dropdownElement.eq(1).find(':text'),
ulElement = dropdownElement.eq(1).find('ul');
// Create child scope for input and dropdown
var childScope = scope.$new(true);
configChildScope();
// Click event handler to set initial values and focus when the dropdown is shown
anchorElement.on('click', function (event) {
if (childScope.disabled) {
return;
}
childScope.$apply(function () {
lastSearch = '';
childScope.searchTerm = '';
});
focusedIndex = -1;
inputElement.focus();
// If filter is not async, perform search in case model changed
if (!options.async) {
getMatches('');
}
});
if (dependsOn) {
scope.$watch(dependsOn, function (newVal, oldVal) {
if (newVal !== oldVal) {
childScope.matches = [];
childScope.select(undefined);
}
});
}
// Event handler for key press (when the user types a character while focus is on the anchor element)
anchorElement.on('keypress', function (event) {
if (!(event.altKey || event.ctrlKey)) {
anchorElement.click();
}
});
// Event handler for Esc, Enter, Tab and Down keys on input search
inputElement.on('keydown', function (event) {
if (!/(13|27|40|^9$)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
switch (event.keyCode) {
case 27: // Esc
anchorElement.dropdown('toggle');
break;
case 13: // Enter
selectFromInput();
break;
case 40: // Down
focusFirst();
break;
case 9:// Tab
anchorElement.dropdown('toggle');
break;
}
});
// Event handler for Up and Down keys on dropdown menu
ulElement.on('keydown', function (event) {
if (!/(38|40)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
var items = ulElement.find('li > a');
if (!items.length) return;
if (event.keyCode == 38) focusedIndex--; // up
if (event.keyCode == 40 && focusedIndex < items.length - 1) focusedIndex++; // down
//if (!~focusedIndex) focusedIndex = 0;
if (focusedIndex >= 0) {
items.eq(focusedIndex)
.focus();
} else {
focusedIndex = -1;
inputElement.focus();
}
});
resetMatches();
// Compile template against child scope
$compile(dropdownElement)(childScope);
elem.append(dropdownElement);
// When model changes outside of the control, update the display text
controller.$render = function () {
setDisplayText();
};
// Watch for changes in the default display text
childScope.$watch(getDisplayText, setDisplayText);
childScope.$watch(function () { return elem.attr('disabled'); }, function (value) {
childScope.disabled = value;
});
childScope.$watch('searchTerm', function (newValue) {
if (timeoutHandle) {
$timeout.cancel(timeoutHandle);
}
var term = (newValue || '').trim();
timeoutHandle = $timeout(function () {
getMatches(term);
},
// If empty string, do not delay
(term && options.searchDelay) || 0);
});
// Support for autofocus
if ('autofocus' in attrs) {
anchorElement.focus();
}
var needsDisplayText;
function setDisplayText() {
var locals = { };
locals[valueName] = controller.$modelValue;
var text = displayFn(scope, locals);
if (text === undefined) {
var map = matchMap[hashKey(controller.$modelValue)];
if (map) {
text = map.label;
}
}
needsDisplayText = !text;
childScope.displayText = text || options.displayText;
}
function getOptions() {
return angular.extend({}, baseOptions, scope.$eval(attrs.customSelectOptions));
}
function getDisplayText() {
options = getOptions();
return options.displayText;
}
function focusFirst() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
focusedIndex = 0;
opts.eq(0).focus();
}
}
// Selects the first element on the list when the user presses Enter inside the search input
function selectFromInput() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
var ngRepeatItem = opts.eq(0).scope();
var item = ngRepeatItem[valueName];
childScope.$apply(function () {
childScope.select(item);
});
anchorElement.dropdown('toggle');
}
}
function getMatches(searchTerm) {
var locals = { $searchTerm: searchTerm }
$q.when(valuesFn(scope, locals)).then(function (matches) {
if (!matches) return;
if (searchTerm === inputElement.val().trim()/* && hasFocus*/) {
matchMap = {};
childScope.matches.length = 0;
for (var i = 0; i < matches.length; i++) {
locals[valueName] = matches[i];
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(matches[i]);
}
//childScope.matches = matches;
}
if (needsDisplayText) setDisplayText();
}, function() {
resetMatches();
});
}
function resetMatches() {
childScope.matches = [];
focusedIndex = -1;
};
function configChildScope() {
childScope.addText = options.addText;
childScope.emptySearchResultText = options.emptySearchResultText;
childScope.emptyListText = options.emptyListText;
childScope.select = function (item) {
var locals = {};
locals[valueName] = item;
var value = valueFn(childScope, locals);
//setDisplayText(displayFn(scope, locals));
childScope.displayText = displayFn(childScope, locals) || options.displayText;
controller.$setViewValue(value);
anchorElement.focus();
typeof options.onSelect === "function" && options.onSelect(item);
};
childScope.add = function () {
$q.when(options.onAdd(), function (item) {
if (!item) return;
var locals = {};
locals[valueName] = item;
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(item);
childScope.select(item);
});
};
childScope.format = format;
setDisplayText();
}
var current = 0;
function hashKey(obj) {
if (obj === undefined) return 'undefined';
var objType = typeof obj,
key;
if (objType == 'object' && obj !== null) {
if (typeof (key = obj.$$hashKey) == 'function') {
// must invoke on object to keep the right this
key = obj.$$hashKey();
} else if (key === undefined) {
key = obj.$$hashKey = 'cs-' + (current++);
}
} else {
key = obj;
}
return objType + ':' + key;
}
}
};
}]);
module.directive('stopPropagation', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var events = attrs['stopPropagation'];
elem.bind(events, function (event) {
event.stopPropagation();
});
}
};
});
})(angular);
<body ng-app="Demo">
<div class="container" ng-controller="DemoController">
<label>Level 1</label>
<div custom-select="g for g in nestedItemsLevel1 | filter: $searchTerm" custom-select-options="level1Options" ng-model="level1"></div>
<label>Level 2</label>
<div custom-select="g for g in nestedItemsLevel2 | filter: $searchTerm" ng-model="level2" cs-depends-on="level1"></div>
</div>
<!-- basic scripts -->
<!--[if !IE]> -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<![endif]-->
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="js/customSelect.js"></script>
<script>
(function () {
var app = angular.module('Demo', ['AxelSoft']);
app.controller('DemoController', ['$scope', '$timeout', '$q', function ($scope, $timeout, $q) {
$scope.searchAsync = function (term) {
// No search term: return initial items
if (!term) {
return ['Item 1', 'Item 2', 'Item 3'];
}
var deferred = $q.defer();
$timeout(function () {
var result = [];
for (var i = 1; i <= 3; i++)
{
result.push(term + ' ' + i);
}
deferred.resolve(result);
}, 300);
return deferred.promise;
};
$scope.nestedItemsLevel1 = ['Item 1', 'Item 2', 'Item 3'];
$scope.level1 = $scope.nestedItemsLevel1[0];
$scope.level1Options = {
onSelect: function (item) {
var items = [];
for (var i = 1; i <= 5; i++) {
items.push(item + ': ' + 'Nested ' + i);
}
$scope.nestedItemsLevel2 = items;
}
};
$scope.nestedItemsLevel2 = [];
$scope.level1Options.onSelect($scope.nestedItemsLevel1[0]);
}]);
})();
</script>
</body>
https://docs.angularjs.org/api/ng/directive/select
There can be only one hard coded in a ngOption.

Codeception acceptance test error for save/reset

I am trying to perform acceptance tests for my website using Codeception, and I am experiencing a strange error due to a reset button on the form I am testing. Basically, my test for clicking on 'Save' works only if either the reset button on my form is AFTER the Save button, or if the reset button is left off the form altogether. If the reset button is inserted in the form before the save button, Codeception throws an Unreachable field "reset" error. Here is my Codeception code:
<?php
$I = new WebGuy($scenario);
$I->wantTo('find an employee in the database');
$I->amOnPage('/employees/find/');
$I->fillField('employeeLookup[first_name]', 'Sergi');
$I->click('Save', '#employeeLookup_save');
$I->see('Based on your search for Sergi, the following employees were found:');
$I->see('Remmele');
$I->see('Feb 28 1992');
And here is my HTML (much of it being generated from Symfony2):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Find existing employee</title>
</head>
<body>
<div id="content">
<p>Hello, enter either the first name, or the last name of the employee
you are searching for.</p>
<form name="employeeLookup" method="post" action="">
<div><label for="employeeLookup_first_name" class="required">Name: </label><input type="text" id="employeeLookup_first_name" name="employeeLookup[first_name]" required="required" /></div>
<div><button type="reset" id="employeeLookup_reset" name="employeeLookup[reset]">Reset</button></div>
<div><button type="submit" id="employeeLookup_save" name="employeeLookup[save]">Save</button></div>
<input type="hidden" id="employeeLookup__token" name="employeeLookup[_token]" value="RcpMVTGgB6WhKgDoXXRwmV_l4AFYKWTZko-dnBDhhvM" /></form>
</div>
<div id="sfwdte5d291" class="sf-toolbar" style="display: none"></div><script>/*<![CDATA[*/ Sfjs = (function() { "use strict"; var noop = function() {}, profilerStorageKey = 'sf2/profiler/', request = function(url, onSuccess, onError, payload, options) { var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); options = options || {}; xhr.open(options.method || 'GET', url, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onreadystatechange = function(state) { if (4 === xhr.readyState && 200 === xhr.status) { (onSuccess || noop)(xhr); } else if (4 === xhr.readyState && xhr.status != 200) { (onError || noop)(xhr); } }; xhr.send(payload || ''); }, hasClass = function(el, klass) { return el.className.match(new RegExp('\\b' + klass + '\\b')); }, removeClass = function(el, klass) { el.className = el.className.replace(new RegExp('\\b' + klass + '\\b'), ' '); }, addClass = function(el, klass) { if (!hasClass(el, klass)) { el.className += " " + klass; } }, getPreference = function(name) { if (!window.localStorage) { return null; } return localStorage.getItem(profilerStorageKey + name); }, setPreference = function(name, value) { if (!window.localStorage) { return null; } localStorage.setItem(profilerStorageKey + name, value); }; return { hasClass: hasClass, removeClass: removeClass, addClass: addClass, getPreference: getPreference, setPreference: setPreference, request: request, load: function(selector, url, onSuccess, onError, options) { var el = document.getElementById(selector); if (el && el.getAttribute('data-sfurl') !== url) { request( url, function(xhr) { el.innerHTML = xhr.responseText; el.setAttribute('data-sfurl', url); removeClass(el, 'loading'); (onSuccess || noop)(xhr, el); }, function(xhr) { (onError || noop)(xhr, el); }, options ); } return this; }, toggle: function(selector, elOn, elOff) { var i, style, tmp = elOn.style.display, el = document.getElementById(selector); elOn.style.display = elOff.style.display; elOff.style.display = tmp; if (el) { el.style.display = 'none' === tmp ? 'none' : 'block'; } return this; } } })();/*]]>*/</script><script>/*<![CDATA[*/ (function () { Sfjs.load( 'sfwdte5d291', '/employees/app_dev.php/_wdt/e5d291', function(xhr, el) { el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none'; if (el.style.display == 'none') { return; } if (Sfjs.getPreference('toolbar/displayState') == 'none') { document.getElementById('sfToolbarMainContent-e5d291').style.display = 'none'; document.getElementById('sfToolbarClearer-e5d291').style.display = 'none'; document.getElementById('sfMiniToolbar-e5d291').style.display = 'block'; } else { document.getElementById('sfToolbarMainContent-e5d291').style.display = 'block'; document.getElementById('sfToolbarClearer-e5d291').style.display = 'block'; document.getElementById('sfMiniToolbar-e5d291').style.display = 'none'; } }, function(xhr) { if (xhr.status !== 0) { confirm('An error occurred while loading the web debug toolbar (' + xhr.status + ': ' + xhr.statusText + ').\n\nDo you want to open the profiler?') && (window.location = '/employees/app_dev.php/_profiler/e5d291'); } } ); })();/*]]>*/</script>
</body>
</html>
Finally, here is the relevant output of the error message from Codeception:
1) Failed to find an employee in the database in FindEmployeeCept.php
Sorry, I couldn't click "Save","#employeeLookup_save":
Behat\Mink\Exception\ElementException: Exception thrown by ((//html/descendant-or-self::*[#id = 'employeeLookup_save'])[1]/.//input[./#type = 'submit' or ./#type = 'image' or ./#type = 'button'][(((./#id = 'Save' or ./#name = 'Save') or contains(./#value, 'Save')) or contains(./#title, 'Save'))] | .//input[./#type = 'image'][contains(./#alt, 'Save')] | .//button[((((./#id = 'Save' or ./#name = 'Save') or contains(./#value, 'Save')) or contains(normalize-space(string(.)), 'Save')) or contains(./#title, 'Save'))] | .//input[./#type = 'image'][contains(./#alt, 'Save')] | .//*[./#role = 'button'][(((./#id = 'Save' or ./#name = 'Save') or contains(./#value, 'Save')) or contains(./#title, 'Save') or contains(normalize-space(string(.)), 'Save'))])[1]
Unreachable field "reset"
Again, if the reset button is rendered after the save button in the HTML, the acceptance tests pass just fine. Also, if the reset button is left off of the form entirely, the acceptance test passes as well. Does anyone have any idea what is causing this?