angular two way dymanic binding issue - angularjs

Im trying to output the following info and have it update realtime using angulars automatic binding. the first and last update ok, but full isnt updating as I would expect. im grateful for any assistance.
http://jsfiddle.net/laurencefass/74u313gx/1/
required output:
first = john
last = doe
full name=john doe.
HTML
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first"/>
<p>first = {{first}}</p>
<input ng-model="last"/>
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
</div>
JS
var app=angular.module("nameApp", []);
app.controller('nameController', function($scope) {
$scope.first="john";
$scope.last="doe";
$scope.full = $scope.first + " " + $scope.last;
});
initial output seems correct and the first and last update as expected. but the full name is not updating despite being a $scope var and a product of first and last.

The following line is only run once. So it is initiated with the first value that has been assigned to first and last.
$scope.full = $scope.first + " " + $scope.last;
So, if you want your binding to work, without having a unnecessary fonction into your controller. (keep your controllers as clean as possible!)
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first"/>
<p>first = {{first}}</p>
<input ng-model="last"/>
<p>last = {{last}}</p>
<p>full name = {{first + ' ' + last}}</p>
</div>
</div>
Have a look at your fiddle :
http://jsfiddle.net/74u313gx/2/
If you really need to have the fullname into the controller, you can use a $watch therefor :
$scope.$watch('first', updateFull);
$scope.$watch('last', updateFull);
function updateFull(){
$scope.full = $scope.first + " " + $scope.last;
}
If you are concerned by performance, you may want to avoid defining to much watches, then a ng-change can be used :
controller :
$scope.updateFull = function(){
$scope.full = $scope.first + " " + $scope.last;
}
View :
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first" ng-change="updateFull();"/>
<p>first = {{first}}</p>
<input ng-model="last" ng-change="updateFull();"/>
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
</div>

Since you're defining a string on $scope that is concatenated with several parameters, it won't change automatically when you change the parts that you used to assemble it with.
If you want to achieve what you're looking for, you can do one of two things:
One:
<p>full name = {{first + ' ' + last}}</p>
Two:
<p>full name = {{getFullName()}}</p>
And in the controller have a function:
$scope.getFullName = function () {
return $scope.first + ' ' + $scope.last;
}

it will not update auto since your are passing a string in full name after concatenation it returns simple string not an angular var, you need to do the following to update value automatically.
$scope.$watchGroup(["first","last"],function(){
$scope.full = $scope.first + " " + $scope.last;
});

update your code like below:
View Update
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first" ng-change="change()"/>
<p>first = {{first}}</p>
<input ng-model="last" ng-change="change()" />
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
Script Update
var app=angular.module("nameApp", []);
app.controller('nameController', function($scope) {
$scope.first="john";
$scope.last="doe";
$scope.full = $scope.first + " " + $scope.last;
$scope.change= function(){
$scope.full=$scope.first + " " + $scope.last;
}
});
As you seen above, I am changed/update view page input elements with ng-change directive and create same scope function to namecontroller w.r.t.nameApp Module.
Hope its helps you!!!

Related

Connecting image from imgArray to select

I'm building a little Js project in which you can select 2 types of motorcycles, and 3 types of weather, and based on you're choices you will get street and snow tires. But right now im trying to connect an image to the option with an imgArray, but can't figure out how to do it. Right now im using onchange in html, I have to do it with eventlisterner but I didn't get that to work. Here's my code
HTML:
<select id="motor" onchange="selectBike()">
<option value="Allroad">Allroad</option>
<option value="Street">Street</option>
</select>
Js
var imgArray2 = ['KTM790', "KTM790D"];
document.querySelector("img").src = 'Bike/' + imgArray2
function selectBike() {
var x = document.getElementById('motor').value;
document.getElementById("demo").innerHTML =
"you selected " + x;
}
function selectWeather() {
var c = document.getElementById('weather').value;
document.getElementById("demo2").innerHTML = "it's " + c;
}
You need yo have an imgObjects which has the values and the images name
then you can use that code which will create image element and add it
var imgObjects = {
Allroad : "KTM790D",
Street : "KTM790"
}
function selectBike() {
var x = document.getElementById('motor').value;
document.getElementById("demo").innerHTML =
"you selected " + x;
var imgElm = document.createElement("img");
imgElm.src = window.location.hostname + "/Bikes/" + imgObjects[x]+".jpg";
console.log(imgElm)
document.getElementById("demo").appendChild(imgElm);
}
<select id="motor" onchange="selectBike()">
<option value="Allroad">Allroad</option>
<option value="Street">Street</option>
</select>
<p id="demo"></p>
In my case, You can create a JSON object and store image data into the Array object. When you adding image data, you should convert image to the base64 format. here is the link. Here is my solution
<html>
<head><script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script></head>
<body>
<select id="bikes" onchange="selBike()">
<option >Allroad</option>
<option >Street</option>
</select>
<div id="demo"></div>
<div id="demo1"></div>
<script>
var imgBikes = [{"imgURL" : "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAGAAYAMBIgACEQEDEQH/xAAcAAEAAQUBAQAAAAAAAAAAAAAACAMEBQYHAQL/xAA7EAABAwMCAwMICAYDAAAAAAABAAIDBAURBhITITEHQVEUIlRhcXSUshYXMzVikbHRI1JVcoGhFTI0/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AO4oiICIiAiIgIiICIiAiIgIiICLxzgxpc4gNAySTyAWN+kNk/rFu+KZ+6ClqvUFJpiyT3Sta97I8NZHGPOkeeQaP3WjR2XWerqKlv8AS6xNtZWxNljoYIHBkLTzA3B3nHxJHP2cl8dt90t9bo+GKiuFLO8V0biyGZrjja/uB9iz3ZzfbTFoeyQ1F0oopo6RjHxvqGNc0jlzBPJBhm1+oezqWkn1bfWXi01tQKdz+GWyUrtrjv8AxNw05GfWPA9NY9sjGvY4Oa4ZBByCFyLt6ulFW6ftkNFWU9Q7y3e7gytfgCN47j+JdC01dKH6O2sSV1MHijh3AzNyDsGe9BnUVtHcaGR4ZHWU7nOOA1srST/tXKAiIgIioSVG2QjHRBTuxxa6w+ED/lKiFC1vAZyH/UfopaXWcutlY04A4EmT6tpUd6bRTxGfKK4bA1zGPZGeTgcNJz1HIkgfn1wGrBo7gAvNozkjJWzx6OqeXFq4o8lrWkxuIcXcMDB7xulAz+Eq9s2gZLla6OufcxTGpaTwX0pJYRuyD5w/ld3eHjyDTNo7hhfHBiJJMbCT3lq6BN2by0vAfPcd7HSMbI1sGzzS4NO1xJyefeFj5NGOfHSvpagtMn2geNw6OPmnAycN6deaDG6EYxutrEQxoIrosED8QUqlHzTWkZaDVlmnFQHCCaN8wLTzc0s3bOmW5d/hd8dUtDsAZ9eUFdFQbUBzg3b19aroC+XMa8YcMr6RBZXhoForuQ/88nylRFjcTG05PTxUur19z13u8nylREi+yZ/aEGStNvfcjXhk/C8joJq05bu38PB29RjOevPHgt00hBcG6bgraOuo2z8KbgU09M92WRTYe4vDwAc1JAG08sd/Na/oSmnray90tJE6aomsdWyONgyXOOwABbnpa31dDpinNbTvgFJDXw1Jk5cJ7qimLWu8CQCQgpX6xvvdRDR1F/o201E6RglgoH74pOO2nMZYZScFxb5xPRufFc1roH0VfVUkj976ad8JcOhLXFpI/JdrqbdUuuEsHkbRXyzSzFjI2te+Jtzjc15x1GwZye5ca1C4O1Fd3NIINfUEEd/8VyDI6DJdraxEkk+Wx9T61KbaPBRY0Bz1vYvfY/1UqEHmAvURAREQWV6+5q/3aT5SoiR/Zt9gUur59y1/u0nylRGYf4bfYEBzA4YcAR4ELo9tsVm+jVnqv+MikqKianime6m38ncLd38hhxO7uz0XOcqtHV1cTdsVVOxuMbWyuAx4Yz6h+SDZNfW+3UfksdDbY6dpfKJCKXZvc2RzMh248jtJDe4YOea1TGByVWWpqJY2xy1EsjGnLWPeSAfEAqkg2Ds+565sXvjFKdRZ7POeurF7439CpTICIiAiIgtbrC+otlXBEMySQPY0ZxklpAUe29k2sNgBoqbIHpTVI5EEcvql1f6HTfFNXv1Sav8ARKX4pqkYiCOf1Sav9Epfimp9Uer/AEWk+KH7KRiIOGaO7MtTWrVNruFbBStp6acSSFtQHHGD0GF3NEQEREH/2Q=="},{"imgURL":"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////2wBDAf//////////////////////////////////////////////////////////////////////////////////////wAARCADqATkDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECA//EACQQAQEBAAIBBAMBAQEBAAAAAAABESExQQISUXFhgZGxocHw/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAMAwEAAhEDEQA/AMriLyCKgg1gQwCgs4FTMOdutepjQak+FzMSVqgxZdRdPPIIvH5WzzGdBriphtTeAXg2ZjKA1pqKDUGZca3foBek8gFv8Ie3fKdA1qb8s7hoL6eLVt51FsAnql3Ut1M7AWbflLMDkEMX/F6/YjK/pADFQAUNA6alYagKk72m/j9p4Bq2fDDSYKLNXPNLoHE/NT6RYC31cJxZ3yWVM+aBYi/S2ZgiAsnYJx5D21vPmqrm3PTfpQQwyAC8JZvSKDni41ZrMuUVVl+Uz9w9v/1QWrZsZ5nFPHYH+JZyureQSF5M+fJ0CAfwRAVRBQA1DAWVUayoJUWoDpsxntPsueBV4+VxhdyAtv8AjOLGpIDMLbeGvbF4iozJfr/WukAVABAXAQXEAAASzVAZdO2WNordm+emFl7XcQSNZiFtv0C9w90nhJf4mA1u+GcJFwIyAqL/AOovwgGNfSRqdIrNa29M0gKCAojU9PAMjWXpckEJFNFEAAXEUBABYz6rZ0ureQc9vyt9XxDF2QAXtABcQAs0AZywkvluJbyipifas52DcyxjlZweAO0xri/hc+wZOEKIu6nSyeToVZyWXwvCg53gW81QQ7aTNAn5dGZJPs1UXURQAUEMCXQLZE93PRZ5hPTgNMrbIzKCm52LZwCs+2M8w2g3sjPuZAXb4IsMAUACzVUGM4/K+md6vEXUUyM5PDR0IxYe6ramih0VNBrS4xoqN8Q1BFQk3yqyAsioioAAKgDSJL4/jQIn5igLrPqtOuf6oOaxbMoAltUAhhIoJiiggrPu+AaOIxtAX3JbaAIaLwi4t9X4T3fg2AFtqcrUUarP20zUDAmqoE0WRBZPNVUVEAAAAVAC8kvih2DSKxOdBqs7Z0l0gI0mKAC4AuHE7ZtBriM+744QAAAAABAFsveIttBICyaikvy1+r/Cen5rWQHIBQa4rIDRqSl5qDWqziqgAAAATA7BpGdqXb2C2+J/UgAtRQBSQtkBWb6vhLbQAAAAAEBRAAAAAUbm+GZNdPxAP+ql2Tjwx7/wIgZ8iKvBk+CJoCXii9gaqZ/qqihAAAEVABGkBFUwBftNkZ3QW34QAAABFAQAVAAAAAARVkl8gs/43sk1jL45LvHArepk+E9XTG35oLqsmIKmLAEygKg0y1AFQBUXwgAAAoBC34S3UAAABAVAAAAAABAUQAVABdRQa1PcYyit2z58M8C4ouM2NXpOEGeWtNZUatiAIoAKIoCoAoG4C9MW6dgIoAIAAAAAAACKWAgL0CAAAALiANCKioNLgM1CrLihmTafkt1EF3SZ5ZVUW4mnIKvAi5fhEURVDWVQBRAAAAAAAAQFRVyAyulgAqCKlF8IqLsEgC9mGoC+IusqCrv5ZEUVOk1RuJfwSLOOkGFi4XPCoYYrNiKauosBGi9ICstM1UAAAAAAFQ0VcTBAXUGgIqGoKhKAzRRUQUAwxoSrGRpkQA/qiosOL9oJptMRRVZa0VUqSiChE6BqMgCwqKqIogAIAqKCKgKoogg0lBFuIKgAAAKNRlf2gqsftsEtZWoAAqAACKoMqAAeSoqp39kL2AqLOlE8rEBFQARYALhigrNC9gGmooLp4TweEQFFBFAECgIoAu0ifIAqAAA//9k="}];
function selBike(){
var getVal = $('#bikes').val();
if(getVal == "Allroad"){
$('#demo').empty();
var imgTag = $("<img src='"+ imgBikes[0].imgURL +"'></img>");
$('#demo1').append(imgTag);
}
else{
$('#demo').empty();
var imgTag = $("<img src='"+ imgBikes[1].imgURL +"'></img>");
$('#demo').append(imgTag);
}
}
</script>
</body>
</html>
Thus, you don't need store images your directory. Because image data is now encoded.
So, Don't care about my images...
Thank you!!!
I have modified bit of your code in order to make it as you expected.
HTML:
added 'this' reference to the 'selectBike' event call to access select control in event callback function.
JS:
by default image src is map to first index of given image reference array imgArray2.
using 'this' reference which is passed as an argument we can access the selectedIndex and can change the src of the image accordingly.
var imgArray2 = ['KTM790', "KTM790D"];
//by default
document.querySelector("img").src = 'Bike/' + imgArray2[0];
function selectBike(selectRef) { var x = document.getElementById('motor').value;
document.getElementById("demo").innerHTML = "you selected " + x;
document.querySelector("img").src = 'Bike/' + imgArray2[selectRef.selectedIndex];
}
function selectWeather() { var c = document.getElementById('weather').value;
document.getElementById("demo2").innerHTML = "it's " + c;
}
<select id="motor" onchange="selectBike(this)">
<option value="Allroad">Allroad</option>
<option value="Street">Street</option>
</select>
<div id="demo"></div>
<img src="" alt="">

Not able to get ng-model value from dynamically created input box

Here is how I am adding fields.
$scope.addEmailField = function () { //Function to add new email field.
if (valid <= 1 && checkToDelete == 0) {
var mailTxtId = 'mail' + valid;
var mailModel = 'Contact_Email' + valid;
var hide = 'hide' + valid;
hide = false;
console.log(mailTxtId);
var emailDiv = angular.element(document.querySelector('#emailDiv'));
var element = $compile('<div id="' + mailTxtId + '" style="margin-left: -60px; width:200px; margin-top:15px"><input id= "' + mailModel + '" type = "text" class="form-control" ng-model="' + mailModel + '" ng-blur="validateEmailDynamic(' + valid + ')">' +
'<input id="' + valid + '" class="form-control" style="margin-left: 206px; width:54px; margin-top:-34px" type="button" value="-" ng-click="deleteField(' + valid + ')"><span ng-show ="' + hide + '" style="color:red">' +
'Invalid email</span></div>')($scope);
emailDiv.append(element);
valid = valid + 1;
}
};
But not getting the value of ng-model.
Store your input box in a directive's template. Then add ng-class that would determine whether it should show or not.
app.directive('inputBox', function(){
template:'<input ng-model="item">'
});
Usage in the html:
<div ng-class="{ input-box : triggerInputBox }"></div>
Controller:
$scope.triggerInputBox = true;
This is just one of many ways to accomplish this. But directives are very useful for dynamically showing templates.

Render angular as string

Here is my problem:
I have a button, with I can add dynamically inputs.And specifically add a textarea with div element above it which contains a counter for chars in textarea.The problems is that angular tags are rendering as ordinary html text.
mainController :
var validationApp = angular.module('validationApp', []).config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('<%').endSymbol('%>');
});
validationApp.controller('mainController', function ($scope, $compile) {
var max_fields = 10; //maximum input boxes allowed
var wrapper = $(".activities_wrap"); //Fields wrapper
var add_button = $(".my-add_field_button"); //Add button ID
var x = 0; //initlal text box count
var elements = "<div class=\"char-counter\">Остават Ви <% (100 - projectManagement['project_management[0][activity]'].$viewValue.length) > 0 ? (100 - projectManagement['project_management[0][activity]'].$viewValue.length) : 0 %> символа" + +"</div>" +
"<div><h3>Дейност #" + x + "</h3>" +
"<textarea ng-model=\"project_management.activity" + x + "\" ng-maxlength=\"100\" name=\"project_management[" + x + "][activity]\" placeholder=\"Дейност\"></textarea>" +
"<input class=\"from\" type=\"text\" name=\"project_management[" + x + "][from]\" placeholder=\"Начална дата\">" +
"<input class=\"to\" type=\"text\" name=\"project_management[" + x + "][to]\" placeholder=\"Крайна дата\">" +
"<input type=\"text\" name=\"project_management[" + x + "][place]\" placeholder=\"Място\">" +
"<input type=\"text\" name=\"project_management[" + x + "][responsible_for_activity]\" placeholder=\"Отговорен за дейността\">" +
"<input type=\"text\" name=\"project_management[" + x + "][count_participants]\" placeholder=\"Брой включени участници\">" +
"<textarea type=\"text\" name=\"project_management[" + x + "][indicators_for_measure_of_activity]\" placeholder=\"Индикатори за измерване на дейността\"></textarea>" +
"<br>Премахни</div>";
$(add_button).click(function (e) { //on add input button click
e.preventDefault();
if (x < max_fields) { //max input box allowed
x++; //text box increment
$(wrapper).append(elements); //add input box
$('.from, .to').datepicker({
dateFormat: 'dd-mm-yy',
dayNames: ["Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
dayNamesMin: ["Нд", "По", "Вт", "Ср", "Чт", "Пт", "Сб"],
monthNames: ["Януари", "Февруари", "Март", "Април",
"Май", "Юни", "Юли", "Август", "Септември",
"Октомври", "Ноември", "Декември"]
}).val();
$compile(elements)($scope);
}
});
$(wrapper).on("click", ".remove_field", function (e) { //user click on remove text
e.preventDefault();
$(this).parent('div').remove();
x--;
})
});
HTML :
<fieldset ng-form="projectManagement">
<div class="activities_wrap">
<button class="action-button my-add_field_button">Добави дейност</button>
</div>
<input type="button" name="previous" class="previous action-button" value="Назад"/>
<input type="button" name="next" class="next action-button" ng-disabled="projectManagement.$invalid" value="Напред"/>
<script>
$('.from, .to').datepicker({
dateFormat: 'dd-mm-yy',
dayNames: ["Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота", "Неделя"],
dayNamesMin: ["Нд", "По", "Вт", "Ср", "Чт", "Пт", "Сб"],
monthNames: ["Януари", "Февруари", "Март", "Април",
"Май", "Юни", "Юли", "Август", "Септември",
"Октомври", "Ноември", "Декември"]
}).val();
</script>
This is not exactly an answer, but a simple example that will, hopefully, help you understand the "angular thinking".
AngularJS is different that JQuery in a way that your view is completely dependent of your data structure. Changes in your data will reflect in view changes, in the same way that, changes in the view may reflect in an update to your data. This is all done by a mechanism called Data-Binding. I'm not that good at explaining so a made a simple example. Besides, you should really study about Data-Binding, ViewModel pattern and Angular in general. You will see that, thankfully, you will not need Jquery for much.
So, to achieve what you want, you need to hold an array of objects in your controller, and push new items to it with the click of a button. This array will store objects with the properties you want to store, like ID, NAME, etc. Each of those properties will be binded to a input, so that they can be edited.
Here is an example of what I'm trying to explain.
https://jsfiddle.net/relferreira/42zrn2t2/1/
JS
angular.module('app', []);
angular.module('app')
.controller('MainController', mainController);
mainController.$inject = ['$scope'];
function mainController($scope){
var vm = this;
vm.itemsList = [];
vm.addItem = addItem;
function addItem(){
vm.itemsList.push({});
}
}
HTML
<div data-ng-app="app">
<div data-ng-controller="MainController as mainVm">
<button data-ng-click="mainVm.addItem()">ADD</button>
<table>
<thead>
<th>ID</th>
<th>Name</th>
</thead>
<tbody>
<tr data-ng-repeat="item in mainVm.itemsList">
<td><input type="number" data-ng-model="item.id"></td>
<td><input type="text" data-ng-model="item.name"></td>
</tr>
</tbody>
</table>
{{mainVm.itemsList}}
</div>
</div>
Take a look at this question for more information
"Thinking in AngularJS" if I have a jQuery background?

Angular ng-if + function not work

In this first code when I change the anoini, the gerar() function show the old value.
But, when I remove <div ng-if.... works fine.
do you knows what's wrong ?
Tks
// JavaScript Document
var app = angular.module('dadosHist', []);
app.controller('dadosHistCtrl', function($scope) {
$scope.mesini = 1; $scope.anoini = 2011;
$scope.mesfim = 7; $scope.anofim = 2015;
$scope.log = "";
$scope.escolherperiodo = true;
$scope.gerar = function() {
this.log = this.anoini;
meses = ((this.anofim - this.anoini) * 12) + (12 - this.mesini) + this.mesfim;
qtdLoop = arrEstacoes.length * meses;
tempoEstimadoMinutos = Math.round((qtdLoop * 20) / 60 );
this.log = 'Tempo Estimado: ' + tempoEstimadoMinutos + ' min.' ;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="dadosHist" ng-controller="dadosHistCtrl">
<input type="checkbox" ng-model="escolherperiodo">Escolher Período<br>
<div ng-if="escolherperiodo">
<input type="text" ng-model="mesini" placeholder="Mes">/<input type="text" ng-model="anoini" placeholder="Ano"><br>
<input type="text" ng-model="mesfim" placeholder="Mes">/<input type="text" ng-model="anofim" placeholder="Ano"><br>
</div>
<button ng-click="gerar()">Gerar</button> <br>
{{log}}
</div>
Always use a dot in ng-model ! . In other words use objects not primitives.
ng-if creates a child scope and since you are using primitives in ng-model you are losing 2 way binding with scope from this child scope.
var myModel ={
mesini : 1,
anoini : 2011,
mesfim : 7,
anofim : 2015
};
$scope.myModel = myModel;
HTML
<input type="text" ng-model="myModel.mesini">
Then in function:
$scope.gerar = function() {
$scope.log = myModel.anoini;
var meses = ((myModel.anofim - myModel.anoini)......
.....
}
Understanding scope nesting in angular is the most important thing to learn when using the framework
You should not assign value to this, but to $scope inside gerar function:
$scope.gerar = function() {
$scope.log = $scope.anoini;
meses = (($scope.anofim - $scope.anoini) * 12) + (12 - $scope.mesini) + $scope.mesfim;
qtdLoop = arrEstacoes.length * meses;
tempoEstimadoMinutos = Math.round((qtdLoop * 20) / 60 );
$scope.log = 'Tempo Estimado: ' + tempoEstimadoMinutos + ' min.' ;
}

AngularJS Dynamically Update ng-style

I'm trying to set an initial background color on a set of div's that are created using ng-repeat. I also want to update the background color for each div on hover.
I'm able to see the correct color on hover, but I'm not sure how I can set the initial background color for each div when I have a variable in the ng-style. I did try looping through the projects in the controller and calling the rgba function in my controller, but it just applies that last background color to all of the divs.
Here is my ng-repeat block:
<section ng-controller="ProjectsCtrl" class="work">
<div ng-repeat="project in projects" class="work-example" ng-style="{'background-color': '{{ project.background_color }}'}">
<a href="#">
<div class="inner" ng-style="{'background-image': 'url({{ project.image_url }})'}">
<div class="type">{{ project.title }}</div>
<div class="client">{{ project.client }}</div>
<div class="overlay" ng-style="background" ng-mouseover="rgba(project.background_color, 0.2)"></div>
</div>
</a>
</div>
</section>
My controller has a function called rgba that will take the hex (coming from rails api call) and turn it into rgba.
App.controller('ProjectsCtrl', ['$scope', 'Projects', function($scope, Projects) {
$scope.rgba = function(hex, opacity) {
var hex = hex.replace('#', ''),
r = parseInt(hex.substring(0,2), 16),
g = parseInt(hex.substring(2,4), 16),
b = parseInt(hex.substring(4,6), 16),
result = 'rgba('+ r + ',' + g + ',' + b + ',' + opacity + ')';
$scope.background = { 'background-color': result }
}
$scope.projects = Projects.query();
}
]);
Here is the service my controller is calling:
App.factory('Projects', ['$resource', function($resource) {
return $resource('/api/projects/:id', {
id: '#id'
});
}
]);
Here is my attempt to update ng-style from the controller (but assigns all divs the last background color):
$scope.projects = Projects.query(function(projects){
angular.forEach(projects, function(value, index) {
$scope.rgba(value.background_color, '0.8');
});
});
I'm pretty new to the AngularJS world, so I hope all of this makes sense. Any help is greatly appreciated. Thanks!
The reason why "it applies that last background color to all of the divs" is because, of the following code.
$scope.rgba = function(hex, opacity) {
var hex = hex.replace('#', ''),
r = parseInt(hex.substring(0,2), 16),
g = parseInt(hex.substring(2,4), 16),
b = parseInt(hex.substring(4,6), 16),
result = 'rgba('+ r + ',' + g + ',' + b + ',' + opacity + ')';
$scope.background = { 'background-color': result }
}
$scope.projects = Projects.query(function(projects){
angular.forEach(projects, function(value, index) {
$scope.rgba(value.background_color, '0.8');
});
});
When your angular.forEach runs, it is invoking $scope.rgba which is in turn updating the value of $scope.background to the latest background color. Inside your HTML markup, you have <div class="overlay" ng-style="background" ng-mouseover="rgba(project.background_color, 0.2)"></div> which looks for a variable called background in $scope.
Now the catch here is, as this markup is inside an ng-repeat every single repetition of the markup will have the same value for ng-style as everything is looking at the same object $scope.background.
Instead, what I would suggest you to do is the following.
Projects.query(function (projects) {
$scope.projects = projects; // <- $scope.projects is set when the query completes
angular.forEach(projects, function (value, index) {
$scope.rgba(project, '0.8');
});
});
$scope.rgba = function(project, opacity) {
var hex = project.background_color.replace('#', ''),
r = parseInt(hex.substring(0,2), 16),
g = parseInt(hex.substring(2,4), 16),
b = parseInt(hex.substring(4,6), 16),
result = 'rgba('+ r + ',' + g + ',' + b + ',' + opacity + ')';
project.backgroundStyle = { 'background-color': result }
}
And then your markup:
<section ng-controller="ProjectsCtrl" class="work">
<div ng-repeat="project in projects" class="work-example" ng-style="{'background-color': '{{ project.background_color }}'}">
<a href="#">
<div class="inner" ng-style="{'background-image': 'url({{ project.image_url }})'}">
<div class="type">{{ project.title }}</div>
<div class="client">{{ project.client }}</div>
<div class="overlay" ng-style="project.backgroundStyle" ng-mouseover="rgba(project.background_color, 0.2)"></div>
</div>
</a>
</div>
</section>
I believe this would solve your issue of every div having the latest background.
$resource.query returns a wrapper object containing a promise instead of the actual results of the query.
So with the following code:
$scope.projects = Projects.query(function(projects){
angular.forEach(projects, function(value, index) {
$scope.rgba(value.background_color, '0.8');
});
})
You are actually setting $scope.projects to the promise wrapper.
You need to change it to something like this:
Projects.query(function (projects) {
$scope.projects = projects; // <- $scope.projects is set when the query completes
angular.forEach(projects, function (value, index) {
$scope.rgba(value.background_color, '0.8');
});
});

Resources