Access to scope variable in custom directive dynamically - angularjs

I try to write a dynamic field validation. if a check box is checked a new section is shown in my page. As a result, in the new section some new inputs should be filled.
I write a custom directive like this
<input .... multiValidation="Street,City,PostalCode" />
my directive code is
app.directive('multiValidation', function () {
return function (scope, iElement, iAttrs) {
var parts = iAttrs.myvalidate.split(',');
scope.$watch('CustomerModel.Billable', function (val) {
if (scope.CustomerModel.Billable) {
angular.forEach(parts, function (part) {
var element = angular.element('[ng-model="CustomerModel.' + part + '"]');
scope.$watch('CustomerModel.' + part, function (value) {
if (value == null || value == "") {
scope.CustomerForm.$setValidity("CustomerForm", false);
element.addClass("ng-invalid ng-invalid-required");
}
else {
element.addClass("ng-valid ng-valid-required");
var validate = true;
angular.forEach(parts, function (part) {
if (scope["CustomerModel." + part ] == "")
validate = false;
});
scope.CustomerForm.$setValidity("CustomerForm", validate);
}
});
});
} else {
if (scope.CustomerModel.LastName == "" || scope.CustomerModel.LastName == null)
scope.CustomerForm.$setValidity("CustomerForm", false);
if (scope.CustomerModel.LastName != "" || scope.CustomerModel.LastName != null)
scope.CustomerForm.$setValidity("CustomerForm", true);
}
}, true);
};});
my problem is in the following line, I cannot access to the scope variable dynamically in custom directive.
scope["CustomerModel." + part ]
please guide me!!

the CustomerModel is also a variable of $scope. then I load CustomerModel form data base which it contains more property.
I can access to property of CustomerModel like following
$scope.CustomerModel.postalCode
the following way is wrong for dynamically access to them
scope['CustomerModel.postalCode']
the correct way is
scope['CustomerModel']['postalCode']
the whole of code is
app.directive('myvalidate', function () {
return function (scope, iElement, iAttrs) {
var parts = iAttrs.myvalidate.split(',');
scope.$watch('CustomerModel.billable', function (val) {
if (scope.CustomerModel.billable) {
angular.forEach(parts, function (part) {
var element = angular.element('[ng-model="CustomerModel.' + part + '"]');
scope.$watch('CustomerModel.' + part, function (value) {
if (value == null || value == "") {
scope.CustomerForm.$setValidity("CustomerForm", false);
element.addClass("ng-invalid ng-invalid-required");
}
else {
element.addClass("ng-valid ng-valid-required");
var validate = true;
angular.forEach(parts, function (p) {
if (scope['CustomerModel'][p] == "" || scope['CustomerModel'][p] == "undefined" || scope['CustomerModel'][p] == null)
validate = false;
});
if (scope.CustomerModel.customerLastName == "" || scope.CustomerModel.customerLastName == null) validate = false;
scope.CustomerForm.$setValidity("CustomerForm", validate);
}
});
});
} else {
if (scope.CustomerModel.customerLastName == "" || scope.CustomerModel.customerLastName == null)
scope.CustomerForm.$setValidity("CustomerForm", false);
if (scope.CustomerModel.customerLastName != "" || scope.CustomerModel.customerLastName != null)
scope.CustomerForm.$setValidity("CustomerForm", true);
}
}, true);
};
});
and the directive
<div class="panel-body" myvalidate="street,city">

Related

How to add thousand separating commas for numbers in angularJS?

I simply want to convert a string of numbers to a number which will be displayed using thousand separated commas.
var value = "123456";
I want to display "123,465" in a grid.
I have looked some documentation on this but everything is about displaying it in HTML.
I want to display this in a dynamic grid.
function numberRenderer (params) {
return new Number (params.value);
}
I want to format the number so that I can convert that into a string for display.
Use a filter ...
HTML usage
{{ number_expression | number : fractionSize}}
Js usage
$filter('number')(number, fractionSize)
I appreciated the answer from #jbrown, but I was also hoping to find some type of solution to add commas to an input field as the user enters numbers. I ended up finding this directive which proved to be exactly what I needed.
HTML
<input type="text" ng-model="someNumber" number-input />
JAVASCRIPT
myApp.directive('numberInput', function($filter) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.push(function(modelValue) {
return setDisplayNumber(modelValue, true);
});
// it's best to change the displayed text using elem.val() rather than
// ngModelCtrl.$setViewValue because the latter will re-trigger the parser
// and not necessarily in the correct order with the changed value last.
// see http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/
// for an explanation of how ngModelCtrl works.
ngModelCtrl.$parsers.push(function(viewValue) {
setDisplayNumber(viewValue);
return setModelNumber(viewValue);
});
// occasionally the parser chain doesn't run (when the user repeatedly
// types the same non-numeric character)
// for these cases, clean up again half a second later using "keyup"
// (the parser runs much sooner than keyup, so it's better UX to also do it within parser
// to give the feeling that the comma is added as they type)
elem.bind('keyup focus', function() {
setDisplayNumber(elem.val());
});
function setDisplayNumber(val, formatter) {
var valStr, displayValue;
if (typeof val === 'undefined') {
return 0;
}
valStr = val.toString();
displayValue = valStr.replace(/,/g, '').replace(/[A-Za-z]/g, '');
displayValue = parseFloat(displayValue);
displayValue = (!isNaN(displayValue)) ? displayValue.toString() : '';
// handle leading character -/0
if (valStr.length === 1 && valStr[0] === '-') {
displayValue = valStr[0];
} else if (valStr.length === 1 && valStr[0] === '0') {
displayValue = '';
} else {
displayValue = $filter('number')(displayValue);
}
// handle decimal
if (!attrs.integer) {
if (displayValue.indexOf('.') === -1) {
if (valStr.slice(-1) === '.') {
displayValue += '.';
} else if (valStr.slice(-2) === '.0') {
displayValue += '.0';
} else if (valStr.slice(-3) === '.00') {
displayValue += '.00';
}
} // handle last character 0 after decimal and another number
else {
if (valStr.slice(-1) === '0') {
displayValue += '0';
}
}
}
if (attrs.positive && displayValue[0] === '-') {
displayValue = displayValue.substring(1);
}
if (typeof formatter !== 'undefined') {
return (displayValue === '') ? 0 : displayValue;
} else {
elem.val((displayValue === '0') ? '' : displayValue);
}
}
function setModelNumber(val) {
var modelNum = val.toString().replace(/,/g, '').replace(/[A-Za-z]/g, '');
modelNum = parseFloat(modelNum);
modelNum = (!isNaN(modelNum)) ? modelNum : 0;
if (modelNum.toString().indexOf('.') !== -1) {
modelNum = Math.round((modelNum + 0.00001) * 100) / 100;
}
if (attrs.positive) {
modelNum = Math.abs(modelNum);
}
return modelNum;
}
}
};
});
AngularJS Directive was found from: AngularJS number input formatted view
https://jsfiddle.net/benlk/4dto9738/
Very appreciative of what Anguna posted. The only thing it was missing for me was handling the decimal places like currency. I wanted it to automatically add 2 decimal places to the displayed value. However, this should only occur on initial display and then again when leaving a field. I updated the code to handle that scenario.
var app = angular.module("myApp", []);
app.directive('currencyInput', function ($filter) {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.push(function (modelValue) {
var displayValue = setDisplayNumber(modelValue, true);
displayValue = setDecimal(displayValue);
return displayValue;
});
// it's best to change the displayed text using elem.val() rather than
// ngModelCtrl.$setViewValue because the latter will re-trigger the parser
// and not necessarily in the correct order with the changed value last.
// see http://radify.io/blog/understanding-ngmodelcontroller-by-example-part-1/
// for an explanation of how ngModelCtrl works.
ngModelCtrl.$parsers.push(function (viewValue) {
setDisplayNumber(viewValue);
return setModelNumber(viewValue);
});
// occasionally the parser chain doesn't run (when the user repeatedly
// types the same non-numeric character)
// for these cases, clean up again half a second later using "keyup"
// (the parser runs much sooner than keyup, so it's better UX to also do it within parser
// to give the feeling that the comma is added as they type)
elem.bind('keyup focus', function () {
setDisplayNumber(elem.val());
});
elem.bind('blur', function () {
// Add Decimal places if they do not exist
var valStr = elem.val().toString();
valStr = setDecimal(valStr);
elem.val(valStr);
});
function setDisplayNumber(val, formatter) {
var valStr, displayValue;
if (typeof val === 'undefined') {
return 0;
}
valStr = val.toString();
displayValue = valStr.replace(/,/g, '').replace(/[A-Za-z]/g, '');
displayValue = parseFloat(displayValue);
displayValue = (!isNaN(displayValue)) ? displayValue.toString() : '';
// handle leading character -/0
if (valStr.length === 1 && valStr[0] === '-') {
displayValue = valStr[0];
} else if (valStr.length === 1 && valStr[0] === '0') {
displayValue = '';
} else {
displayValue = $filter('number')(displayValue);
}
// handle decimal
if (!attrs.integer) {
if (displayValue.indexOf('.') === -1) {
if (valStr.slice(-1) === '.') {
displayValue += '.';
} else if (valStr.slice(-2) === '.0') {
displayValue += '.0';
} else if (valStr.slice(-3) === '.00') {
displayValue += '.00';
}
} // handle last character 0 after decimal and another number
else {
if (valStr.slice(-1) === '0') {
displayValue += '0';
}
}
}
if (attrs.positive && displayValue[0] === '-') {
displayValue = displayValue.substring(1);
}
if (typeof formatter !== 'undefined') {
return (displayValue === '') ? 0 : displayValue;
} else {
elem.val((displayValue === '0') ? '' : displayValue);
}
}
function setModelNumber(val) {
var modelNum = val.toString().replace(/,/g, '').replace(/[A-Za-z]/g, '');
modelNum = parseFloat(modelNum);
modelNum = (!isNaN(modelNum)) ? modelNum : 0;
if (modelNum.toString().indexOf('.') !== -1) {
modelNum = Math.round((modelNum + 0.00001) * 100) / 100;
}
if (attrs.positive) {
modelNum = Math.abs(modelNum);
}
return modelNum;
}
function setDecimal(val) {
// Add Decimal places if they do not exist
var valStr = val.toString();
// If no decimal then add it
if (valStr.indexOf('.') === -1) {
valStr += '.00';
}
else {
var decimalDigits = valStr.length - (valStr.indexOf('.') + 1);
var missingZeros = 2 - decimalDigits;
for (var i = 1; i <= missingZeros; i++) {
valStr += '0';
}
}
return valStr;
}
}
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
<div ng-app="myApp">
<input type="text" ng-model="myModelValue" currency-input />
</div>

FuelUX spinbox - use custom strings array

Is it possible to leverage FuelUX spinbox to cycle/scroll through array of custom strings?
This is an example of what I mean, but it's a jQueryUI implementation:
Links to jsfiddle.net must be accompanied by code.
Please indent all code by 4 spaces using the code
toolbar button or the CTRL+K keyboard shortcut.
For more editing help, click the [?] toolbar icon.
http://jsfiddle.net/MartynDavis/gzmvc2ds/
Not out of the box, no.
You'd have to modify this portion of spinbox to make newVal be set by accessing your desired array instead of doing maths:
step: function step(isIncrease) {
//refresh value from display before trying to increment in case they have just been typing before clicking the nubbins
this.setValue(this.getDisplayValue());
var newVal;
if (isIncrease) {
newVal = this.options.value + this.options.step;
} else {
newVal = this.options.value - this.options.step;
}
newVal = newVal.toFixed(5);
this.setValue(newVal + this.unit);
},
Something quick and dirty based on FuelUX spinbox:
/*
* Fuel UX SpinStrings
* https://github.com/ExactTarget/fuelux
*
* Copyright (c) 2014 ExactTarget
* Licensed under the BSD New license.
*/
// -- BEGIN UMD WRAPPER PREFACE --
// For more information on UMD visit:
// https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
(function (factory) {
if (typeof define === 'function' && define.amd) {
// if AMD loader is available, register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// OR use browser globals if AMD is not present
factory(jQuery);
}
}(function ($) {
// -- END UMD WRAPPER PREFACE --
// -- BEGIN MODULE CODE HERE --
var old = $.fn.spinstrings;
// SPINSTRINGS CONSTRUCTOR AND PROTOTYPE
var SpinStrings = function SpinStrings(element, options) {
this.$element = $(element);
this.$element.find('.btn').on('click', function (e) {
//keep spinstrings from submitting if they forgot to say type="button" on their spinner buttons
e.preventDefault();
});
if ($.isPlainObject(options) && 'options' in options) {
if (!$.isArray(options.options)) {
delete options.options;
} else {
options.min = 0;
options.max = options.options.length - 1;
if (options.value && ((idx = options.options.indexOf(options.value)) > -1)) {
options.index = idx;
} else {
options.index = 0;
}
}
}
this.options = $.extend({}, $.fn.spinstrings.defaults, options);
if (this.options.index < this.options.min) {
this.options.index = this.options.min;
} else if (this.options.max < this.options.index) {
this.options.index = this.options.max;
}
this.$input = this.$element.find('.spinstrings-input');
this.$input.on('focusout.fu.spinstrings', this.$input, $.proxy(this.change, this));
this.$element.on('keydown.fu.spinstrings', this.$input, $.proxy(this.keydown, this));
this.$element.on('keyup.fu.spinstrings', this.$input, $.proxy(this.keyup, this));
this.bindMousewheelListeners();
this.mousewheelTimeout = {};
this.$element.on('click.fu.spinstrings', '.spinstrings-up', $.proxy(function () {
this.step(true);
}, this));
this.$element.on('click.fu.spinstrings', '.spinstrings-down', $.proxy(function () {
this.step(false);
}, this));
this.lastValue = this.options.value;
this.render();
if (this.options.disabled) {
this.disable();
}
};
// Truly private methods
var _applyLimits = function(value) {
// if unreadable
if (isNaN(parseFloat(value))) {
return value;
}
// if not within range return the limit
if (value > this.options.max) {
if (this.options.cycle) {
value = this.options.min;
} else {
value = this.options.max;
}
} else if (value < this.options.min) {
if (this.options.cycle) {
value = this.options.max;
} else {
value = this.options.min;
}
}
return value;
};
SpinStrings.prototype = {
constructor: SpinStrings,
destroy: function destroy() {
this.$element.remove();
// any external bindings
// [none]
// set input value attrbute
this.$element.find('input').each(function () {
$(this).attr('value', $(this).val());
});
// empty elements to return to original markup
// [none]
// returns string of markup
return this.$element[0].outerHTML;
},
render: function render() {
this.setValue(this.getDisplayValue());
},
change: function change() {
this.setValue(this.getDisplayValue());
this.triggerChangedEvent();
},
triggerChangedEvent: function triggerChangedEvent() {
var currentValue = this.getValue();
if (currentValue === this.lastValue) return;
this.lastValue = currentValue;
// Primary changed event
this.$element.trigger('changed.fu.spinstrings', currentValue);
},
step: function step(isIncrease) {
//refresh value from display before trying to increment in case they have just been typing before clicking the nubbins
this.setValue(this.getDisplayValue());
var newVal;
if (isIncrease) {
newVal = this.options.index + this.options.step;
} else {
newVal = this.options.index - this.options.step;
}
newVal = _applyLimits.call(this, newVal);
newVal = this.getOptionByIndex(newVal);
this.setValue(newVal);
},
getDisplayValue: function getDisplayValue() {
var inputValue = this.$input.val();
var value = (!!inputValue) ? inputValue : this.options.value;
return value;
},
/**
* #param string value
*/
setDisplayValue: function setDisplayValue(value) {
this.$input.val(value);
},
/**
* #return string
*/
getValue: function getValue() {
return this.options.value;
},
/**
* #param string val
*/
setValue: function setValue(val) {
var intVal = this.getIndexByOption(val);
//cache the pure int value
this.options.value = val;
this.options.index = intVal;
//display number
this.setDisplayValue(val);
return this;
},
value: function value(val) {
if (val || val === 0) {
return this.setValue(val);
} else {
return this.getValue();
}
},
/**
* Get string's position in array of options.
*
* #param string value
* #return integer
*/
getIndexByOption: function(value) {
ret = null;
if (this.options.options) {
ret = this.options.options.indexOf(value);
}
return ret;
},
/**
* Get option string by index.
*
* #param integer index
* #return string
*/
getOptionByIndex: function(value) {
ret = null;
if (this.options.options[value]) {
ret = this.options.options[value];
}
return ret;
},
disable: function disable() {
this.options.disabled = true;
this.$element.addClass('disabled');
this.$input.attr('disabled', '');
this.$element.find('button').addClass('disabled');
},
enable: function enable() {
this.options.disabled = false;
this.$element.removeClass('disabled');
this.$input.removeAttr('disabled');
this.$element.find('button').removeClass('disabled');
},
keydown: function keydown(event) {
var keyCode = event.keyCode;
if (keyCode === 38) {
this.step(true);
} else if (keyCode === 40) {
this.step(false);
} else if (keyCode === 13) {
this.change();
}
},
keyup: function keyup(event) {
var keyCode = event.keyCode;
if (keyCode === 38 || keyCode === 40) {
this.triggerChangedEvent();
}
},
bindMousewheelListeners: function bindMousewheelListeners() {
var inputEl = this.$input.get(0);
if (inputEl.addEventListener) {
//IE 9, Chrome, Safari, Opera
inputEl.addEventListener('mousewheel', $.proxy(this.mousewheelHandler, this), false);
// Firefox
inputEl.addEventListener('DOMMouseScroll', $.proxy(this.mousewheelHandler, this), false);
} else {
// IE <9
inputEl.attachEvent('onmousewheel', $.proxy(this.mousewheelHandler, this));
}
},
mousewheelHandler: function mousewheelHandler(event) {
if (!this.options.disabled) {
var e = window.event || event;// old IE support
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
var self = this;
clearTimeout(this.mousewheelTimeout);
this.mousewheelTimeout = setTimeout(function () {
self.triggerChangedEvent();
}, 300);
if (delta > 0) {//ACE
this.step(true);
} else {
this.step(false);
}
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
return false;
}
}
};
// SPINSTRINGS PLUGIN DEFINITION
$.fn.spinstrings = function spinstrings(option) {
var args = Array.prototype.slice.call(arguments, 1);
var methodReturn;
var $set = this.each(function () {
var $this = $(this);
var data = $this.data('fu.spinstrings');
var options = typeof option === 'object' && option;
if (!data) {
$this.data('fu.spinstrings', (data = new SpinStrings(this, options)));
}
if (typeof option === 'string') {
methodReturn = data[option].apply(data, args);
}
});
return (methodReturn === undefined) ? $set : methodReturn;
};
// value needs to be 0 for this.render();
$.fn.spinstrings.defaults = {
value: null,
index: 0,
min: 0,
max: 0,
step: 1,
disabled: false,
cycle: true
};
$.fn.spinstrings.Constructor = SpinStrings;
$.fn.spinstrings.noConflict = function noConflict() {
$.fn.spinstrings = old;
return this;
};
// DATA-API
$(document).on('mousedown.fu.spinstrings.data-api', '[data-initialize=spinstrings]', function (e) {
var $control = $(e.target).closest('.spinstrings');
if (!$control.data('fu.spinstrings')) {
$control.spinstrings($control.data());
}
});
// Must be domReady for AMD compatibility
$(function () {
$('[data-initialize=spinstrings]').each(function () {
var $this = $(this);
if (!$this.data('fu.spinstrings')) {
$this.spinstrings($this.data());
}
});
});
// -- BEGIN UMD WRAPPER AFTERWORD --
}));
// -- END UMD WRAPPER AFTERWORD --
Mark-up & style:
<style>
.spinstrings {position:relative;display:inline-block;margin-left:-2px}
.spinstrings input {height:26px;color:#444444;font-size:12px;padding:0 5px;margin:0 !important;width:100px}
.spinstrings input[readonly] {background-color:#ffffff !important}
.spinstrings .spinstrings-arrows {position:absolute;right:5px;height:22px;width:12px;background:transparent;top:1px}
.spinstrings .spinstrings-arrows i.fa {position:absolute;display:block;font-size:16px;line-height:10px;width:12px;cursor:pointer}
.spinstrings .spinstrings-arrows i.fa:hover {color:#307ecc}
.spinstrings .spinstrings-arrows i.fa.spinstrings-up {top:0}
.spinstrings .spinstrings-arrows i.fa.spinstrings-down {bottom:0}
</style>
<div class="spinstrings" id="mySpinStrings">
<input type="text" class="spinstrings-input" readonly="readonly">
<div class="spinstrings-arrows"><i class="fa fa-caret-up spinstrings-up"></i><i class="fa fa-caret-down spinstrings-down"></i></div>
</div>
Init as:
$('#mySpinStrings', expiryControlsW).spinstrings({
options: ['day', 'week', 'month'],
value: 'week'
});
That's it, folks!

AngularJs reflect changes in current browser tab to other tab

Using this code, does angularjs supports binding that will also reflects the changes in the current tab you're working with into the other tab
<input type="text" ng-model="name"><span ng-bind="name"></span>
No, not using just that code.
However, I just wrote a nice directive for you to use with ng-model and ng-bind (does not work with just {{ inline expressions }}, though).
Here it is in action
And here is the code:
/**
* sync-between-tabs directive.
* Use in conjunction with ng-model or ng-bind to synchronise the contents
* between tabs. The value is synced using localStorage, so each thing to sync
* needs a unique key. Specify the key using the sync-between-tabs attribute
* value, or leave blank to use the ng-model or ng-bind attributes.
* Example usage:
* <input ng-model="some.thing" sync-between-tabs></input>
* uses the key "some.thing"
* <input ng-model="some.thing" sync-between-tabs="UNIQUEKEY25"></input>
* uses the key "UNIQUEKEY25"
* <span ng-bind="some.other.thing" sync-between-tabs></span>
* uses the key "some.other.thing"
* <span ng-bind="name" sync-between-tabs="UNIQUE_KEY_12"></span>
* uses the key "UNIQUE_KEY_12"
*/
app.directive('syncBetweenTabs', ['$window', '$parse',
function($window, $parse) {
var callbacks = {}, keysToWatch = [];
var localStorage = {
key: function(key) {
return '__syncValue_' + (key || '').replace('__syncValue_', '');
},
getItem: function(key) {
return $window.localStorage.getItem(localStorage.key(key));
},
setItem: function(key, val) {
$window.localStorage.setItem(localStorage.key(key), val);
},
onItemChange: function(key, callback) {
key = localStorage.key(key);
var keyAlreadyExists = false;
if (keysToWatch.indexOf(key) < 0) {
keysToWatch.push(key);
callbacks[key] = [callback];
} else {
callbacks[key].push(callback);
keyAlreadyExists = true;
}
return function deregister() {
if (!keyAlreadyExists)
keysToWatch = without(keysToWatch, key);
callbacks[key] = without(callbacks[key], callback);
if (callbacks.length === 0)
delete callbacks[key];
};
}
};
function without(arr, value) {
var newArr = [];
for (var i = 0, len = arr.length; i < len; ++i) {
if (arr[i] !== value)
newArr.push(arr[i]);
}
return newArr;
}
if ($window.addEventListener) {
$window.addEventListener("storage", handle_storage, false);
} else {
$window.attachEvent("onstorage", handle_storage);
}
function handle_storage(e) {
if (!e) e = $window.event;
if (callbacks[e.key])
angular.forEach(callbacks[e.key], function(callback) {
callback(e.newValue);
});
}
return {
restrict: 'A',
require: '?ngModel',
link: function(scope, elem, attrs, ngModelCtrl) {
var key = attrs['syncBetweenTabs'],
mode = 'unknown',
dereg, ngBindExpr
if (ngModelCtrl) {
mode = 'ngModel';
if (!key)
key = attrs['ngModel'];
} else if (attrs['ngBind']) {
mode = 'ngBind';
if (!key)
key = attrs['ngBind'];
} else {
throw new Error('sync-between-tabs only works for ng-model and ng-bind at present');
}
if (mode == 'ngModel') {
ngModelCtrl.$viewChangeListeners.push(function() {
localStorage.setItem(key, ngModelCtrl.$viewValue);
});
var currentValue = localStorage.getItem(key);
if (currentValue && currentValue !== ngModelCtrl.$viewValue) {
ngModelCtrl.$setViewValue(currentValue);
ngModelCtrl.$render();
}
dereg = localStorage.onItemChange(key, function(value) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$render();
});
} else {
ngBindExpr = $parse(attrs['ngBind']);
dereg = localStorage.onItemChange(key, function(value) {
ngBindExpr.assign(scope, value);
scope.$digest();
});
}
scope.$on('$destroy', dereg);
}
}
}
]);
assuming both tabs have the same site loaded under the same domain, you can use local/session storage or cookies to post the shared data and make it available to any tab, the downside you have to constantly check for changes. but it should work

How to check if expression will have a value after evaluating

Let's say I have a following template:
"foo['x'] = '{{ myVar }}';"
Is there an angular way of checking if evaluating this against my current scope will give myVar some value ? I've got an array of such small templates and I only want to include them in the document when values are truthy. I was hoping either $interpolate, $parse or $eval might come in handy here. I know for sure that $interpolate is useless. What about the other two ? Maybe it's at least possible to get the name of the assigned value/expression ?
EDIT
I wasn't specific enough. What I was trying to achieve, was checking in advance if for example template '{{ myVar }}' evaluated against the current scope will return an empty string or value of the scope variable (if it exists). The case was really specific - when traversing an array of short templates I wanted to know if a template will return as an empty string or not, and only include it in my final html if it doesn't.
I'm not sure what are you trying to achieve, but to if you want to check if myVar is truthy in current scope, you can:
{{myVar ? "aw yiss" : "nope"}}
Evaluates to "aw yiss" if myVar is truthy and "nope" otherwise.
I ended up with a modified $interpolate provider but maybe someone knows a shorter solution :
app.provider('customInterpolateProvider', [
function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';
this.startSymbol = function(value){
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};
this.endSymbol = function(value){
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};
this.$get = ['$parse', '$sce', function($parse, $sce) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
index = 0,
expressions = [],
parseFns = [],
textLength = text.length,
exp;
var getValue = function (value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
var stringify = function (value) {
if (value == null) {
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
value = angular.toJson(value);
}
return value;
};
var parseStringifyInterceptor = function(value) {
try {
return stringify(getValue(value));
} catch(err) {
console.err(err.toString());
}
};
while(index < textLength) {
if ( ((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1) ) {
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
} else {
break;
}
}
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (!mustHaveExpression) {
var compute = function(values) {
for(var i = 0, ii = expressions.length; i < ii; i++) {
if (allOrNothing && angular.isUndefined(values[i])) {
return;
}
expressions[i] = values[i];
}
return expressions.join('');
};
return angular.extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
var values = new Array(ii);
try {
if (ii && !parseFns.length) {
return expressions[0];
} else {
for (; i < ii; i++) {
values[i] = parseFns[i](context);
}
return compute(values);
}
} catch(err) {
console.err(err.toString());
}
}, {
exp: text,
expressions: expressions,
$$watchDelegate: function (scope, listener, objectEquality) {
var lastValue;
return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
if (angular.isFunction(listener)) {
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
}
lastValue = currValue;
}, objectEquality);
}
});
}
}
return $interpolate;
}];
}
]);
Lines below were added because in some cases I have a predefined text in my short template and I always want to render it :
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (ii && !parseFns.length) {
return expressions[0];
} else {

directive with multiple behaviors depending on attribute or passed value from DOM

I am writing a directive, and I would like this directive to behave differently based on a value I pass to it, this value would be static. Ideally I would like to pass the value via an attribute in the DOM, like <div my-directive directive-value="value"> or even better <div my-directive="directive-value"> I know I could just add an attribute and check if it exists in the attr object in the link function, but is there a better way, what is the best practice here?
Here is my code:
layout.directive('columnDirective', function() {
return {
require: 'ngModel',
// scope: {
// directive_type: '=test'
// },
link: function (scope, el, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.unshift(function (viewValue) {
var columns = scope.row.columns;
var column_total_before = 0;
angular.forEach(columns, function(column) {
var column_size = parseInt(column.size);
column_total_before = column_total_before + column_size;
});
if(attrs.typeOf != "add") {
//Remove current (prior to edit) value from total
column_total_before = column_total_before - ngModel.$modelValue;
var default_value = 1;
} else {
if (column_total_before == 12) {
var default_value = 0;
}
else {
var default_value = (12 - column_total_before);
}
}
//Convert string to integer
viewValue = parseInt(viewValue);
//Add total existing columns with new value
column_total = column_total_before + viewValue;
scope.column_total = column_total;
if(column_total >= 1 && column_total <= 12 && viewValue > 0) {
return viewValue;
} else if (isNaN(viewValue)) {
ngModel.$setViewValue(default_value);
return default_value;
} else if (column_total_before == 12 && viewValue == 0) {
return viewValue;
} else {
ngModel.$setViewValue(default_value);
ngModel.$render();
return default_value;
}
});
}
}
});
Currently using attrs.typeOf , in my html this works fine,but I don't know if it's best practice?

Resources