Angular - The easiest way to populate $scope - angularjs

I am currently populating $scope arrays like this:
$scope.categories = [];
$scope.getCategories = function() {
ItemService.getCategories().success(function(categories) {
$scope.categories = categories;
});
}
$scope.getCategories();
Is this really the easiest way to do it?

Do you actually need a getCategories function ? If this is the only place it's called, then you can just remove and leave the service call in directly.
Otherwise your code is as short as it gets for using promises I think, if you're using v1.2. Before 1.2, angular did have automatic promise unwrapping. That is, instead of
//$scope.getCategories = function() {
ItemService.getCategories().success(function(categories) {
$scope.categories = categories;
});
//}
You could do this instead, which seems much more elegant:
//$scope.getCategories = function() {
$scope.categories = ItemService.getCategories();
//}
Issues with this mean it's recently been removed though - https://github.com/angular/angular.js/issues/4158 . I think it's possible an alternative will be added in the future if there's a good way to do it though.

You could simply use the resolve property of your router (assuming you're using a router) to inject the results from ItemService.getCategories() into your controller. resolve properties block the route from completing until they're fully resolved--it unwraps promises automatically. The code would look something like:
angular.module('MyModule').config(function ($routeProvider) {
$routeProvider.when('/mypage', {
templateUrl: '/mypage.html',
controller: 'MyController',
resolve: {
categories: function (ItemService) { return ItemService.getCategories(); }
}
});
});
angular.module('MyModule').controller('MyController', function (categories, $scope) {
$scope.categories = categories;
});

Related

Does UI-Router Resolve Work with Angular 1.5 Components?

So, UI-router resolves were a thing of beauty in angular 1:
$stateProvider.state('myState', {
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
controller: function($scope, myVar){
$scope.myVar= myVarFromResolve;
if(true){console.log($scope.myVar}; //logs 'test'
}
How do I do the same with an Angular 1.5. component (example below)?
export default function FooComponent() {
let fooComponent = {
restrict: 'E',
templateUrl: 'foo.html',
controller: controller,
controllerAs: 'foo',
bindToController: true,
};
return landingComponent;
}
And the resolve...
.state('fooState', {
parent: 'someParent',
url: '/fooUrl',
template: '<foo></foo>',
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
I read a guide on how to do this, but I don't really understand it. Seems like the functionality isn't fully in place and that's a hack.
Looks like the answer is "Yes, kinda"...
I was given a work-around to this problem by a team member that works for angular-ui-router 0.2.18 (use npm list angular-ui-router --depth=0 to check your version quickly). It does work while using ES6 classes.
The work around is based on the fact that there is no (easy/known) way to get the variable passed into the controller, like you would in Angular 1 before component architecture introduced in 1.5. Instead, we create a service method that creates a promise (or returns an http call). Then you simply put the promise in the resolve. The promise method WILL return before the controller loads. So while you do not have a easy to use var, you have the data you need in a service.
resolve: {
preloadSomeVariableIntoAService: function (MyExampleService) {
return MyExampleService.presetSomeVariable();
}
//Note that 'preloadSomeVariableIntoAService' won't be used anywhere in our case
}
The service could be something like this:
export default class MyExampleService {
constructor( $q ) {
this.q = $q;
this.myVariableFromResolve = undefined;
}
presetStreamStatus(){
var deferred = this.q.defer();
return $http.get(someUrl).then(response) => {
this.myVariableFromResolve = response.data;
deferred.resolve(events);
});
return deferred;
};
};
You will then be able to get myVariableFromResolve from the service. I am not sure you have to use $q deferred, but it seems to be necessary since I am not simply returning the call but also setting the variable. Not sure. Hope this helps someone.

Can't get this.var to bind correct data in AngularJs

Two Important Notes:
1. My goal is to AVOID using $scope in this case since it's my understanding that impedes the new "controller as" syntax.
2. My problem is likely a variable scope issue and so perhaps just clarifying the proper JS way might solve the problem.
Nevermind the exports, I'm working with browserify in my workflow.
I have this working code:
exports.IntroCtrl = function($scope, $http) {
$scope.introData = [];
$http.get('data/intro.json')
.success(function(res){
$scope.introData = res;
});
};
That ideally I'd like to work as something like this, for the sake of using the "controller as" syntax.
exports.IntroCtrl = function($http) {
this.introData = [];
$http.get('data/intro.json')
.success(function(res){
introData = res;
});
};
The problem is that the $http service seems to be executing before my initial this.introData declaration since I get a variable not defined error.
If tell this.introData = $http.get… then it returns an array of 5 objects that I can't access and intro.json only contains 4.
Thanks for any guidance/help.
First of all create a service for the http call. It is very convenient way to get the callback in the controller and then assign your controller as variables. Here is the factory for you:
Factory
app.factory('getDataService',function ($http) {
return {
getData:function(callback){
$http.get('data/intro.json')
.success(callback)
});
}
}
});
In your controller you get inject the getDataService and bind the data like this:
Controller:
app.controller('testController',['getDataService',function(testDataService){
this.introData = [];
testDataService.getData(function(data){
this.introData = data;
});
}]);
Here you need to bind the introData of the controller function.
You have to remember this variable that reference the controller instance, and use it later in the success callback like this:
exports.IntroCtrl = function($http) {
this.introData = [];
var ctrl = this; // remember 'this', the controller instance, to use in the success callback below
$http.get('data/intro.json')
.success(function (res) {
ctrl.introData = res;
});
};
Hope this helps.

AngularJS: Resolving not in RouteProvider but in Controller?

I saw some sample code here Delaying AngularJS route change until model loaded to prevent flicker
And straight away I though this was the right way to go, I need to have my controller LOAD only when a resolve is finished loading, normally most of the examples around tell you to put the code under resolve in the routeprovder as an inline function, but this sounds wrong. The controller needs it so why not have the controller implement the function to resolve. This sounded just what I was looking for ie. This seems to use the prototype pattern ??
function PhoneListCtrl($scope, phones) {
$scope.phones = phones;
$scope.orderProp = 'age';
}
PhoneListCtrl.resolve = {
phones: function(Phone, $q) {
// see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
var deferred = $q.defer();
Phone.query(function(successData) {
deferred.resolve(successData);
}, function(errorData) {
deferred.reject(); // you could optionally pass error data here
});
return deferred.promise;
}
}
Problem is I have my controller like so
'use strict';
angular.module('TestApp')
.controller('ItemsCtrl', function ($scope) {
});
So how do I apply a new function on the controller when my controller is declared inside a module ?
What I really need is something like
TestCtrl.resolve = {
items: function( $q) {
..........
return deferred.promise;
}
}
This then would allow me to have in my routeprovider..
when('/items', {
templateUrl: 'views/items.html',
controller: 'TestCtrl',
resolve: 'TestCtrl.resolve'}). // Need to use ' around resolve?
But I am confused how I would get this to work ?
I would really love any feedback, I am at a loss.
Not possible to define like 'TestCtrl.resolve' if you want to use resolve with .controller syntax then you have to define inline within route provider . The advantage of inline resolve in routeprovider is that you can reuse controller easily but using same controller and changing the logic in resolve function
You can also use a service:
resolve: {data : function(service) {
return service.getData();
}}

asynchronous loading controller's code

how do you bootstrap a controller that loaded asynchronously via require.js?
if I have something like that:
$routeProvider.when('/',
{
templateUrl:'view1.html',
controller:'ctrl',
resolve:{
load:function($q){
var dfrd = $q.defer();
require(['view1-script'],function(){
dfrd.resolve();
})
return dfrd.promise;
}
}
})
why angular still won't find the controller? I am resolving the route after it loads the script
check out this plunkr
try calling $controllerProvider.register to create your controller. I would also call $apply() on the $rootScope after resolving the deferred because without it, the view does not seem to appear:
load: function($q, $rootScope){
var dfrd = $q.defer();
require(['view1'],function(){
dfrd.resolve();
$rootScope.$apply();
})
return dfrd.promise;
}
http://plnkr.co/edit/fe2Q3BhxPYnPmeiOORHP
in addition, here is a good post: http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx
It's been 3 years, but just in case anyone still interested, a few months ago I wrote a post about a similar technique to do it.
The most important part is that second parameter of the method $routeProvider.when(route, ctrl) method can handle promises, so you can simply emulate it:
function controllerFactory(ctrl) {
return {
then: function (done) {
var self = this;
require(['./controller/' + ctrl], function (ctrl) {
self.controller = ctrl;
self.resolve = ctrl.resolve;
self.templateUrl = ctrl.templateUrl;
done();
});
}
};
}
And you can end up writing your route definition like this:
$routeProvider.
when('/some/route', controllerFactory('some/route')).
when('/other/route', controllerFactory('other/route'))

"Computed Properties" in AngularJS

I recently chose AngularJS over ember.js for a project I am working on, and have been very pleased with it so far. One nice thing about ember is its built in support for "computed properties" with automatic data binding. I have been able to accomplish something similar in Angular with the code below, but am not sure if it is the best way to do so.
// Controller
angular.module('mathSkills.controller', [])
.controller('nav', ['navigation', '$scope', function (navigation, $scope) {
// "Computed Property"
$scope.$watch(navigation.getCurrentPageNumber, function(newVal, oldVal, scope) {
scope.currentPageNumber = newVal;
});
$scope.totalPages = navigation.getTotalPages();
}]);
// 'navigation' service
angular.module('mathSkills.services', [])
.factory('navigation', function() {
var currentPage = 0,
pages = [];
return {
getCurrentPageNumber: function() {
return currentPage + 1;
},
getTotalPages: function() {
return pages.length;
}
};
});
// HTML template
<div id=problemPager ng-controller=nav>
Problem {{currentPageNumber}} of {{totalPages}}
</div>
I would like for the UI to update whenever the currentPage of the navigation service changes, which the above code accomplishes.
Is this the best way to solve this problem in AngularJS? Are there (significant) performance implications for using $watch() like this? Would something like this be better accomplished using custom events and $emit() or $broadcast()?
While your self-answer works, it doesn't actually implement computed properties. You simply solved the problem by calling a function in your binding to force the binding to be greedy. I'm not 100% sure it'd work in all cases, and the greediness might have unwanted performance characteristics in some situations.
I worked up a solution for a computed properties w/dependencies similar to what EmberJS has:
function ngCreateComputedProperty($scope, computedPropertyName, dependentProperties, f) {
function assignF($scope) {
var computedVal = f($scope);
$scope[computedPropertyName] = computedVal;
};
$scope.$watchCollection(dependentProperties, function(newVal, oldVal, $scope) {
assignF($scope);
});
assignF($scope);
};
// in some controller...
ngCreateComputedProperty($scope, 'aSquared', 'a', function($scope) { return $scope.a * $scope.a } );
ngCreateComputedProperty($scope, 'aPlusB', '[a,b]', function($scope) { return $scope.a + $scope.b } );
See it live: http://jsfiddle.net/apinstein/2kR2c/3/
It's worth noting that $scope.$watchCollection is efficient -- I verified that "assignF()" is called only once even if multiple dependencies are changed simultaneously (same $apply cycle).
"
I think I found the answer. This example can be dramatically simplified to:
// Controller
angular.module('mathSkills.controller', [])
.controller('nav', ['navigation', '$scope', function (navigation, $scope) {
// Property is now just a reference to the service's function.
$scope.currentPageNumber = navigation.getCurrentPageNumber;
$scope.totalPages = navigation.getTotalPages();
}]);
// HTML template
// Notice the first binding is to the result of a function call.
<div id=problemPager ng-controller=nav>
Problem {{currentPageNumber()}} of {{totalPages}}
</div>
Note that with ECMAScript 5 you can now also do something like this:
// Controller
angular.module('mathSkills.controller', [])
.controller('nav', function(navigation, $scope) {
$scope.totalPages = navigation.getTotalPages();
Object.defineProperty($scope, 'currentPageNumber', {
get: function() {
return navigation.getCurrentPageNumber();
}
});
]);
//HTML
<div ng-controller="nav">Problem {{currentPageNumber}} of {{totalPages}}</div>

Resources