Reputation: 278
I would like to bind a variable took from a json file inside a javascript data field, but i can't use the {{}} operator because it's processed after.
<div class="milestone">
<div class="number" data-animation="true" data-animation-type="number" data-final-number="{{itemSold}}"></div>
<div class="title">Items Sold</div>
</div>
In this way it gives to me a NaN because he can't see the value of itemSold.
This is how itemSold is retrived
var app = angular.module('app', []);
app.controller('AppCtrl', ['$scope', '$http', function($scope, $http)
{
$http.get('app/shared/homeStatistics.json').success(function(data){
$scope.itemSold = data.itemSold;
$scope.themeAndTemplate = data.themeAndTemplate;
$scope.members = data.members;
});
}]);
I think that i have to use something like ng-bing that is processed before, but i dont know how.
Thanks for all suggestions and sorry for my bad english
EDIT 1
The data are correctly retrieved but are processed after the data-final-number so he read a "" at the beginning
my json data
{
"itemSold":1000
}
EDIT 2
here it how it is processed the data-final-number
var handlePageScrollContentAnimation = function() {
$('[data-scrollview="true"]').each(function() {
var myElement = $(this);
var elementWatcher = scrollMonitor.create( myElement, 60 );
elementWatcher.enterViewport(function() {
$(myElement).find('[data-animation=true]').each(function() {
var targetAnimation = $(this).attr('data-animation-type');
var targetElement = $(this);
if (!$(targetElement).hasClass('contentAnimated')) {
if (targetAnimation == 'number') {
var finalNumber = parseInt($(targetElement).attr('data-final-number'));
$({animateNumber: 0}).animate({animateNumber: finalNumber}, {
duration: 1000,
easing:'swing',
step: function() {
var displayNumber = handleAddCommasToNumber(Math.ceil(this.animateNumber));
$(targetElement).text(displayNumber).addClass('contentAnimated');
}
});
} else {
$(this).addClass(targetAnimation + ' contentAnimated');
}
}
});
});
});
};
Upvotes: 2
Views: 1919
Reputation: 136124
I'd like to put watch on 'itemSold' variable and whenever value fetched from $http call, i'll call handlePageScrollContentAnimation handler.
var watchItemSold = $scope.$watch('itemSold', function(newVal, oldVal, scope){
if(newVal != oldVal){
handlePageScrollContentAnimation(); //call handler
watchItemSold(); //watch destructor will ensure that handler will call once
}
});
Hope this will help you. Thnaks.
Upvotes: 0
Reputation: 48972
I suggest using attr.$observer with a finalNumber
directive. This will trigger a function to be executed whenever there is an update. With that said, it's not only rendered one time, whenever the value changes, the view is updated.
.directive('finalNumber',function() {
function link(scope, element, attrs) {
$attrs.$observe('finalNumber', function(value) {
if (!isNaN(value)){
//update accordingly, it's kind of hack to
//bring this code to angular. It's better to write all these
// as angular directives.
handlePageScrollContentAnimation();
}
});
}
return {
link: link
};
});
You need to change your mindset when moving to angular from jQuery background. Angular is an MVC framework and based mainly on databinding. With data binding, the view should not care when and how the model is updated, but whenever there is an update, the view should be aware of it and update the view accordingly.
The above example should be the right way to work with angular. But as I said, bring your jQuery code to angular is quite a hack, all these should be written as directives. I'm not sure whether you need to run your jQuery code only once (running multiple times may cause side effects) after the first update. You may need a bit of hack. This is not recommended and if possible, you should write all these as directives (scrollview
,animation
,finalNumber
,..)
.directive('finalNumber',function() {
function link(scope, element, attrs) {
var hasRun = false;//hack the code to run only once.
$attrs.$observe('finalNumber', function(value) {
if (!isNaN(value) && !hasRun){
//update accordingly, it's kind of hack to
//bring this code to angular. It's better to write all these
// as angular directives.
handlePageScrollContentAnimation();
}
});
}
return {
link: link
};
});
Upvotes: 1
Reputation: 12437
Apparently, you're trying to handle the data-final-number="{{itemSold}}"
attribute before it has been resolved. To handle it after, make sure to only call the handler after the you have retrieved the JSON data and the AngularJS lifecycle have resolved it already for you. To do that, you can use AngularJS's $timeout
, so it will be enqueued and executed after AngularJS manipulation.
var app = angular.module('app', []);
app.controller('AppCtrl', ['$scope', '$http', function($scope, $http, $timeout)
{
$http.get('app/shared/homeStatistics.json').success(function(data){
$scope.itemSold = data.itemSold;
$scope.themeAndTemplate = data.themeAndTemplate;
$scope.members = data.members;
//calling handler after AngularJS applied all two-way-bindings
$timeout(function() {
handlePageScrollContentAnimation();
}, 0);
});
}]);
For further information, read: Anyway to trigger a method when Angular is done adding scope updates to the DOM?
Upvotes: 0
Reputation: 722
If I understood you correctly, you want to have the data just after the component/directive/controller is loaded.
In that case - use resolve in controller, it will inject the result of your ajax request into controller and by the time controller is loaded you'll have all the data.
Router
app.config(function ($routeProvider) {
$routeProvider
.when('/',
{
templateUrl: "app.html",
controller: "AppCtrl"
resolve: {
statResponse: function () {
return $http.get('app/shared/homeStatistics.json');
}
}
}
)
});
Controller
app.controller('AppCtrl', ['$scope', 'statResponse', function($scope, statResponse)
{
if(statResponse && statResponse.status === 200) {
$scope.itemSold = statResponse.data.itemSold;
$scope.themeAndTemplate = statResponse.data.themeAndTemplate;
$scope.members = statResponse.data.members;
}
}]);
Another way is to use ng-cloak on the element. It will hide the element until all variables are resolved.
<div class="number" ng-cloak data-animation="true" data-animation-type="number" data-final-number="{{itemSold}}"></div>
Hope that helps.
Upvotes: 0
Reputation: 142
It's OK in most cases. If you don't want any NaN
s, you have few options:
ng-if
or ng-show
Upvotes: 0