Angular js not updating dom when expected - angularjs

I have a fiddle for this, but basically what it's doing it geo-encoding an address that is input into a textbox. After the address is entered and 'enter' is pressed, the dom does not immediately update, but waits for another change to the textbox. How do I get it to update the table right after a submit?
I'm very new to Angular, but I'm learning. I find it interesting, but I have to learn to think differently.
Here is the fiddle and my controller.js
http://jsfiddle.net/fPBAD/
var myApp = angular.module('geo-encode', []);
function FirstAppCtrl($scope, $http) {
$scope.locations = [];
$scope.text = '';
$scope.nextId = 0;
var geo = new google.maps.Geocoder();
$scope.add = function() {
if (this.text) {
geo.geocode(
{ address : this.text,
region: 'no'
}, function(results, status){
var address = results[0].formatted_address;
var latitude = results[0].geometry.location.hb;
var longitude = results[0].geometry.location.ib;
$scope.locations.push({"name":address, id: $scope.nextId++,"coords":{"lat":latitude,"long":longitude}});
});
this.text = '';
}
}
$scope.remove = function(index) {
$scope.locations = $scope.locations.filter(function(location){
return location.id != index;
})
}
}

Your problem is that the geocode function is asynchronous and therefore updates outside of the AngularJS digest cycle. You can fix this by wrapping your callback function in a call to $scope.$apply, which lets AngularJS know to run a digest because stuff has changed:
geo.geocode(
{ address : this.text,
region: 'no'
}, function(results, status) {
$scope.$apply( function () {
var address = results[0].formatted_address;
var latitude = results[0].geometry.location.hb;
var longitude = results[0].geometry.location.ib;
$scope.locations.push({
"name":address, id: $scope.nextId++,
"coords":{"lat":latitude,"long":longitude}
});
});
});

Related

Two $firebaseArrays on one page & one ctrl

I would like to use two different $firebaseArrays on one view with one controller. But only one of them works and the other only works if i put him in his own controller.
from my factory file:
.factory("AlphaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('alpha/');
return $firebaseArray(ref);
}
])
.factory("BetaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('beta/');
return $firebaseArray(ref);
}
])
and my controller:
.controller('DemoCtrl', function($scope, AlphaFactory, BetaFactory) {
$scope.alphaJobs = AlphaFactory;
$scope.addalphaJob = function() {
$scope.alphaJobs.$add({
Testentry: $scope.loremipsum,
timestamp: Date()
});
$scope.alphaJob = "";
};
$scope.betaJobs = BetaFactory;
$scope.addbetaJob = function() {
$scope.betaJobs.$add({
Testentry2: $scope.dolorest,
timestamp: Date()
});
$scope.betaJob = "";
};
)}
Are you sure it is not a simple matter of a promise has not finished?
var alphaJobs = AlphaFactory;
alphaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.alphaJobs = alphaJobs;
});
var betaJobs = BetaFactory;
betaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.betaJobs = betaJobs;
});

$scope.$watch does not seem to watch factory variable

I'm a beginner to angularjs. In my NFC project, I want to be able to GET from the server data based on a changing patientId.
However, I am not able to see my $watch execute correctly, even though I see that the patientId changes each time I scan a new NFC tag.
var nfc = angular.module('NfcCtrl', ['PatientRecordsService'])
nfc.controller('NfcCtrl', function($scope, NfcService, PatientRecordsService) {
$scope.tag = NfcService.tag;
$scope.patientId = NfcService.patientId
$scope.$watch(function() {
return NfcService.patientId;
}, function() {
console.log("Inside watch");
PatientRecordsService.getPatientRecords(NfcService.patientId)
.then(
function(response) {
$scope.patientRecords = response
},
function(httpError) {
throw httpError.status + " : " +
httpError.data;
});
}, true);
$scope.clear = function() {
NfcService.clearTag();
};
});
nfc.factory('NfcService', function($rootScope, $ionicPlatform, $filter) {
var tag = {};
var patientId = {};
$ionicPlatform.ready(function() {
nfc.addNdefListener(function(nfcEvent) {
console.log(JSON.stringify(nfcEvent.tag, null, 4));
$rootScope.$apply(function(){
angular.copy(nfcEvent.tag, tag);
patientId = $filter('decodePayload')(tag.ndefMessage[0]);
});
console.log("PatientId: ", patientId);
}, function() {
console.log("Listening for NDEF Tags.");
}, function(reason) {
alert("Error adding NFC Listener " + reason);
});
});
return {
tag: tag,
patientId: patientId,
clearTag: function () {
angular.copy({}, this.tag);
}
};
});
Not sure what I'm missing here - please enlighten me!
Update
Per rakslice's recommendation, I created an object to hold my data inside the factory, and now the html (with some server side delay) correctly displays the updated values when a new NFC tag is scanned.
var nfc = angular.module('NfcCtrl', ['PatientRecordsService'])
nfc.controller('NfcCtrl', function($scope, NfcService) {
$scope.tagData = NfcService.tagData;
$scope.clear = function() {
NfcService.clearTag();
};
});
nfc.factory('NfcService', function($rootScope, $ionicPlatform, $filter, PatientRecordsServi\
ce) {
var tagData = {
tag: null,
patientId: null,
patientRecords: []
};
$ionicPlatform.ready(function() {
nfc.addNdefListener(function(nfcEvent) {
//console.log(JSON.stringify(nfcEvent.tag, null, 4));
$rootScope.$apply(function() {
tagData.tag = nfcEvent.tag;
tagData.patientId = $filter('decodePayload')(tagData.tag.ndefMessage[0]);
PatientRecordsService.getPatientRecords(tagData.patientId)
.then(
function(response) {
tagData.patientRecords = response
},
function(httpError) {
throw httpError.status + " : " +
httpError.data;
});
});
console.log("Tag: ", tagData.tag);
console.log("PatientId: ", tagData.patientId);
}, function() {
console.log("Listening for NDEF Tags.");
}, function(reason) {
alert("Error adding NFC Listener " + reason);
})
});
return {
tagData: tagData,
clearTag: function() {
angular.copy({}, this.tagData);
}
};
});
Your code doesn't update the patientId value in the returned NfcService, only the local variable patientId inside the factory function.
Try saving a reference to the object you're returning in the factory function as in a local variable and use that to update the patientId.
For instance, change the creation of the object to put it in a local variable:
var nfcService = {
tag: tag,
patientId: patientId,
clearTag: function () {
angular.copy({}, this.tag);
}
};
...
return nfcService
and then change the patientId update to change the value in the object through the variable.
nfcService.patientId = $filter('decodePayload')(tag.ndefMessage[0]);
Update:
The basic fact about JavaScript that you need to understand is that when you assign one variable to another, if the first variable had a primitive data value the second variable gets a copy of that value, so changing the first variable doesn't affect the second variable after that, but if the first variable had an object reference the second variable gets pointed at that same object that the first variable is pointed at, and changing the object in the first variable after that will affect what you see through the second variable, since it's looking at the same object.
A quick experiment in the browser JavaScript console should give you the idea:
> var a = 1;
> a
1
> var b = a;
> b
1
> a = 5;
> a
5
> b
1
vs.
> var a = {foo: 1}
> var b = a
> a.foo = 5
> a.foo
5
> b.foo
5

Angularjs State transition

I am building a hybrid mobile app using ionic framework and cordova (first time).I am having problems with state transition because by default angular renders the template before completing the transition.This makes the the app look slow (when you click a menu item and wait for it to come).This happens only for those who load data from local storage or service! My Question is: How can I make the template come empty in the moment I click the menu item , then show a loader until the template is ready.Below is some code is use in my menu controller for the state transition!
//I use ng-click="navigateTo('state name')"
$scope.navigateTo = function (stateName) {
$timeout(function () {
$mdSidenav('left').close();
if ($ionicHistory.currentStateName() != stateName) {
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$state.go(stateName);
}
}, ($scope.isAndroid == true ? 1000 : 0));
};// End navigateTo.
Below is the controller code for the view that needs a solution
appControllers.controller("calendar_Ctrl", function($scope,$rootScope, $state,$stateParams, $ionicHistory, $filter, $q, $timeout, $log, MaterialCalendarData, $moment) {
$scope.isAnimated = $stateParams.isAnimated;
$scope.selectedDate = null;
$scope.weekStartsOn = 0;
$scope.dayFormat = "d";
$scope.disableFutureDates = false;
$scope.directionn = "horizontal";
$scope.setDirection = function(direction) {
$scope.directionn = direction;
$scope.dayFormat = direction === "vertical" ? "EEEE, MMMM d" : "d";
};
$scope.dayClick = function(date) {
$scope.msg = "You clicked " + $filter("date")(date, "MMM d, y h:mm:ss a Z");
};
$scope.setContentViaService = function() {
var today = new Date();
MaterialCalendarData.setDayContent(today, '<span> :oD </span>')
}
$scope.getItems = function(){
if(localStorage.getItem("eventsData")){
var eventsData = localStorage.getItem("eventsData");
return JSON.parse(eventsData);
}else{
return [];
}
}
var events = $scope.getItems();
// You would inject any HTML you wanted for
// that particular date here.
var numFmt = function(num) {
num = num.toString();
if (num.length < 2) {
num = "0" + num;
}
return num;
};
var loadContentAsync = false;
$log.info("setDayContent.async", loadContentAsync);
$scope.setDayContent = function(date) {
var key = [date.getFullYear(), numFmt(date.getMonth()+1), numFmt(date.getDate())].join("-");
var data = (events[key]||[{ type: ""}]);
if (loadContentAsync) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(data);
});
return deferred.promise;
}
return data;
};
$scope.isAnimated = $stateParams.isAnimated;
});
Thank You Very Much for your time and help!!
Hi Use $ionicLoading Service to solve this problem,
http://ionicframework.com/docs/api/service/$ionicLoading/

ng-infinite scroll angular setting offset

Need to keep track of my offset so that I can get the next set each time either scroll or click 'Load more'. It improves performance. I am trying out here by setting offset and limit and passing as request params to my node server,but how to update or increment after that limit using offset:
my url as: /foo?limit=7&&offset=0;
My angular controller function as:
$scope.findDetails = function(){
var limit = 10;
var offset = 0;
//DataService.getUsers(limit,offset).then(function(customerdetails){
DataService.getUsers({limit,offset},function(customerdetails){
$scope.customers = customerdetails;
}, function(error){
$scope.status = 'Unable to load customer data: ' + error.message;
});
};
You must keep the offset in the scope of the controller and update the offset every time the infinite directive request more records to display:
$scope.limit = 10;
$scope.offset = 0;
//bind this function to the ng-infinite directive
$scope.infiniteScrollFunction = function() {
$scope.offset += $scope.limit;
$scope.findDetails();
};
$scope.findDetails = function() {
DataService.getUsers({limit: $scope.limit,offset: $scope.offset},
function(customerdetails){
...
}
var $scope.height = $('#div-height').height()
var flag = false;
var $scope.customers = []
$(window).scroll(function() {
if ($(this).scrollTop() > $scope.height-2000) {
if (!flag) {
flag = true;
refreshCustomers();
}
}
});
function refreshCustomers() {
DataService.getCustomers().then(function (data) {
$scope.customers = $scope.customers.concat(data);
setTimeout(function () {
$scope.height = $('#div-height').height();
flag = false
}, 0.1);
});
}
In DataService
factory.getCustomers = function(){
return $http.get(...api......&&limit=7).then(function (results) {
var customers = results.data.customers;
return customers;
});
};
Now after the window is scrolled up to certain height(windowHeight-2000px), the api is called again to get data. The previous data is being concatenated with present data.

View doesn't get updated from Service

I am trying to display an object (songTitle) from my service. The initial state (tmp) is displayed. If I am changing the object in the service, the view doesnt get updated.
Js:
var party = angular.module("party", []);
party.run(function () {
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
});
party.service('PlayerService', function ($window) {
this.playlist = [
"https://www.youtube.com/watch?v=fnW2uLwHAas",
"https://www.youtube.com/watch?v=iPT8DA32U6U",
"https://www.youtube.com/watch?v=eGjEnfQl37s",
"https://www.youtube.com/watch?v=nFtTY2S20mI",
"https://www.youtube.com/watch?v=UmXQiPLoLTk",
"https://www.youtube.com/watch?v=PbVx85DS9zc",
"https://www.youtube.com/watch?v=ciidn3nEoiE",
"https://www.youtube.com/watch?v=sm0DgkBEnUI",
"https://www.youtube.com/watch?v=J2OCSWF7sAw",
"https://www.youtube.com/watch?v=y_-giRHtuv8",
"https://www.youtube.com/watch?v=iPT8DA32U6U",
"https://www.youtube.com/watch?v=eGjEnfQl37s",
"https://www.youtube.com/watch?v=nFtTY2S20mI",
"https://www.youtube.com/watch?v=UmXQiPLoLTk",
"https://www.youtube.com/watch?v=PbVx85DS9zc"
];
this.player = {};
this.pbTimer = null;
this.songTitle = "tmp";
$window.onYouTubeIframeAPIReady = function () {
this.player = new YT.Player('ytplayer', {
height: '100',
width: '100',
videoId: 'ciidn3nEoiE',
events: {
'onReady': onPlayerReady
}
});
}
function onPlayerReady() {
console.log("db ready");
songTitle = player.getVideoData().title;
console.log(songTitle);
}
this.playVideo = function (url) {
console.log("db playVideo " + url);
player.loadVideoById(url.split("watch\?v=")[1], 0, "large");
console.log(player);
}
});
party.controller("FrontController", function ($scope) {
$scope.front = {};
$scope.front.title = "PARTY";
});
party.controller("PartyController", ['$scope', 'PlayerService', function ($scope, PlayerService) {
$scope.party = {};
$scope.party.title = "PARTY";
Sortable.create(playlist, { /* options */ });
$scope.playlist = PlayerService.playlist;
$scope.playVideo = function (url) {
PlayerService.playVideo(url);
}
$scope.songTitle = PlayerService.songTitle;
}]);
HTML
<body ng-app="party">
<div ng-controller="PartyController" class="container-fluid">
...
<p id="playertitle">{{songTitle}}</p>
...
Log:
db ready
Blackmill Feat. Veela - Life (Full Version)
The problem is in your onPlayerReady function. The line songTitle = player.getVideoData().title; doesn't set songTitle on your service, but rather on the global scope, which is the window object. Simply using this.songTitle won't help either, because this doesn't refer to your service too in the scope of onPlayerReady.
The easiest solution would be to save a reference to your service outside of onPlayerReady and then use it to assign songTitle:
var self = this;
function onPlayerReady() {
console.log("db ready");
self.songTitle = player.getVideoData().title;
console.log(self.songTitle);
}
Still, this is not enough. Because you change songTitle from outside the Angular world (the Youtube player callbacks), you need to call $scope.$apply to notify Angular something has changed.
For that, you need to inject $rootScope into your service:
party.service('PlayerService', function ($window, $rootScope)
and change songTitle using $rootScope.$apply:
var self = this;
function onPlayerReady() {
console.log("db ready");
$rootScope.$apply(function() {
self.songTitle = player.getVideoData().title;
console.log(self.songTitle);
});
}

Resources