I am creating a messaging service that needs to do the following 1.) Load a messsage from our messages service, get the recipient's ids, and then load the recipients' info from a users service. I've tried both using the messages service callback, and also creating a watcher on the message object, without much success. The service works, but it doesn't assign the result to the $scope correctly. Here's the controller. All of the services are working correctly:
function MessageCtrl ($scope, $http, $location, $routeParams, Messages, Members) {
if ($routeParams.mid) { // Checks for the id of the message in the route. Otherwise, creates a new message.
$scope.mid = $routeParams.mid;
$scope.message = Messages.messages({mid: $scope.mid}).query();
$scope.$watch("message", function (newVal, oldVal, scope) {
if (newVal.data) {
$scope.recipients = Members.members({uids: newVal.data[0].uids}).query();
}
}, true);
} else {
$scope.create = true;
}
// Events
$scope.save = function () { };
$scope.preview = function () { };
$scope.send = function () { };
}
The correct way to use query is to perform the action in the callback that is passed in query function. In other words$scope.message should be assigned in the callback. Also you don't need a $watch. You can call the other service within the callback directly. But to keep it clean please use deferred
http://docs.angularjs.org/api/ngResource.$resource
http://docs.angularjs.org/api/ng.$q
Related
I am using the following snippet to get the data from server to load the initial content of the page.
(function () {
'use strict';
angular
.module('app')
.controller('MyController', MyController);
MyController.$inject = ['UserService', '$rootScope', '$scope', '$cookieStore', 'AuthenticationService'];
function MyController(UserService, $rootScope, $scope, $cookieStore, AuthenticationService) {
/*Discussion Board related Functions*/
angular.element(document).ready(function () {
// Get Current User
$rootScope.globals = $cookieStore.get('globals') || {};
$scope.currentUser = AuthenticationService.GetAuthData($rootScope.globals.currentUser.authdata);
$.getJSON(
"http://localhost/getSomeServerData.php",
{ userName: $scope.currentUser },
$scope.getSomeDataResponse
);
});
$scope.getSomeDataResponse = function (jason) {
var servRet = jason;
alert("On Load Called-2");
};
}
})();
However, the response function $scope.getSomeDataResponse is not getting called.
Please let me know what is wrong with this approach.
However, the response function $scope.getSomeDataResponse is not
getting called.
Please let me know what is wrong with this approach.
The issue is, you are referencing getSomeDataResponse before it is being declared. Also, you are using jQuery's getJSON() to get the data from server using HTTP GET request in your angularJS code.
This is particularly not a recommended practise. If you include jQuery in your page, AngularJS will use jQuery instead of jqLite when wrapping elements within your directives, otherwise it'll delegable to jqLite(which might not work in all cases). To be at a safer side, use angular's $http.get() service instead of $.getJSON()
$http.get("http://localhost/getSomeServerData.php",{ userName: $scope.currentUser})
.then(function(jason){
//success handler function
var servRet = jason;
alert("On Load Called-2");
},function(err){
//error handler function
alert(err);
})
Ofcourse you'd need to inject $http service in your controller to make it work. Have a look at this thread for other possible alternatives.
Cheers!
First, you have to define your result function before using.
Second, you have to use $.getJSON correctly. So this worked for me after fixing
$scope.getSomeDataResponse = function (jason) {
var servRet = jason;
alert("On Load Called-2");
};
/*Discussion Board related Functions*/
angular.element(document).ready(function () {
// Get Current User
$rootScope.globals = $cookieStore.get('globals') || {};
$scope.currentUser = AuthenticationService.GetAuthData($rootScope.globals.currentUser.authdata);
$.getJSON(
"http://localhost/getSomeServerData.php",
{ userName: $scope.currentUser }
).always($scope.getSomeDataResponse);
the callback method was different in jQuery. use fail and done callbacks by looking at the jQuery documentation
http://api.jquery.com/jquery.getjson/
I am trying to have a $watch on a scope so I Can listen for changes, however, I do not want the watch to start listening until the data has come in and populated. I am using a $q factory and then to populate the items, then I want the watch to start listening after everything is populated. I can't seem to get down how to control these order of events.
SO I have in my controller -
//call to the $q factoy to execut all http calls
getDefaults.resource.then(function(data){
//fill in scopes with data
$scope.allAccounts = data[0].data.accounts;
//THEN watch the scope for changes
$scope.$watch('selectedResources', function (newValue) {
//do action on change here
});
}
SO I'm wondering if there is any way to control these order of events in angular. Thanks for reading!
You can create a service for your xhr calls:
.factory('xhrService', function ($http) {
return {
getData: function () {
return $http.get('/your/url').then(cb);
}
};
function cb(data) {
/// process data if you need
return data.accounts;
}
});
after that you can use it like this inside your controller:
.controller('myController', function (xhrService) {
$scope.allAccounts = [];
xhrService.getData()
.then(function (accounts) {
$scope.allAccounts = accounts;
return $scope.allAccounts;
})
.then(function () {
$scope.$watch('allAccounts', function (newValue) {
// do something
}
});
});
I think this is a good way to structure your code because you can reuse your service and you can add (or not) any watch you need (inside any controller)
And the most important, from the docs: https://docs.angularjs.org/api/ng/service/$q - "$q.then method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback" - this is why each then callback needs a return statement.
I want to factor out the angularjs $http success callback function so that instead of having two (or N) anonymous callback functions I have one named callback function.
Here are the two controllers:
function CreateCurriculumCtrl($scope, $http, $location, select2Options){
$scope.curriculumInfo = {};
$scope.curriculumInfo.statusOK = true;
$scope.select2Options = select2Options;
$scope.saveCurriculum = function(){
$http.post('bignibou/curriculum/new', $scope.curriculumInfo).success(function(curriculumInfo) {
if(curriculumInfo.statusOK == true){
$scope.curriculumInfo.statusOK=true;
$location.path('/view/'+curriculumInfo.curriculum.id);
}
else{
$scope.curriculumInfo.statusOK = false;
$scope.curriculumInfo.errors = curriculumInfo.errors;
}
});
};
}
function EditCurriculumCtrl($scope, $http, $location, select2Options, $routeParams){
$scope.curriculumInfo = {};
$scope.curriculumInfo.statusOK = true;
$scope.select2Options = select2Options;
$scope.id = $routeParams.id;
$http.get('/bignibou/utils/findCurriculumById.json',{params: {id: $routeParams.id}}).success(
function(curriculum){
$scope.curriculumInfo.curriculum = curriculum;
});
$scope.editCurriculum = function(){
$http.post('bignibou/curriculum/edit/'+$routeParams.id, $scope.curriculumInfo)
.success(function(curriculumInfo) {
if(curriculumInfo.statusOK == true){
$scope.curriculumInfo.statusOK=true;
$location.path('/view/'+curriculumInfo.curriculum.id);
}
else{
$scope.curriculumInfo.statusOK = false;
$scope.curriculumInfo.errors = curriculumInfo.errors;
}
});
};
}
I am not sure how to do that because what will become the named callback function has a couple of dependencies (i.e. $scope and $location).
If I extract the function (named callback) out of the angularjs controllers, then the named
callback has no longer access to its dependencies.
Can anyone please help with factoring out the success callback function and making sure the dependencies are satisfied?
Just pass them as arguments.
First, $http.post expects the callback to accept one argument. So we write the function that post expects:
function (curriculumInfo) {
// .....
}
But the body of the function needs access to $scope and $location. So we write a function that accepts those and return the function that post expects:
function (scope,location) {
return function (curriculumInfo) {
// ... use scope and location in here
}
}
Now we can name the function appropriately. Lets see, it's handling the response to new curriculum so I'd call it either new_curriculum_callback or new_curriculum_callback or handle_new_curriculum to indicate that it's a callback:
function handle_new_curriculum (scope,location) {
return function (curriculumInfo) {
}
}
Now you can call it to return the callback function to post:
$http
.post('bignibou/curriculum/new',$scope.curriculumInfo)
.success(handle_new_curriculum($scope,$location));
Create a Curriculum service and refactor every call to the backend into it.
Your controllers shouldn't care on how to getting the data. Inject the Curriculum service into your controllers. Your controllers then simply call functions on the Curriculum service and get the data.
angular.module('resources.curriculum', ['..'])
.factory('Curriculum', function ($http, $q) {
return {
create: function(dataToSave) {
var deferred = $q.defer()
http.get('...').success(function(data) {
deferred.resolve(data)
}).error(function(err) {
deferred.reject(err)
})
return deferred.promise
}
}
})
.controller('SomeCtrl', function($scope, Curriculum) {
$scope.someValue = Curriculum.create($scope.someDataToSave)
})
You would probably want to create angularjs service and call particular request on click of some button. This way you call requests in a synchronous way.
I have got a page layout with two controllers at the same time, one holds the data displayed as kind of side navigation, based on data stored the browsers local storage and at least one other controller, which is bind to a route and view.
I created this little wire frame graphic below which show the page layout:
The second controller is used for manipulating the local stored data and performs actions like adding a new item or deleting an existing one. My goal is to keep the data in sync, if an item got added or deleted by the 'ManageListCrtl' the side navigation using the 'ListCtrl' should get updated immediately.
I archived this by separating the local storage logic into a service which performs a broadcast when the list got manipulated, each controller listens on this event and updates the scope's list.
This works fine, but I'm not sure if there is the best practice.
Here is a stripped down version of my code containing just the necessary parts:
angular.module('StoredListExample', ['LocalObjectStorage'])
.service('StoredList', function ($rootScope, LocalObjectStorage) {
this.add = function (url, title) {
// local storage add logic
$rootScope.$broadcast('StoredList', list);
};
this.delete = function (id) {
// local storage delete logic
$rootScope.$broadcast('StoredList', list);
};
this.get = function () {
// local storage get logic
};
});
angular.module('StoredListExample')
.controller('ListCtrl', function ($scope, StoredList) {
$scope.list = StoredList.get();
$scope.$on('StoredList', function (event, data) {
$scope.list = data;
});
});
angular.module('StoredListExample')
.controller('ManageListCtrl', function ($scope, $location, StoredList) {
$scope.list = StoredList.get();
$scope.add = function () {
StoredList.add($scope.url, $scope.title);
$location.path('/manage');
};
$scope.delete = function (id) {
StoredList.delete(id);
};
$scope.$on('StoredList', function (event, data) {
$scope.list = data;
});
});
I don't see anything wrong with doing it this way. Your other option of course is to just inject $rootScope into both controllers and pub/sub between them with a $rootScope.$broadcast and a $rootScope.$on.
angular.module('StoredListExample')
.controller('ListCtrl', function ($scope, $rootScope) {
$scope.list = [];
$rootScope.$on('StoredList', function (event, data) {
$scope.list = data;
});
});
angular.module('StoredListExample')
.controller('ManageListCtrl', function ($scope, $rootScope, $location) {
$scope.list = [];
$scope.add = function () {
//psuedo, not sure if this is what you'd be doing...
$scope.list.push({ url: $scope.url, title: $scope.title});
$scope.storedListUpdated();
$location.path('/manage');
};
$scope.delete = function (id) {
var index = $scope.list.indexOf(id);
$scope.list.splice(index, 1);
$scope.storedListUpdated();
};
$scope.storedListUpdated = function () {
$rootScope.$broadcast('StoredList', $scope.list);
};
});
Additionally, you can achieve this in a messy but fun way by having a common parent controller. Whereby you would $emit up a 'StoredListUpdated' event from 'ManageListCtrl' to the parent controller, then the parent controller would $broadcast the same event down to the 'ListCtrl'. This would allow you to avoid using $rootScope, but it would get pretty messy in terms of readability as you add more events in this way.
It is always a better practice to use a common service that is a singleton for sharing the data between the 2 controllers - just make sure you use only references and not creating a local object in one of the controllers that should actually be in the service
I'm using BreezeJS with AngularJS but I'm having a difficult time understanding how to get Promises to work with $scope. Whenever I try to submit my form its not showing the validation errors until I click it for a 2nd time. I realize I could call $scope.$apply() but I read its not best practice? Here is my code:
app.controller("MainController", ["$scope", "$q", "datacontext", function ($scope, $q, datacontext) {
datacontext.manager.fetchMetadata();
$scope.errors = [];
$scope.addDamp = function () {
var item = datacontext.manager.createEntity("Damp", {
name: $scope.newDamp
});
var tes = datacontext.manager.saveChanges()
.then(function () {
alert("yay");
})
.fail(function (error, a, b, c) {
var arr = [];
error.entitiesWithErrors.map(function (entity) {
entity.entityAspect.getValidationErrors().map(function (validationError) {
arr.push(validationError.errorMessage);
});
});
$scope.errors = arr;
datacontext.manager.rejectChanges();
});
};
}]);
What is the best way to go about handling scope changes that come from inside of a Promise?
Yes, you're going to need $scope.apply here, because the promise isn't coming out of a core Angular call (such as $http, which would have handled the .apply() itself behind the scenes). In fact, the Breeze/Angular example on the BreezeJS page (http://www.breezejs.com/samples/todo-angular) includes a $scope.apply() after its data retrieval:
datacontext.getAllTodos()
.then(success)
.fail(failed)
.fin(refreshView);
function refreshView() {
$scope.$apply();
}
It's a bad practice to toss $scope.apply() about where you don't need it. But when you're handling promises created outside of Angular itself, it's going to come up.