I'm facing a need to display a sum of values (related to $scope variables) depending on the selection of flags. For instance:
There are 4 $scope variables (e.g. $scope.Var_1, $scope.Var_2...) containing integer values,
There are 4 $scope variables (e.g. $scope.Var_1_Flag, $scope.Var_2_Flag...)containing true or false for each of the above integer variables.
So, in we have:
$scope.Var_1 = 1 ;
$scope.Var_2 = 2 ;
$scope.Var_3 = 3 ;
$scope.Var_4 = 4 ;
$scope.Var_1_Flag = true ;
$scope.Var_2_Flag = true ;
$scope.Var_3_Flag = true ;
$scope.Var_4_Flag = true ;
then 10 will be displayed, but if:
$scope.Var_1_Flag = true ;
$scope.Var_2_Flag = false;
$scope.Var_3_Flag = false;
$scope.Var_4_Flag = true ;
then 5 will be displayed.
Does AngularJS supports a binding syntax that would realize this?
Thanks.
MARKUP:
<div ng-controller="MyCtrl">
<input type="checkbox" ng-model="Var_1_Flag" ng-checked="Var_1_Flag" ng-change="changeStatus(Var_1_Flag);" />
<input type="checkbox" ng-model="Var_2_Flag" ng-checked="Var_2_Flag" ng-change="changeStatus(Var_2_Flag);" />
<input type="checkbox" ng-model="Var_3_Flag" ng-checked="Var_3_Flag" ng-change="changeStatus(Var_3_Flag);" />
<input type="checkbox" ng-model="Var_4_Flag" ng-checked="Var_4_Flag" ng-change="changeStatus(Var_4_Flag);" />
<br/> Sum is: {{sum}}
</div>
JS:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.sum = 0;
$scope.Var_1 = 1;
$scope.Var_2 = 2;
$scope.Var_3 = 3;
$scope.Var_4 = 4;
$scope.Var_1_Flag = true;
$scope.Var_2_Flag = false;
$scope.Var_3_Flag = false;
$scope.Var_4_Flag = true;
$scope.changeStatus = function(checkValue) {
$scope.checkValue = !checkValue;
$scope.calculateSum();
}
$scope.calculateSum = function() {
$scope.sum = ($scope.Var_1_Flag ? $scope.Var_1 : 0) + ($scope.Var_2_Flag ? $scope.Var_2 : 0) + ($scope.Var_3_Flag ? $scope.Var_3 : 0) + ($scope.Var_4_Flag ? $scope.Var_4 : 0)
}
$scope.calculateSum();
}
Check this http://jsfiddle.net/ananyaojha/ADukg/13641/
// Need to keep track of watcher
$scope.$watch('Var_1_Flag', function(newVal, oldVal){
// this callback is invoked if any change is detected in the value of Var_1_Flag
// add condition and update scope using $apply or $evalAsync
// You have to set watchers also whenever flags are keep getting changed for all falg types.
})
you will have to watch the scope variables
$scope.$watch('Var_1_Flag', function(newVal, oldVal){
// this callback is invoked if any change is detected in the value of Var_1_Flag
// add condition and update scope using $apply or $evalAsync
})
you could set up more watchers or add all the flag variables into a object and then watch the object so you don't have to use a different callback for each scope variable
Create an Array of Objects with value and flag propeties. And create filter to check the flag and sum of only those values.
$scope.sumArray = [
{value:1,flag:true},
{value:2,flag:false},
{value:3,flag:false},
{value:4,flag:true}
];
You could instead assign the function the the $scope.variable..makes it more easier..hope this is what you are looking for
angular.module('myApp', [])
.controller('MainCtrl', function($scope) {
$scope.Var_1_Flag = true;
$scope.Var_2_Flag = false;
$scope.Var_3_Flag = true;
$scope.Var_4_Flag = true;
$scope.var_1 = function() {
if ($scope.Var_1_Flag) {
return 1;
} else {
return 0;
}
}
$scope.var_2 = function() {
if ($scope.Var_2_Flag) {
return 2;
} else {
return 0;
}
}
});
<body ng-app="myApp" ng-controller="MainCtrl">
<div>
<span>{{var_1() + var_2()}} </span>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
In your controller :
$scope.sumValues =0;
var Values =[
{v:1,f:true},
{v:2,f:false},
{v:3,f:false},
{v:4,f:true}];
Values.forEach(function(element) {
if(element.f)
$scope.sumValues += element.v;
});
and in your HTML :
<div ng-controller="MyCtrl">
{{sumValues}}
</div>
I create an example for you :
http://jsfiddle.net/ADukg/13643/
$scope.sumArray = [
{value:1,flag:true},
{value:2,flag:false},
{value:3,flag:false},
{value:4,flag:true}
];
function sum(){
$scope.sum =0;
for(var i=0;i<$scope.sumArray.length;i++){
$scope.sum = $scope.sum +
$scope.sumArray[i].flag ? $scope.sumArray[i].value: 0
}
}
$scope.$watch('$scope.sumArray', sum,true);
or :
you can use $filter
function sum(){
$scope.sum=0;
var filtered = $filter('filter')($scope.sumArray,'flag');
for(var i=0;i<filtered.length;i++){
$scope.sum = $scope.sum+filtered[i].value;
}
}
You just need One $watch to update the values of sum. Watch all the flags together and whenever the checkbox(flag) changes, the sum will automatically update.
var myApp = angular.module('myApp', []);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
myApp.controller("MyCtrl", function($scope) {
$scope.sum = 0;
$scope.Var_1 = 1;
$scope.Var_2 = 2;
$scope.Var_3 = 3;
$scope.Var_4 = 4;
$scope.Var_1_Flag = true;
$scope.Var_2_Flag = false;
$scope.Var_3_Flag = false;
$scope.Var_4_Flag = true;
$scope.$watch('Var_1_Flag + Var_2_Flag + Var_3_Flag +Var_4_Flag', function(val) {
$scope.sum = ($scope.Var_1_Flag ? $scope.Var_1 : 0) + ($scope.Var_2_Flag ? $scope.Var_2 :
0) + ($scope.Var_3_Flag ? $scope.Var_3 : 0) + ($scope.Var_4_Flag ? $scope.Var_4 :
0);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<h6>
CheckBoxes
</h6>
<input type="checkbox" ng-model="Var_1_Flag">
<input type="checkbox" ng-model="Var_2_Flag">
<input type="checkbox" ng-model="Var_3_Flag">
<input type="checkbox" ng-model="Var_4_Flag">
<h6>
Sum
</h6> {{sum}}
</div>
I found an amazingly simple solution that does the job exactly as I wanted.
Here is a piece of code within the controller:
$scope.Test_1_Value = 1 ;
$scope.Test_1_Flag = true ;
$scope.Test_2_Value = 2 ;
$scope.Test_2_Flag = true ;
$scope.Test_3_Value = 3 ;
$scope.Test_3_Flag = true ;
$scope.Test_4_Value = 4 ;
$scope.Test_4_Flag = true ;
$scope.ConditionalAdd = function (p1,p2,p3,p4) {
var aaa = 0 ;
if ($scope.Test_1_Flag) {aaa = aaa + $scope.Test_1_Value }
if ($scope.Test_2_Flag) {aaa = aaa + $scope.Test_2_Value }
if ($scope.Test_3_Flag) {aaa = aaa + $scope.Test_3_Value }
if ($scope.Test_4_Flag) {aaa = aaa + $scope.Test_4_Value }
return aaa ;
}
and here the HTML part:
<input type="checkbox" ng-model="Test_1_Flag"> Add 1
<br>
<input type="checkbox" ng-model="Test_2_Flag"> Add 2
<br>
<input type="checkbox" ng-model="Test_3_Flag"> Add 3
<br>
<input type="checkbox" ng-model="Test_4_Flag"> Add 4
<br>
<label>Total 1: </label> {{ConditionalAdd(Test_1_Value,Test_2_Value,Test_3_Value,Test_4_Value)}}
As the checkboxes are changed (checked/unchecked), the result shown next to Total 1: is updated automatically, as needed.
The values Test_x_Value are part of the data generated for the creation and population of the table (using ng-repeat), and hence are available within each single cell of the table.
So, no filters, no watches.
Thanks to every one for your support :-).
EDIT:
I just finished implementing this solution and tested it with a table containing over 2,500 cells. This solution works perfectly well, including performance.
Related
I am programming a calculator in AngularJS. I am stuck on a validating user input. I do not want the user to be able to enter two 2 operators ('+','/','*') next to each other.
Thus every time, I try to compare the last character and the second to last character of the string. But I always find I have two operator characters.
var app = angular.module("myApp", []);
app.controller("myCtrl", function ($scope) {
$scope.expression = "";
var liste = ['+', '/', '*'];
$scope.add = function (ope) {
$scope.expression += String(ope);
var der = $scope.expression[$scope.expression.length - 1];
var avantDer = $scope.expression[$scope.expression.length - 2];
if ($scope.expression.length > 3 && liste.includes(der) && liste.includes(avantDer)) {
alert("error");
} else {
$scope.expression += String(ope);
}
};
});
You are very close. The problem is that you are adding the operator to the expression before you have checked if it is valid or not. It is better to check the last character of the existing expression and the new character as a separate variable.
You also want to check if the length of expression is greater than 0 rather than 3 as otherwise, the user could enter two '+' characters straight away when the length is less than 3.
var app = angular.module("myApp", []);
app.controller("myCtrl", function ($scope) {
$scope.expression = "";
var liste = ['+', '/', '*'];
$scope.add = function (ope) {
// don't add to expression, just store into der
var der = String(ope);
var avantDer = $scope.expression[$scope.expression.length - 1];
if ($scope.expression.length > 0 && liste.includes(der) && liste.includes(avantDer)) {
alert("error");
} else {
$scope.expression += der;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div>
<button ng-click="add('+')">+</button>
<button ng-click="add('*')">*</button>
<button ng-click="add('/')">/</button>
</div>
<div>
<button ng-click="add('1')">1</button>
<button ng-click="add('2')">2</button>
<button ng-click="add('3')">3</button>
</div>
{{expression}}
</div>
There were two things wrong.
$scope.expression.length > 3 should have been
$scope.expression.length > 2
You were calling $scope.expression += String(ope); twice
I made a minor change below so I could run it in the code snippet window.
I also added subtraction to liste.
var $scope = {
expression: ""
};
var liste = ['+', '/', '*', '-'];
debugger
$scope.add = function (ope) {
var temp = $scope.expression + String(ope);
console.log(temp);
var len = temp.length - 1;
if (len > 1) {
var der = temp[len];
var avantDer = temp[len - 1];
if (liste.includes(der) && liste.includes(avantDer)) {
console.log("error");
} else {
$scope.expression = temp;
}
}
else {
$scope.expression = temp;
}
};
$scope.add('3');
$scope.add('+');
$scope.add('-');
When I call $scope.add('-'); it displays the error like you expect.
Good day. I've got some problem.
I've got input where I wrote some information.
Example:
<div class="wizard wizardstep1" ng-controller='someCtrl'>
<p class="wizardtitle">Put you Theme</p>
<input id="taskTheme" required type="text" placeholder="Put you Theme" ng-model="taskThemeWizardInputValue" ng-change="checkThemeWizardInputValue()">
</div>
And I've got my controller.
Example:
$scope.checkThemeWizardInputValue = function () {
if ($scope.taskThemeWizardInputValue === undefined) {
$scope.taskThemeWizardInputValue = "";
console.log($scope.taskThemeWizardInputValue);
console.log($scope.taskThemeWizardInputValue.length);
} else {
var strt = $scope.taskThemeWizardInputValue.split('.');
for (var i = 0 ; i < strt.length; i++) {
strt[i] = strt[i].charAt(0).toUpperCase() + strt[i].substr(1);
}
$scope.taskThemeWizardInputValue = strt.join('.');
console.log($scope.taskThemeWizardInputValue);
console.log(strt);
}
}
How I can add space after dot? Who knows?
Here is link to jsfiddle with my example.
We achieve it by adding space to each splitted string other than first one and an empty string
function someCtrl($scope) {
$scope.checkThemeWizardInputValue = function () {
if ($scope.taskThemeWizardInputValue === undefined) {
$scope.taskThemeWizardInputValue = "";
console.log($scope.taskThemeWizardInputValue);
console.log($scope.taskThemeWizardInputValue.length);
} else {
var strt = $scope.taskThemeWizardInputValue.split('.');
for (var i = 0 ; i < strt.length; i++) {
var addSpace='';
if(i>0 && strt[i].trim().length>0){
addSpace=' ';
}
strt[i] = addSpace+strt[i].trim().charAt(0).toUpperCase() + strt[i].trim().substr(1);
}
$scope.taskThemeWizardInputValue = strt.join('.');
console.log($scope.taskThemeWizardInputValue);
console.log(strt);
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<div class="wizard wizardstep1" ng-controller='someCtrl'>
<p class="wizardtitle">Put you Theme</p>
<input id="taskTheme" required type="text" placeholder="Put you Theme" ng-model="taskThemeWizardInputValue" ng-change="checkThemeWizardInputValue()">
</div>
</div>
You can do this simply by changing strt.join('.') to strt.join('. ').
$scope.checkThemeWizardInputValue = function () {
if ($scope.taskThemeWizardInputValue === undefined) {
$scope.taskThemeWizardInputValue = "";
console.log($scope.taskThemeWizardInputValue);
console.log($scope.taskThemeWizardInputValue.length);
} else {
var strt = $scope.taskThemeWizardInputValue.split('.');
for (var i = 0 ; i < strt.length; i++) {
strt[i] = strt[i].trim();
if(strt[i].length > 0) {
strt[i] = ' '+strt[i].charAt(0).toUpperCase() + strt[i].substr(1);
}
}
$scope.taskThemeWizardInputValue = strt.join('.');
console.log($scope.taskThemeWizardInputValue);
console.log(strt);
}
}
This is working fiddle
I suggest creating a directive so that you can plugin this behaviour whenever required., rather than writing your ng-change in every controller.
In directive simple line element.val(event.target.value.split(".").join(". ")); will work for you., with help of directive controller parameter.
See example fiddle
I'm new to Angular, so please bear with me.
I have an app I'm building where you can hit an "X" or a heart to dislike/like something. I'm using a swipe library called ng-swippy.
I'm trying to use ng-click="clickLike()"for the "Like" button and ng-click="clickDislike()"but neither are firing. I can't figure out what's going on.
Here's the URL:
http://430designs.com/xperience/black-label-app/deck.php
deck.php code
<ng-swippy collection='deck' item-click='myCustomFunction'
data='showinfo' collection-empty='swipeend' swipe-left='swipeLeft'
swipe-right='swipeRight' cards-number='4' label-ok='Cool'
label-negative='Bad'>
</ng-swippy>
The template is called from card-tpl.html:
<div class="ng-swippy noselect">
<div person="person" swipe-directive="swipe-directive" ng-repeat="person in peopleToShow" class="content-wrapper swipable-card">
<div class="card">
<div style="background: url({{person.thumbnail}}) no-repeat 50% 15%" class="photo-item"></div>
<div class="know-label">{{labelOk ? labelOk : "YES"}}</div>
<div class="dontknow-label">{{labelNegative ? labelNegative : "NO"}}</div>
</div>
<div class="progress-stats" ng-if="data">
<div class="card-shown">
<div class="card-shown-text">{{person.collection}}</div>
<div class="card-shown-number">{{person.subtitle}}</div>
</div>
<div class="card-number">{{collection.length - (collection.indexOf(person))}}/{{collection.length}}
</div>
</div>
<div class="container like-dislike" >
<div class="circle x" ng-click="clickDisike()"></div>
<div class="icon-like" ng-click="clickLike()"></div>
<div class="clearfix"></div>
</div>
</div><!-- end person-->
<div class="clearfix"></div>
Controller.js
angular.module('black-label', ['ngTouch', 'ngSwippy'])
.controller('MainController', function($scope, $timeout, $window) {
$scope.cardsCollection = [
{
thumbnail: 'images/deck/thor_01.jpg',
collection: 'thoroughbred',
}, {
thumbnail: 'images/deck/thor_02.jpg',
collection: 'thoroughbred',
},
];
// Do the shuffle
var shuffleArray = function(array) {
var m = array.length,
t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
};
$scope.deck = shuffleArray($scope.cardsCollection);
$scope.myCustomFunction = function(person) {
$timeout(function() {
$scope.clickedTimes = $scope.clickedTimes + 1;
$scope.actions.unshift({ name: 'Click on item' });
$scope.swipeRight(person);
});
};
$scope.clickLike = function(person) {
console.log($scope.count);
// swipeRight(person);
};
$scope.count = 0;
$scope.showinfo = false;
$scope.clickedTimes = 0;
$scope.actions = [];
$scope.picks = [];
var counterRight = 0;
var counterLeft = 0;
var swipes = {};
var picks = [];
var counts = [];
var $this = this;
$scope.swipeend = function() {
$scope.actions.unshift({ name: 'Collection Empty' });
$window.location.href = 'theme-default.html';
};
$scope.swipeLeft = function(person) {
//Essentially do nothing
$scope.actions.unshift({ name: 'Left swipe' });
$('.circle.x').addClass('dislike');
$('.circle.x').removeClass('dislike');
$(this).each(function() {
return counterLeft++;
});
};
$scope.swipeRight = function(person) {
$scope.actions.unshift({ name: 'Right swipe' });
// Count the number of right swipes
$(this).each(function() {
return counterRight++;
});
// Checking the circles
$('.circle').each(function() {
if (!$(this).hasClass('checked')) {
$(this).addClass('checked');
return false;
}
});
$('.icon-like').addClass('liked');
$('.icon-like').removeClass('liked');
$scope.picks.push(person.collection);
// console.log('Picks: ' + $scope.picks);
// console.log("Counter: " + counterRight);
if (counterRight === 4) {
// Calculate and store the frequency of each swipe
var frequency = $scope.picks.reduce(function(frequency, swipe) {
var sofar = frequency[swipe];
if (!sofar) {
frequency[swipe] = 1;
} else {
frequency[swipe] = frequency[swipe] + 1;
}
return frequency;
}, {});
var max = Math.max.apply(null, Object.values(frequency)); // most frequent
// find key for the most frequent value
var winner = Object.keys(frequency).find(element => frequency[element] == max);
$window.location.href = 'theme-' + winner + '.html';
} //end 4 swipes
}; //end swipeRight
});
Any thoughts and help is greatly appreciated!
The ng-click directive is inside an ng-repeat directive inside a directive with isolate scope. To find the clickLike() function it needs to go up two parents:
<!--
<div class="icon-like" ng-click="clickLike()"></div>
-->
<div class="icon-like" ng-click="$parent.$parent.clickLike()"></div>
For information, see AngularJS Wiki - Understanding Scopes.
I have an spinner element made from two spans for + and - and an text input element in the middle that shows the quantity selected from the increase and decrease:
<div class="quantity-spinner">
<span class="input-number-decrement">–</span><input class="input-number" type="text" value="1" min="0" max="10"><span class="input-number-increment">+</
</div>
I have two instances of this element, but currently when increasing the quantity for one of the elements it also controls the other one.
My question is, how can I separate the two elements, so that they are controller independently.
Here is my JavaScript:
(function() {
window.inputNumber = function(el) {
var min = el.attr('min') || false;
var max = el.attr('max') || false;
var els = {};
els.dec = el.prev();
els.inc = el.next();
el.each(function() {
init($(this));
});
function init(el) {
els.dec.on('click', decrement);
els.inc.on('click', increment);
function decrement() {
var value = el[0].value;
value--;
if(!min || value >= min) {
el[0].value = value;
}
}
function increment() {
var value = el[0].value;
value++;
if(!max || value <= max) {
el[0].value = value++;
}
}
}
};
})();
inputNumber($('.input-number'));
Thank you in advance!
try replacing
els.dec.on('click', decrement);
els.inc.on('click', increment);
by
el.prev().on('click', decrement);
el.next().on('click', increment);
you bug comes from the fact that els.dec and els.inc contain
predecessors and successors for both counters
I am looking for way to automate this selection.
For example, I will have 10 double inputs (20 inputs total) and I don't want to write JS script for each inputs, but simply use each() function (I am open to different ways) and declare only selectors.
JsFiddle: http://jsfiddle.net/vs7fa/
Idea:
var SELECTORS_H = array();
$.each(SELECTORS_H){
$('SELECTOR_H').keyup(function () {
// do magic
$('SELECTOR_V').val(num);
});
$('SELECTOR_V').keyup(function () {
// do magic
$('SELECTOR_H').val(num);
});
}
HTML:
<label for="h_one">H_ONE:</label>
<input type="text" name="h_one">
<label for="v_one">V_ONE:</label>
<input type="text" name="v_one">
There will be more of inputs. Pattern is:
h_one, v_one
h_two, v_two
h_something, v_something
...
JS:
$(function() {
$('input[name="h_one"]').keyup(function() {
var one = $(this).val();
if (one > 0) {
var num = Math.abs(one) * -1;
}
else {
var num = Math.abs(one) * 1;
}
$('input[name="v_one"]').val(num);
});
$('input[name="v_one"]').keyup(function() {
var two = $(this).val();
if (two > 0) {
var num = Math.abs(two) * -1;
}
else {
var num = Math.abs(two) * 1;
}
$('input[name="h_one"]').val(num);
});
});
You can handle this using a selector with a common class for all your element and data-attributes to know the element and the linked elements.
HTML:
<label>H_ONE:</label>
<input type="text" class="handler" data-id="h1" data-link="v1" />
<br>
<label>V_ONE:</label>
<input type="text" class="handler" data-id="v1" data-link="h1" />
Code:
$(function () {
$('.handler').keyup(function () {
var one = $(this).val();
if (one > 0) {
var num = Math.abs(one) * -1;
} else {
var num = Math.abs(one) * 1;
}
$('input[data-id=' + $(this).attr("data-link")+']').val(num);
});
});
Demo: http://jsfiddle.net/8KgTk/
may be this...
Jsfiddle: http://jsfiddle.net/vs7fa/3/
$('input[name="h_one"]').keyup(function () {
var num = DoMagic($(this));
$('input[name="v_one"]').val(num);
});
$('input[name="v_one"]').keyup(function () {
var num = DoMagic($(this));
$('input[name="h_one"]').val(num);
});
function DoMagic(element) {
var one = $(element).val();
if (one > 0) {
var num = Math.abs(one) * -1;
} else {
var num = Math.abs(one) * 1;
}
return num;
}
You should be able to perform the .each function by using jQuery and making the items the same class.
such as:
<label class="forElement" for="h_one">H_ONE:</label>
<input class="inputElement" type="text" name="h_one">
<label class="forElement"for="v_one">V_ONE:</label>
<input class="inputElement" type="text" name="v_one">
$('.forElement').each( function() {
//some code
}
You can do this without adding extra attributes if you want.
$(function () {
$('input[name^="h_"], input[name^="v_"]').keyup(function () {
var one = $(this).val();
var num = - one;
var inputType = $(this).attr("name").substr(0,1);
var inputNumber = $(this).attr("name").substr(2);
$('input[name="'+(inputType == 'v' ? 'h' : 'v')+'_' + inputNumber + '"]').val(num);
});
});
However Irvin Dominin aka Edward's solution is quite good.
Here's a sollution that doesn't require extra markup, and doesn't use string concatenation for logic. It uses $.proxy() to get correct scoping.
Fiddle