Angular.js ng-style won't bind value - javascript

I've got a problem with angularjs and even after research I just couldn't find where I'm wrong.
I need to recalculate the css value "left" for an element. I'm using the 'ng-style' directive and a method that will return an object with the css value. Thats - afaik - what I have to do. But when I update the value, it wont update the style.
ng-bind usage:
<div ng-style="getCssShiftObject()">
method to create object
$scope.getCssShiftObject =function(){
return {'left':this.cssShift+'px'};
};
method to change the object
$scope.nextPosition = function(){
if((this.currentPosition+1) <= this.maxPosition){
this.currentPosition = this.currentPosition+1;
this.cssShift = (this.currentPosition*this.slideSize)*-1;
}
return this.currentPosition;
};
It will update at another place in the content when I use it like that:
{{getCssShiftObject()}}
I hope you can give mit a hit, thanks for your time!

I came across a similar problem. I was trying to use ngStyle to load a background image, but if the variable in an expression is not immediately available (which might be the case if it's part of a resource promise), it won't work.
To address this, I created my own ngStyle directive that addresses this issue. Hopefully this is better than creating functions for every single scenario where you want to use ngStyle in this way.
app.directive("myStyle", function (){
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var el = element[0],
attr = el.getAttribute('style');
el.setAttribute('style', attr);
// We need to watch for changes in the style in case required data is not yet ready when compiling
attrs.$observe('style', function (){
attr = el.getAttribute('style');
if(attr)
{
el.setAttribute('style', attr);
}
});
}
};
});
Then, you can use it this way:
<a my-style style="background-image: url('{{promise.myImage}}')"></a>

Thx for your time! I solved the Problem with the input from Cherniv, but I'm not sure how. I changed the way I create the values. Now it's working.
$scope.calcCssShift = function(){
this.cssShift = ($scope.currentPosition * $scope.slideSize)*-1;
};
$scope.getCssShiftObject =function(){
return {'left':$scope.cssShift+'px'};
};
$scope.nextPosition = function(){
if((this.currentPosition+1) <= this.maxPosition){
$scope.currentPosition = this.currentPosition+1;
$scope.calcCssShift();
}
};

I had a similar problem with the style attribute. My binding was not working in some browsers, especially IE. I solved it by using ng-attr-style="{{yourBindingExpression}}".
Read more about ng-attr interpolation at https://docs.angularjs.org/guide/interpolation

Related

How to access an HTML element that is dynamically filled with values?

I've read lots of stackoverflow questions with no luck. My problem is, I have an HTML page in which I have
<select id="myid"></select>
and there, there's a Firebase command that retrieves names of values i need, and put it inside the <option> like HERE:
reference.on("child_added", function (childSnapshot){
var key = childSnapshot.key;
var opt = document.createElement('option');
opt.textContent = key;
document.getElementById('myid').appendChild(opt);
});
Now, i need to somehow access these values, that by the way are correctly appearing in both HTML and my site, however:
var val = document.getElementById('myid').value;
console.log(val);
It always returns blank in console. I don't know how else can I access it. Whenever I type those values in <option> by myself in HTML, everything works as it should and console returns the names that are in database.
#edit: as far as i tried to crack it, it seems to do with the fact that javascript cannot access elements, that for javascript itself aren't yet loaded, but i tried doing window.onload and other similar ones and they don't help.
You can use AngularJS, with directive $scope.watch, i will write a simple example of how to use and the link of documentation, if you have any question talk back to me!
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.buttonClicked = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
}
AngularJS Documentation
I hope help with this :)

Angular directive and SVG

so, this is a little complicated.
I have a directive that loads an SVG file based on some criteria. It is a large directive so I won't be posting it here as it does not affect my issue, you just need to know it exists.
In that directive I have a template file that has this bit of HTML:
<div ng-include="svgPath" onload="loaded()"></div>
note the onload function. This is called when the SVG has loaded.
Inside this SVG there are some hidden groups that I need to interact with. Because this is changing the DOM I have written another directive, which looks like this:
.directive('kdGraphicsRepeater', function () {
return {
restrict: 'A',
scope: {
targetElementId: '#kdGraphicsRepeater'
},
templateUrl: '/assets/tpl/directives/graphicsRepeater.tpl.html',
link: function (scope, element) {
// Declare our options
scope.options = [];
// Get our target elements
var target = angular.element(document.getElementById(scope.targetElementId)),
svg = angular.element(target.find('svg')),
children = svg.children();
console.log(target.length);
console.log(svg.length);
console.log(children.length);
// Loop through the SVG children
for(var i = 0; i < children; i++) {
// Get the current child
var child = children[i],
childId = child.attr('id');
console.log(childId);
// If we have an option
if (childId && childId.indexOf('options-') > -1) {
// Push the name to our options array
scope.options.push(childId.replace('options-'));
}
}
}
}
})
and the corresponding template looks like this:
<div ng-repeat="item in options">
{{ item }}
</div>
As you should be able to make out. I am trying to loop through the SVG immediate children and check to see if any of the elements have a partial id match to options-. If they do I add them (omitting the "options-" string) to the scope.options variable. My template simply loops through them displays them.
The problem is that the SVG has not loaded for this directive and the console.log(svg.length) returns 0 for that very reason. I need some way of knowing when the SVG has loaded.
Can someone help me out?
Okay, I'm not sure about this but maybe try using the ng-if directive by angular.
in your loaded() function, have like a boolean that says its loaded, say its called svgLoaded. use ng-init instead of onload. Make sure this would be the same scope as the one in your svg.
then in your svgPath, bind ng-if to your boolean
<"element where you are attaching kdGraphicsRepeater"
ng-if="svgLoaded" kdGraphicsRepeater
>
Sorry, I'm making assumption that you have same scope, and that is possible to do.

Angular: Selecting elements from DOM

I'm using an ng-repeat to create items. I would like to determine the height of each element that is created by using a function.
I know how to pass the index of the element that is created by ng-repeat to the function that should determine the height, but I'm getting stuck in actually selecting that item.
This is what I'm using now:
$scope.getItemHeight = function(index) { // index is index of element in ng-repeat
var itemHeight = angular.element('li').eq('+index+').offsetHeight;
return itemHeight;
};
But that doesn't work due to error: Error: [jqLite:nosel] Looking up elements via selectors is not supported by jqLite!
I also tried:
$scope.getItemHeight = function(index) {
var itemHeight = document.querySelectorAll('ul:nth-child('+index+')');
return itemHeight;
};
This returns an element with length 0 so the selector doesn't work.
What am I missing here?
Codepen here: http://codepen.io/squrler/pen/LxsfE?editors=101
EDIT:
What I want is not possible at this time. More information here: https://github.com/driftyco/ionic/issues/1691
Edit: After looking at this further it seems a bit more complicated. The directive that is firing the repeater needs to run in order for the lis to be rendered, as the li is rendered (assuming you move this to a directive) it triggers the directive to get its height, but the li and it's corresponding data are not yet fully rendered and thus have no height. If you wait for the rendering using timeout, the repeater will just continue rendering without valid height data while the timeout waits. So it seems you have a bit of a conundrum. You might try using http://binarymuse.github.io/ngInfiniteScroll/ or something of the like.
This should be put in a directive which will give you easy access to the li as it is rendered.
Something like:
.directive('getHeight', ['$filter', function ($filter) {
'use strict';
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
var li = element[0];
var height = li.offsetHeight;
console.log('Height': height)
}
};
}]);
Not sure what you're looking to do with the height once you have it...
Otherwise you can just go:
var ul=document.getElementsByTagName('ul')[0];
var li=ul.getElementsByTagName('li')[index];
var height=li.offsetHeight;

Binding multiple events to elements stored in variable

I know that puting reference of HTML element into the variable is a good practice if I need to reference to this element many times. But I run into the problem with this while making my project. How can I bind multiple and the same events to the elements which are stored into the variable?
For now I deal with it this way:
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
bind(seriesEl);
bind(brandEl);
bind(seriesEl);
function bind($el) {
$el.on("keypress", function () {
// some code..
});
}
I need something like $(producerEl, brandEl, seriesEl).on...
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
producerEl.add(brandEl).add(seriesEl).on("click", function () {
alert('hello');
});
If you are trying to keep your code readable, might I suggest this approach?
$("#js-producer, #js-brand, #js-series").on('keypress', function () { });
Hmm. If you're using these selectors only one, don't care about "I know it is good to". The best solution is the one provided by David Smith.
Anyway, jQuery is using the sizzle selector engine, who has it's own cache. You can ask for
$("#js-producer, #js-brand, #js-series")
the result would be cached and reused.

Angularjs ng-show based on callback

I'm new to angular so maybe I am missing something.
On my registration form I need a location from the user. Based on whether they allow/support navigator.geolocation, I want to show a drop down to choose a location.
Controller.
$scope.showLoc = true;
if(navigator && navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(pos){
$scope.showLoc = false;
},function(err){
$scope.showLoc = true;
});
}
and my form:
<form class="form-horizontal" name="register" ng-controller="RegisterCtrl" ng-submit="registerUser()"> ....
<div ng-show="showLoc" accesskey="">
test
</div>
....
</form>
This approach is not working for me. Any insight would be appreciated.
Whenever you do some form of operation outside of angularjs, such as doing an ajax call with jquery, or grabbing the geolocation like your example here you need to let angular know to update itself. I looked at your jsfiddle and changed some of your code around to look like this:
var app = angular.module('myApp', []);
app.controller('RegisterCtrl',function($scope){
$scope.showLoc = false;
navigator.geolocation.getCurrentPosition(function(pos){
$scope.showLoc = true;
$scope.$apply();
},function(err){
$scope.showLoc = false;
$scope.$apply();
});
});
And now showLoc changes to true on the update. Here's the documation on using the $apply() method http://docs.angularjs.org/api/ng.$rootScope.Scope#$apply
jsFiddle http://jsfiddle.net/yLFNP/6/
Edit: My answer was edited, but I don't agree with the edit. While you could wrap the $apply method around $scope.showLoc = false to make it "shorter" You're really only saving 1 character (the semi colon). Also, I tend to like the $apply method after a bunch of logic instead of wrapping everything in it. If there were more things I was doing to the scope, you'd either have to write each additional one like so:
$scope.$apply($scope.var1 = newValue1);
$scope.$apply($scope.var2 = newValue2);
$scope.$apply($scope.var2 = newValue3);
Which I find overkill, or you could use the function method:
$scope.$apply(function(){
$scope.var1 = newValue1;
$scope.var2 = newValue2;
$scope.var3 = newValue3;
});
Or directly after code that needs "applying":
$scope.var1 = newValue1;
$scope.var2 = newValue2;
$scope.var3 = newValue3;
$scope.$apply();
By doing it this method all the time, you're code is easily transferable and very readable. Also less lines is not always the best method.

Categories