watch changes on JSON object properties - json

I'm trying to implement a directive for typing money values.
var myApp = angular.module('myApp', []);
var ctrl = function($scope) {
$scope.amount = '0.00';
$scope.values = {
amount: 0.00
};
};
myApp.directive('currency', function($filter) {
return {
restrict: "A",
require: "ngModel",
scope: {
separator: "=",
fractionSize: "=",
ngModel: "="
},
link: function(scope, element, attrs) {
if (typeof attrs.separator === 'undefined' ||
attrs.separator === 'point') {
scope.separator = ".";
} else {
scope.separator = ",";
};
if (typeof attrs.fractionSize === 'undefined') {
scope.fractionSize = "2";
};
scope[attrs.ngModel] = "0" + scope.separator;
for(var i = 0; i < scope.fractionSize; i++) {
scope[attrs.ngModel] += "0";
};
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
};
var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;
if (pattern.test(newValue)) {
scope[attrs.ngModel] += "00";
return;
};
}, true);
}
};
});
HTML template:
<div ng-app="myApp">
<div ng-controller="ctrl">
{{amount}}<br>
<input type="text" style="text-align: right;" ng-model="amount" currency separator="point" fraction-size="2"></input>
</div>
</div>
I want to bind the value in my input element to values.amount item in controller but the watch instruction of my directive doesn't work.
How do I leverage two-way-data-binding to watch JSON objects?
To understand problem more precise I've created a jsfiddle.
The task is the following: Add extra zeros to the input element if user put a point. I mean if the value in input element say "42" and user put there a point, so the value now is "42." two extra zeros have to be aded like this "42.00".
My problems:
If I use ng-model="amount" the logic in input element works, but amount value of outer controller doesn't update.
If I use ng-model="values.amount" for binding, neither amount of outer controller nor input element logic works.
I really have to use ng-model="values.amount" instruction, but it doesn't work and I don't know why.
Any ideas?

Related

Make own calibration dialog with comma delimiter decimal

Is there an event similar as "FINISHED_CALIBRATION" when the style for the "calibration panel" is set to display: block? I want to create a custom calibration dialog with comma separator. I want to open own calibration dialog instead of your dialog. I check two point for calibration with this code:
const calibrationEndpoints: HTMLCollection = document.getElementsByClassName('calibration-endpoint');
const countPoint = Array.from(calibrationEndpoints).filter(e => e.classList.contains('editable')).length;
if (countPoint === 2) {
this.openCalibrationDialog();
}
Please review this sample
this.showPanel = function() {
var self = this;
const _window = this.getWindow();
if (_calibrationPanel) {
_window.setTimeout(function () { _calibrationPanel.requestedSizeTextbox.focus();}, 0);
_calibrationPanel.setVisible(true);
_calibrationPanel.updatePanelPosition(_measurement.indicator.labelPosition, _measurement.indicator.p1, _measurement.indicator.p2, _measurement.indicator.calibrationLabel.clientHeight);
self.addWindowEventListener("keyup", function onKeyUp(e){
var key = e.key || String.fromCharCode(e.keyCode);
if (key == "Escape" && self.isActive()) {
self.hidePanel();
self.clearSize();
self.showAddCalibrationLabel();
self.removeWindowEventListener("keyup", onKeyUp);
}
});
}
else {
_viewer.dispatchEvent({ type: MeasureCommon.Events.OPEN_CALIBRATION_PANEL_EVENT, data: {size: _selectedSize, units: _selectedUnits } });
}
};

Angularjs custom filter not working

I am trying to filter elements based on the range. I am using two controllers & $rootScope broadcast-on approach to retrieve the min-max range of a slider & sharing it with the other controller.
HTML-
<body ng-app="myApp">
<div ng-controller="RangeController as vm">
<rzslider rz-slider-model="vm.slider.minValue" rz-slider-high="vm.slider.maxValue" rz-slider-options="vm.slider.options"></rzslider>
</div>
<div ng-controller="SampleController">
<div ng-repeat="x in elements | inRange:min:max">
{{x}}
</div>
</div>
</body>
AngularJS-
var app = angular.module('myApp', ['rzModule']);
app.controller('SampleController', function($scope,$rootScope) {
$scope.min = 1500;
$scope.max = 5500;
$scope.elements = [1530,2100,2780,3323,3420,4680,5020,5300,5402];
$scope.$on('MIN_PRICE', function(response) {
$scope.min = minPrice;
})
$scope.$on('MAX_PRICE', function(response) {
$scope.max = maxPrice;
})
});
app.value('minPrice',1500);
app.value('maxPrice',5500);
app.controller('RangeController', RangeController);
function RangeController($scope,$rootScope) {
var vm = this;
vm.changeListener = function() {
minPrice = vm.slider.minValue;
maxPrice = vm.slider.maxValue;
console.log(minPrice + " " +maxPrice);
$rootScope.$broadcast('MIN_PRICE', minPrice);
$rootScope.$broadcast('MAX_PRICE', maxPrice);
};
vm.slider = {
minValue: 1500,
maxValue: 5500,
options: {
floor: 1500,
ceil: 5500,
step: 500,
translate: function(value) {
return '₹' + value;
},
onChange:vm.changeListener
}
}
}
app.filter('inRange', function() {
return function(array, min, max) {
array = array.filter(function(element) {
return (element >= min && element <= max);
});
console.log(array);
};
});
I tried debugging, the filter works fine but it won't reflect in the template.
The self-assignment to array inside your filter (array = array.filter(…);) seems slightly suspicious to me. Have you tried simply returning array.filter(…); directly?
app.filter('inRange', function() {
return function(array, min, max) {
return array.filter(function(element) {
return (element >= min && element <= max);
});
};
});

unable to call click event in template in angularjs directive

In have one common directive which will display in each and every page. Already visited page displaying as a done, So i want click event on already visited page. I added ng-click and wrote function in controller. Can anybody help why it's not working.
html
<div class="row">
<div class="col-sm-12">
<wizard-menu currentPage="searchOffering"></wizard-menu>
</div>
</div>
js
function generateMenuHtml(displayMenuItems, currentPage, businessType) {
var htmlOutput = '';
var indexOfCurrentPage = getIndexOf(displayMenuItems, currentPage, 'pageName');
if (businessType) {
htmlOutput += '<ol class="wizard wizard-5-steps">';
} else {
htmlOutput += '<ol class="wizard wizard-6-steps">';
}
angular.forEach(displayMenuItems, function (value, key) {
var htmlClass = '';
if (indexOfCurrentPage > key) {
htmlClass = 'class="done" ng-click="goToFirstPage()"';
} else if (key === indexOfCurrentPage) {
htmlClass = 'class="current"';
} else {
htmlClass = '';
}
if (key!==1){
htmlOutput += '<li ' + htmlClass + '><span translate="' + value.title + '">' + value.title + '</span></li>';
}
});
htmlOutput += '</ol>';
return htmlOutput;
}
.directive('wizardMenu',['store','WIZARD_MENU', 'sfSelect', function(store, WIZARD_MENU, Select) {
function assignPageTemplate(currentPageValue){
var storage = store.getNamespacedStore(WIZARD_MENU.LOCAL_STORAGE_NS);
var data=storage.get(WIZARD_MENU.LOCAL_STORAGE_MODEL);
var businessTypePath='offeringFilter.businessType.masterCode';
var businessTypeValue = Select(businessTypePath, data);
if(businessTypeValue!=='' && businessTypeValue==='Prepaid'){
template = generateMenuHtml(businessTypePrepaid, currentPageValue, true);
}
else{
template = generateMenuHtml(commonMenu, currentPageValue, true);
}
return template;
}
return {
require: '?ngModel',
restrict: 'E',
replace: true,
transclude: false,
scope: {
currentPage: '='
},
controller: ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
$scope.goToFirstPage = function() {
console.log('inside First Page');
};
}],
link: function(scope,element,attrs){
element.html(assignPageTemplate(attrs.currentpage));
},
template: template
};
}])
I'm unable to call goToFirstPage(). Can anybody tell what is wrong here.
Thanks in advance....
You need to compile the template. If you use Angular directive such as ng-click and you simply append them to the DOM, they won't work out of the box.
You need to do something like this in your link function:
link: function(scope,element,attrs){
element.append($compile(assignPageTemplate(attrs.currentpage))(scope));
},
And don't forget to include the $compile service in your directive.
Hope this helps, let me know!
Documentation on $compile: https://docs.angularjs.org/api/ng/service/$compile

Polymer - reload core-list data

I wanted reload a core-list element to show new data, but it´s not refreshing.
I re-call the JS function thats generate the data but doesn t work... and reload like a 'normal' div doesn t work either! The list only shows the new data if i reload the entire page...
function values(sender, textomsg, datacriacao, senderfoto){
var sender2 = sender.split(",");
var textomsg2 = textomsg.split(",");
var datacriacao2 = datacriacao.split(",");
var senderfoto2 = senderfoto.split(",");
var namegen = {
generateString: function (inLength) {
var s = '';
for (var i = 0; i < inLength; i++) {
s += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
return s;
},
generateName: function (inMin, inMax) {
return this.generateString(Math.floor(Math.random() * (inMax - inMin + 1) + inMin));
}
};
Polymer('list-test', {
count: sender.length,
ready: function () {
this.data = this.generateData();
},
generateData: function () {
var names = [], data = [];
for (var i = 0; i < this.count; i++) {
names.push(namegen.generateName(4, 8));
}
names.sort();
for (var i = 0; i < this.count; i++) {
data.push({
index: i,
sender: sender2[i],
textomsg: textomsg2[i],
datacriacao: datacriacao2[i],
senderfoto: senderfoto2[i]
});
}
return data;
},
tapAction: function (e) {
console.log('tap', e);
}
});
}
<%----%>
<template id="templateConversas" runat="server">
<div id="item" class="item {{ {selected: selected} | tokenList }}" ><%--onClick="conversa('{{name}}');"--%>
<div class="message" style="background-image: url({{senderfoto}});">
<span class="from"><br/>{{sender}}</span>
<span class="timestamp">{{datacriacao}}</span>
<div class="subject"><br/>{{textomsg}} </div><%--------Infinite List. {{index}}--%>
<%--<div class="body"><br/>Mensagem de teste...........</div>--%>
</div>
</div>
</template>
The problem is also reload the 'list-test'. if i call the js function after the list is loaded it doesn't apply the new data...
Your code isn't complete so it is hard to understand but I think that the problem is that you don't assign the result of the generateData() function to the template's model. Try following script for your component
Polymer('list-test', {
created: function () {
this.data = [];
},
refresh: function () {
this.data = this.generateData();
},
generateData: function () {
// your original code here
}
});
Now the list content should be updated with newly generated data when you call refresh() of the list-test element. To fill the list when element is created add
ready: function () {
this.refresh();
},

Automatic text detection on contenteditable div using angular js

what is the best way to do following using angular js
when writing text on a contenteditable div need to detect special word like ' {{FULL_NAME}} ' and covert to tag with pre-deifined constant words
example - if some one write
' His name is {{FULL_NAME}} '
should be instantly convert to ' His name is john smith '
Here is a demo plunker: http://plnkr.co/edit/GKYxXiDKv7fBeaE7rrZA?p=preview
Service:
app.factory('interpolator', function(){
var dict = { .... };
return function(str){
return (str || "").replace(/\{\{([^\}]+)\}\}/g, function(all, match){
return dict[match.trim().toLowerCase()] || all;
});
};
});
Directive:
app.directive('edit',[ 'interpolator', function(interpolator){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
element.on('blur', function(e) {
scope.$apply(function() {
var content = interpolator(element.text());
element.text(content);
ngModel.$setViewValue(content);
});
});
ngModel.$formatters.push(interpolator);
ngModel.$render = function() {
element.text(ngModel.$viewValue);
ngModel.$setViewValue(ngModel.$viewValue);
};
}
};
}]);
Here's an example using simple DOM methods, based on other answers I've provided on Stack Overflow. It also uses Rangy to save and restore the selection while doing the substitutions so that the caret does not move.
Demo:
http://jsbin.com/zuvevocu/2
Code:
var editorEl = document.getElementById("editor");
var keyTimer = null, keyDelay = 100;
function createKeyword(matchedTextNode) {
var el = document.createElement("b");
el.style.backgroundColor = "yellow";
el.style.padding = "2px";
el.contentEditable = false;
var matchedKeyword = matchedTextNode.data.slice(1, -1); // Remove the curly brackets
matchedTextNode.data = (matchedKeyword.toLowerCase() == "name") ? "John Smith" : "REPLACEMENT FOR " + matchedKeyword;
el.appendChild(matchedTextNode);
return el;
}
function surroundInElement(el, regex, surrounderCreateFunc) {
// script and style elements are left alone
if (!/^(script|style)$/.test(el.tagName)) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1) {
surroundInElement(child, regex, surrounderCreateFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
function updateKeywords() {
var savedSelection = rangy.saveSelection();
surroundInElement(editorEl, /\{\w+\}/, createKeyword);
rangy.restoreSelection(savedSelection);
}
function keyUpHandler() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateKeywords();
keyTimer = null;
}, keyDelay);
}
editorEl.onkeyup = keyUpHandler;
Related:
https://stackoverflow.com/a/5905413/96100
https://stackoverflow.com/a/4026684/96100
https://stackoverflow.com/a/4045531/96100