Angular not updating view in $http.get().then() - angularjs

This is my template:
<p>{{$ctrl.status}}</p>
This is the component:
var phoneListModule = angular.module("phoneListModule");
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
this.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
self.status = "again";
});
}
});
And this is the module:
var phoneListModule = angular.module("phoneListModule", []);
The problem is that, the view is compiled properly with the "testing" text, but it's never updated to "again" when the GET request completes.
A console.log confirms that it completed fine
console.log(self.status);
Inside of the then() method.
Why is the view not updating if the dataset has changed?

self is not defined inside of the then() function, you need to define it at the start of the controller.
var phoneListModule = angular.module("phoneListModule");
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
var self = this; <<<< THIS IS IMPORTANT
this.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
self.status = "again";
});
}
});

i think you should avoid using this or self because depending where you are ( like inside function or loop ) it means something different. Try with a variable pointing on this in the beggining of controller
phoneListModule.component("phoneList", {
templateUrl: "phone-list/phone-list.template.html",
controller: function PLController($http) {
var vm = this ;
vm.status = "testing";
$http.get("http://localhost/data.json").then(function(response) {
vm.status = "again";
});
}
});

Related

AngularJS How to access local scope from outside function?

I have this code in my service
orderSewaService.vehicleDetail = function (license_plate) {
//var defer = $q.defer();
var config = {
headers: {
'X-Parse-Application-Id': parseAppId
},
params: {
where: {
vehicle_license_plate: license_plate,
vehicle_status: 'available'
},
limit: 1,
include: 'car_id.car_class_id,pool_id.city_id,partner_id.user_id'
}
}
return $http.get('http://128.199.249.233:1337/parse/classes/vehicle', config).then(function (response) {
var detail = {
license_plate: response.data.results[0].vehicle_license_plate,
photo: response.data.results[0].vehicle_photo,
partner_name: response.data.results[0].partner_id.user_id.full_name,
year: response.data.results[0].vehicle_year,
class: response.data.results[0].car_id.car_class_id.name,
pool_address: response.data.results[0].pool_id.pool_address,
city: response.data.results[0].pool_id.city_id.city_name,
zone_id: response.data.results[0].zone_id.objectId,
car_class_id: response.data.results[0].car_id.car_class_id.objectId
};
return detail;
//defer.resolve(detail);
}, function (error) {
//defer.reject(error);
return error;
});
//return defer.promise;
};
in my controller
$scope.vehicle = {};
orderSewaService.vehicleDetail($routeParams.license_plate).then(function(response){
$scope.vehicle = response;//rendered in view
console.log($scope.vehicle); //log object success
}, function (error) {
console.log(error);
});
console.log($scope.vehicle); //doesn't work //empty object
//My goal is I will call other service function like this
orderSewaService.infoTarif($scope.vehicle.zone_id, $scope.vehicle.car_class_id).then(...);
Already read this access scope data from outside function but looks like to complex or not suit for my simple goal.
How I can access $scope.vehicle outside function or how to achieve my goal ?
And I don't think $rootScope is good solution in this case.
You need to declare $scope.vehicle outside the function call,
somewhere in your controller at the begining,
If it's an array
$scope.vehicle =[];
The problem is with the way this controller code flow works.
$scope.vehicle = {}; //vehicle property is declared and defined as empty obj in the $scope
orderSewaService.vehicleDetail($routeParams.license_plate)
This is an ajax call, js calls this method and then goes to the next line , after the end of this method, i.e.
console.log($scope.vehicle); without waiting for the call to return and populate $scope.vehicle with your response.
So, try this:
In Controller:
`
$scope.vehicle = {};
orderSewaService.vehicleDetail($routeParams.license_plate).then(function(response){
$scope.vehicle = response;//rendered in view
getInfoTarif();
}, function (error) {
console.log(error);
});
function getInfoTarif(){
console.log($scope.vehicle);
orderSewaService.infoTarif($scope.vehicle.zone_id,$scope.vehicle.car_class_id).then(...);
}
`
I think there are two matter of concerns in this question.
Firstly - sync & async methods
Since orderSewaService.vehicleDetail is asynchronous, $scope.vehicle would be null.
If you are not sure what that means, compare the two:
var foo = null;
foo = ['a','b'];
console.log(foo); // ['a','b']
versus
var foo = null;
setTimeout(function(){
foo = ['a','b'];
console.log(foo); // array
}, 500); // or any value including zero
console.log(foo); // null
Conclusively, your code should look like this:
$scope.vehicle = {};
orderSewaService
.vehicleDetail($routeParams.license_plate)
.then(function(response){
$scope.vehicle = response;//rendered in view
console.log($scope.vehicle); //log object success
//My goal is I will call other service function like this
orderSewaService.infoTarif($scope.vehicle.zone_id, $scope.vehicle.car_class_id).then(...);
}, function (error) {
console.log(error);
});
There are a ton of articles and docs that describe this, if you are further interested.
Secondly - load contents before reaching controller
Now, from how you described the problem, it seems like you also want to load the contents of orderSewaService.vehicleDetail based on a URL parameter before it reaches the controller. Otherwise, you will have to call orderSewaService.vehicleDetail and orderSewaService.infoTarif in every controller.
A much cleaner and more common approach is to use ui-router's $stateProvider. Tutorials here
If you run a few examples from their docs, you can inject dependencies into your controller like this:
app.route.js
$stateProvider
.state('vehicles', {
url: '/vehicles',
resolve: {
vehicles: ['VehiclesService', function(VehiclesService){
return VehiclesService.getAll();
}]
},
controller: 'VehiclesListCtrl',
templateUrl: 'vehicles.html'
})
.state('vehicles.detail', {
url: '/vehicles/:vehicleId',
resolve: {
info: ['VehiclesService', '$stateParams', function(VehiclesService, $stateParams){
return VehiclesService.get($stateParams.vehicleId)
.then(function(vehicle){
return orderSewaService.infoTarif(vehicle.zone_id, vehicle.car_class_id)
.then(function(tarif){
return {
vehicle: vehicle,
tarif: tarif
};
});
});
}]
},
controller: 'VehicleDetailCtrl',
templateUrl: 'vehicle.detail.html'
});
vehicle.detail.controller.js
.controller('VehicleDetailCtrl', VehicleDetailCtrl);
VehicleDetailCtrl.$inject = [
'$scope',
'info'
];
function VehicleDetailCtrl(
$scope,
info
) {
console.log('vehicle %o tarif %o', info.vehicle, info.tarif);
}
vehicles.controller.js
.controller('VehiclesCtrl', VehiclesCtrl);
VehiclesCtrl.$inject = [
'$scope',
'vehicles'
];
function VehiclesCtrl(
$scope,
vehicles
) {
console.log('vehicles list %o', vehicles);
}
To access this state, you need to do something like
menu.html
<a ui-sref="vehicles.detail({vehicleId: 1234})">
I purposely did not make vehicles route abstract for illustration purposes. You may want to look into that if you want to create nested state/views.
I hope this helps.

How to get component's controller's scope from animation?

I've got a component with an animation defined as such:
angular
.module('slider')
.component('slider', {
templateUrl: '/mytemplate.html',
controller: function SliderController() {
var self = this;
self.direction = 'left';
}
})
.animation('.slide-animation', function() {
return {
beforeAddClass: function (element, className, done) {
var scope = element.scope();
// The line below logs undefined???
console.log(scope.direction);
}
};
});
I'm trying to access a value from the controller's scope but it's being returned as undefined which leads me to believe I've got the wrong scope. Is that correct? How can I get the controller's scope from within the animation?
I was able to get the scope I was looking for with help from Genti's comment.
angular
.module('slider')
.component('slider', {
templateUrl: '/mytemplate.html',
controller: function SliderController() {
var self = this;
self.direction = 'left';
}
})
.animation('.slide-animation', function() {
return {
beforeAddClass: function (element, className, done) {
var scope = element.scope().$ctrl;
console.log(scope.direction);
}
};
});

Angular 'this' doesn't assign value

I have the following code, and the problem is, that value fetched from json is not assigned to the vm variable
(function() {
angular
.module('app', [])
.controller('theController', theController);
function theController($http) {
var vm = this;
vm.message = [];
vm.message2 = [];
fetchJSON();
console.log(vm.message);
vm.message2 = vm.message;
function fetchJSON() {
$http
.get('http://beta.json-generator.com/api/json/get/4y2l2jq8l')
.success(function(data) {
vm.message = data;
});
}
}
})();
The value I assign to vm.message in fetchJson method doesn't appear in the main view, so when I set message2 = message it is still empty.
Live demo: http://codepen.io/matt23/pen/PZbNQa?editors=101
Have you tried setting the vm.message2 inside the fetchJSON function?
You are setting the vm.message2 right after fetchSON() hence the success message has not yet bene defined.
function fetchJSON() {
$http
.get('http://beta.json-generator.com/api/json/get/4y2l2jq8l')
.success(function(data) {
vm.message = data;
// HERE
});
}
or you can add another callback:
.complete(function() {
});

AngularJS Two way data binding

In my controller I have the following data:
(function() {
'use strict';
angular
.module('myappl.mymodule')
.controller('MyController', MyController);
MyController.$inject = ['$scope', 'myService'];
function MyController($scope, 'myService') {
$scope.vm = this;
var vm = this;
vm.myService = myService;
vm.userManagement = userManagement.data;
vm.userManagementSomeDataObjects = vm.userManagement.someDataObjects;
Somewhere in this controller I have a function which first gets data from backend and than invoke showModal:
function modalForUserInteraction() {
vm.myService.getData(parameters).success(function(data) {
vm.modalService.showModal(data, vm.userManagement, vm.userManagementSomeDataObjects);
}).error(function(data) {
console.log('error');
});
}
The modal- controller looks like this:
...
function showModalService($modal, $stateParams, otherService) {
var service = {
showModal: showModal
};
return service;
////////////
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
userManagement: function() {
return userManagement;
},
userManagementSomeDataObjects: function() {
return userManagementSomeDataObjects;
}
},
templateUrl: 'url/to.html'
});
return myModal;
and in the modal controller there is a method like this one:
function ModalController(userManagement, userManagementSomeDataObjects) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
userManagement = data;
userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}
If I do it like this:
userManagement = data; and userManagementSomeDataObjects = data.someDataObjects; than the new data is not set.
If I set each property separately of the objects than it works more often than not but somethimes it does not.
My question now would be what I can do in order to get it work.
Currently I do not have a $scope- variable in my modal and actually I don't know
if $scopeOfModal.$apply() would help and I also don't know how to get access from modal to MyController - $scope.
I would be glad for any hint in this direction.
Thanks a lot!
[EDIT]
Here is an image of my currently viewed (right) an on the left side the object, which should be shown after setting in modal- function.
[EDIT]
is there any posibility to pass parameters to this function in the modal controller:
this.previewArchivedSchedule = function(hereINeedParamerts) {
alert('archivedScheduleIntervalContainerId: ' + hereINeedParamerts);
};
This looks to me just like it may have nothing to do with angular, just some confusion with javascript variable references.
First you pass userManagement from showModalService.showModal to ModalController via resolve.
So now ModalController has a reference to the same object as showModalService.
However, in ModalController, you reassign the userManagement variable to point to data instead. So now the userManagement variable inside ModalController isn't pointing at the injected object anymore, because you've reassigned it. This has nothing to do with angular two-way data binding, it's just javascript variable assignment. You've lost your reference to the original object.
showModalService still has a reference to the instance that it sent in via resolve, it has no idea that you swapped the reference out in the ModalController.
I'd try sending over an object encapsulating the data you want to share to fix this problem.
function showModal(data, userManagement, userManagementSomeDataObjects) {
var myModal = $modal.open({
controller: ModalController,
controllerAs: 'vm',
windowClass: "modal fade in",
resolve: {
sharedData: function() {
return {
userManagement: userManagement,
userManagementSomeDataObjects: userManagementSomeDataObjects
}
},
templateUrl: 'url/to.html'
});
return myModal;
Then manipulate the properties on the shared object instead of overwriting references.
function ModalController(sharedData) {
var vm = this;
...
function doSomeActionAfterButtonClickAtModal() {
otherService.getDataFromBackend(params).success(function(data) {
sharedData.userManagement = data;
sharedData.userManagementSomeDataObjects = data.someDataObjects;
})error(function(data) {
console.log('error');
});
}

AngularJS : get object by id from factory

I have a factory to get an array with all my clientes from the database.
Then i need to filter this array by the person id and show only it's data in a single page.
I have a working code already, but it's only inside a controller and I want to use it with a factory and a directive since i'm no longer using ng-controller and this factory already make a call to other pages where I need to show client data.
This is what i tried to do with my factory:
app.js
app.factory('mainFactory', function($http){
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente")
.success( function(data) {
return data;
})
.error(function(data) {
});
};
var getDetCliente = function($scope,$routeParams) {
getCliente();
var mycli = data;
var myid = $routeParams.id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
};
return {
getCliente: getCliente,
getDetCliente: getDetCliente
}
});
app.directive('detClienteTable', function (mainFactory) {
return {
restrict: "A",
templateUrl: "content/view/det_cliente_table.html",
link: function($scope) {
mainFactory.getDetCliente().then(function(mycli) {
$scope.pagedCliente = mycli;
})
}
}
});
detClient.html
<p>{{pagedCliente.name}}</p>
<p>{{pagedCliente.tel}}</p>
<p>{{pagedCliente.email}}</p>
[...more code...]
The problem is, I'm not able to get any data to show in the page, and also, i have no errors in my console.
What may be wrong?
Keep in mind I'm learning AngularJS.
Basically you need to implement a promise chain as look into your code looks like you are carrying getCliente() promise to getDetCliente method. In that case you need to use .then function instead of using .success & .error which doesn't allow you to continue promise chain. There after from getDetCliente function you again need to use .then function that gets call when getCliente function gets resolved his promise. Your code will reformat it using form it and return mycli result.
Code
var getCliente = function() {
return $http.get("scripts/php/db.php?action=get_cliente")
.then( function(res) { //success callback
return res.data;
},function(err){ //error callback
console.log(err)
})
};
var getDetCliente = function(id) {
return getCliente().then(function(data){
var mycli = data;
var myid = id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
})
};
Edit
You shouldn't pass controller $scope to the service that will make tight coupling with you directive and controller, Also you want to pass id parameter of your route then you need to pass it from directive service call
link: function($scope) {
mainFactory.getDetCliente($routeParams.id).then(function(mycli) {
$scope.pagedCliente = mycli;
})
}
You are treating getCliente as a synchronous call in getDetCliente. Interestingly in your directive you understand that the getDetCliente is asynchronous. Change getCliente to this and treat it as an asynchronous call when you call it in getDetCliente:
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente");
};

Resources