I do understand why angular throws this error but i didn't find a solution that solves my case.
When angular encounter with ng-controller attribute, it tries to find injected controller named as the attribute value.
For example for my case:
Angular tries to find StreamHelperController in the injected controller list, when it doesn not find it, it throws
"StreamHelperController is not a function"
.
Known Solution: Injecting controller before loading view layer.
Controller:
var app = angular.module('myapp');
app.controller('StreamHelperController', ['$scope', function ($scope) {}]);
View:
<div ng-controller="StreamHelperController ">
But the problem occurs when i load view before controller class. And this is my case. So how can i manage and solve this problem ?
ENV:
Angularjs 1.5.8
javascript is synchronous.
controller is the bond|connection between model and view, without it no communication.
Thus, controller not defined at time will freeze the parsing.Try using promise or some other asynchronous trick or provide more info.
Related
I've came across with problem exposing api of directives in order to its interaction with controller.
There is a simplified jsfiddle to describe my structure.
The problem is that directives has templateUrl property (I replaced it with template in fiddle above), it leads to loading templates async (correct behavior according to this question).
As a result directive's controller is called after main controller therefore directive's api function doWork is undefined (even if you wrap it call with something like $timeout):
.controller('MainCtrl', function($scope, $timeout) {
$scope.api = {};
$scope.init = function() {
$timeout(function() {
$scope.api.doWork();
})
}
$scope.init()
})
There is an approach that comes to my mind - use event in link function of directive and subscribe $scope.api.doWork to that event. But I'm not happy about using events. Also it's not clear how to handle such case if there are some directives nested to each other with similar way of exposing api.
On the other hand it's possible to replace templateUrl with template - but it's also quite bad decision in case of complex layout in template.
Hence, I stuck a bit with the way to resolve such kind of problem. What's the best way to fix it out? Probably there is another technique to expose directive's api (I was inspired by Andrej Kaurin answer in that thread)?
If you are using Angular 1.5+ and are using components you could try $postLink or $onInit function link. Otherwise you just create onDoWork directive scope property and then apply some function from main controller that will be fired when doWork will actually happen(since I think that directive should control when to doWork, if not then maybe you should just create service ?)
I'm trying to add a chart to code created by others.
I understand a bit of angular, only...
I'm using angular-ui, so I don't have access to the HEAD tag where the simple Google instructions say to put the SCRIPT tags. I tried putting it later in the html, with other SCRIPT tags, but it kept saying "google" was undefined.
Finally, it seems to work if I put it inside the onload function:
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
drawCharts = function() {
var is = issue;
... create the chart using data inside $scope.issue
}
// google.charts.load( -- DOESN'T WORK HERE, google is undefined
$(window).load(function () {
// finally, in here, 'google' is defined
google.charts.load('current', {'packages': ['corechart', 'bar']});
google.charts.setOnLoadCallback(drawCharts);
}
PROBLEM: drawCharts() needs to access the $scope, but here there's no access to $scope, so my angular data isn't accessible and drawCharts() fails.
So somewhere I need to connect
google.charts.setOnLoadCallback() and $scope
How?
I found one question where the person had a:
$rootScope
.$on('$viewcontentLoaded', function(...)
But he has it in main.js, and I don't have a main.js.
I tried putting it in my controller for the page, but it doesn't define $rootScope. I tried adding $rootScope to the parameters passed to the first line of my controller:
people.controller("voterIssueCtrl", function ($rootScope, $scope, $http, $cookieStore, $window, ClIENT
And that took care of the undefined $rootScope, but the $viewContentLoaded function was never called (it just contained a console.log() message...)
Perhaps my app.js is his main.js.
But it references the controller by name, so it probably loads it, so it probably can't reference something the controller defines.
Help?
===============
I pulled a google chart directive from the web
added the tag in index.html to pull it in
and added the directive to my module definition.
(Of course I forgot the 2nd one and the code didn't complain...)
Nothing. But no complaint that Google wasn't known...
Putting in sample data helps.
In the grand style of js and angular, it doesn't complain if the data isn't exactly in the form it needs...
If only Angular2 + Typescript had been invented sooner...
In AngularJS, a Controller itself is an object defined by a Javascript constructor function and this constructor function is a callback to .controller method.
var myapp=angular.module('scopeExample', []);
myapp.controller('MyController',function MyController($scope){
$scope.name="john";
this.num=700;
});
In the above example, MyController is the constructor function which creates the Controller object with one property (num). I have three queries upon that:
Actually, what is the use of the Controller object in that case?
Does it have some more properties not visible and is it accessible from outside Angular?
How it is interconnected to its scope which in turn is another object?
I came upon the following queries because of the controller as syntax which creates a controller object that is a property of controller's scope and therefore easily accessible, e.g.
<div ng-app="scopeExample" ng-controller="MyController as ctrl">
<input id="box" ng-model="ctrl.num"> equals {{ ctrl.num }}
</div>
<script>
angular.module('scopeExample', [])
.controller('MyController', [function () {
this.num=12;
}]);
</script>
var x=angular.element('#box').scope().ctrl; //x is the controller object itself
1.a. What is the use of the Controller object in that case?
There is nothing special about this example, angular is an MVC framework(or any other buzz word you wish to use that describes almost the same thing), the controller's responsibility is to response to view events, update the model accordingly and execute business logic tasks (you can choose where to actually implement the logic, wheres in the controller itself or use services).
Of course that in this example the controller is pretty useless, because you have no logic, and only 2 proprieties.
1.b. Specking of ctrl-as syntax, in your example you injected 'scope' into the controller and added property ($scope.name), when you're using controller as it is recommended for you to avoid using scope unless you are obligated to do so. (e.g. $watch, parent...)
2.a. Does it have some more properties not visible?
No it doesn't have any invisible properties, you can check it easily by your self with the following code:
.controller('MyController', function () {
window.DoesThisControllerHaveInvisibleProps = this;
});
2.b. is it accessible from outside Angular?
I'm not sure that I fully understood what you've meant with "outside Angular", if so here is an example that the controller obj can be accessible from "outside":
class MyController {
static foo() {
console.log('hello!');
}
}
myapp.controller('MyController', MyController);
// maybe somewhere else in that module
MyController.foo();
3.How it is interconnected to its scope which in turn is another object?
As you said, when using controller as syntax angular is initializing the controller and put it on the $scope so it will be accessible in the template.
$scope is just an unnecessary glow and you should avoid using it. my way of seeing it is like it was angular implantation details, when migrating to ng-2 you will see that there is no more scope.
If you're interested in more detailed info about how exactly $scope and controllers in angular are working I suggest you have a look at
I am following this best practice style guide for angular development and I can't seem to get this one part working cleanly.
Best Practice Style Guide:
https://github.com/johnpapa/angular-styleguide
It recommends to declare controllers by the following:
angular
.module('app')
.controller('feedController', FeedController);
function feedController(){
var vm = this; //My $scope variable
}
The problem is I am trying to use $on to add an event listener but it gives me an undefined error for vm.$on. A temporary solution I found was to inject $scope into the controller by the following:
FeedController.$inject = ['apiservice', '$scope'];
and call $scope.$on which works but I feel its inconsistent now. Is there a way to still use vm in a clean way.
You can see the full controller here https://github.com/bliitzkrieg/TrailerFeed/blob/master/app/components/feed/FeedController.js
this/vm refers to the instance of the controller, not the scope that is associated with that controller.
Events only exist on scopes, so to use the event system it is correct to inject $scope to get a reference to the controller's scope where $on is available.
angular
.module('app')
.controller('feedController', FeedController);
function feedController($scope){
var vm = this; // the instance of the controller
$scope.on(...) // the controller's scope
}
Take a look at the following plunker: http://plnkr.co/edit/uEbEdNifuBReENxzhb6H?p=preview
The expected behaviour for the routeProvider resolve (to my understanding) is for the resolved object to be injected into the controller. However Angularjs throws an "Unknown provider" error.
I have seen suggestions to inject the original service, but that will result in a deferred object, which renders the routeProvider resolve rather useless.
You should specify the controller with the root provider - not in the template (or the controller will be used regardless of the path, which may result in the dependencies not resolving):
$routeProvider.when('/',{controller:'TestCtrl', ...
I've updated your plunker: http://plnkr.co/edit/mSb58e8cGDNYU27xSizk?p=preview