Angular apply scope changes from global function cause unknown provider error - angularjs

jsfiddle: http://jsfiddle.net/3gd8a/1/
(function () {
var app = angular.module("index", []);
app.run(function ($log, $controller) {
$log_service = $log;
$controller_service = $controller;
});
app.controller("AlertsController", function () {
this.alerts = [ "first alert" ];
this.innerFunction = function() {
this.alerts.push("inner alert");
$log_service.debug(alerts.alerts);
};
});
})();
function outerFunction() {
var alerts = $controller_service("AlertsController");
alerts.alerts.push("outer alert");
$log_service.debug(alerts.alerts);
};
In this example I changed the property of controller instance in the outer js function,
now I don't known how can I apply it just like use $scope.apply().
I already read angular-tips watch-how-the-apply-runs-a-digest,
$scope has the $apply function, but controller instance didn't have one.
You may want to ask why I use controller instance not $scope, because I learn angular js from codeschool so I want use the same way I learned if possible.
And you may want to ask why not use ng-click, I known use ng-click will work but I want figure out how angular js watch properties of controller instance and how to apply the changes of them manually.
Edit:
I figured out controller instance is just a property named alerts under $scope when I use it by ng-controller="AlertsController as alerts".
Now I had another problem, If I access $scope from outerFunction will cause an error.
The first time is Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope
http://errors.angularjs.org/1.2.1/$injector/unpr?p0=%24scopeProvider%20%3C-%20%24scope angular.js:78
And the second time is
Error: [$injector:cdep] Circular dependency found:
http://errors.angularjs.org/1.2.1/$injector/cdep?p0=

Since nobody answer, I leave my answer here.
Controller instance is a object under $scope after initiazlied, in the example I alias AlertsController to alerts so it named alerts under $scope.
Then $scope.alerts and this are equivalent.
In the first comment (http://jsfiddle.net/3gd8a/5) I tried put $scope to global then access it from outerFunction but failed, I still need figure out why.
In the second comment (http://jsfiddle.net/3gd8a/6) I found $scope can get from the element inside of controller, and it's can be access normally.
So the way to update and apply property of controller instance from global function is
add id to any element inside controller
use angular.element find that element then call .scope() to get the scope.
use scope.{alias of controller}.{property name} to update property, then call $scope.apply to apply.

Related

Call Angular directive controller method from another controller

I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;

Unit testing controllers in angularJS

I am having a hard time understanding unit tests in angularJs. I have just started with unit tests and the syntax seems weird to me. Below is the code for testing a controller :
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
beforeEach(module('phonecatApp'));
it('should create "phones" model with 3 phones',
inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', {$scope:scope});
expect(scope.phones.length).toBe(3);
}));
});
});
What I can understand from this syntax is that before each it block phonecatApp is initialised and that $controller service is used to get an instance of PhoneListCtrl controller.
However I am not able to understand the scope thing here. Can someone elaborate on whats behind getting the scope of the controller on this line.
ctrl = $controller('PhoneListCtrl', {$scope:scope});
Normally, at runtime, angular creates a scope and injects it into the controller function to instantiate it.
In your unit test, you instead want to create the scope by yourself and pass it to the controller function, in order to be able to see if it indeed has 3 phones after construction (for example).
You might also want to inject mock services instead of the real ones into your controller. That's what the array of objects allows in
$controller('PhoneListCtrl', {$scope:scope});
It tells angular: create an instance of the controller named 'PhoneListCtrl', but instead of creating and injecting a scope, use the one I give you.
If your controller depended on a service 'phoneService', and you wanted to inject a mock phoneService, you could do
var mockPhoneService = ...;
$controller('PhoneListCtrl', {
$scope: scope,
phoneService: mockPhoneService
});
It's not necessary to inject the scope, you can directly use the instance of controller to call the controller's functions and objects.In your example you can use like below, this will give the same result set as yours
describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
beforeEach(module('phonecatApp'));
it('should create "phones" model with 3 phones',
inject(function($controller) {
var ctrl = $controller('PhoneListCtrl');
expect(ctrl.phones.length).toBe(3);
}));
});
});
and for you information each time the controller is instantiated it is bound to a $scope variable which is derived from $rootScope (i.e: child of rootscope). So you need to pass the $scope to grab the instance of controller and I am doing same thing in above example.

$location.url and $scope.$apply = Cannot read property '$$nextSibling' of null

We have a service handler that changes the location. The location does not change until something else triggers a digest. So, I used $apply, but getting errors from that.
$scope.myFunction = function () {
$scope.$apply(function () {
$location.url(path);
});
};
Above gives this error:
Uncaught TypeError: Cannot read property '$$nextSibling' of null
From what I found, it seems like the $scope is getting destroyed during a phase cycle...But how can you use apply() on a location change without this error?
EDIT:
Testing to see if this scenario is related to our current app and discovered that reloading the route works oddly enough, which seems like a hack?
$scope.myFunction = function () {
$location.url(path);
$route.reload()
};
Inject a $rootScope and use it instead of the $scope:
$location.url(path);
$rootScope.$apply();
The $rootScope will be available for the lifetime of the app.

Angularjs: ng-click has no method '$apply'

I append DOM element to body. Wrote the code in factory.
var templateElement = angular.element('<div class="popup modal-body"><div class="button-cancel" type="button" ng-click="closePopup()"></div>'+content+'</div>');
var scope = {};
scope.closePopup = function(){
var popup = angular.element(document.querySelector('.popup'));
popup.remove();
}
var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
body.append(clonedElement);
});
everything works except the ng-click. I got this error when I click the div:
Uncaught TypeError: Object #<Object> has no method '$apply'
What did I do wrong? Thanks
According to the docs, the function returned by $compile() takes a Scope object as its first argument. You supply a normal JS object (which does not have an $apply method of course).
If you want to create a new scope, you can inject the $rootScope (through Dependency Injection) and use its $new() method:
app.factory('myFactory', function($rootScope) {
var scope = $rootScope.$new();
...
});
It seems a little bizarre to create an new scope inside a factory though, so providing more details on what you are ultimately trying to achieve might help someone suggest a better approach.

AngularJS with SignalR

I am playing with Angular and SignalR, I have tried to create a service which will act as a manager.
dashboard.factory('notificationsHub', function ($scope) {
var connection;
var proxy;
var initialize = function () {
connection = $.hubConnection();
proxy = connection.createHubProxy('notification');
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$scope.$emit('numberOfIncidents', numOfIncident);
});
connection.start()
.done(function() {
console.log('Connected');
})
.fail(function() { console.log('Failed to connect Connected'); });
};
return {
initialize: initialize
};
});
however I get the error Error: Unknown provider: $scopeProvider <- $scope <- notificationsHub.
How can I use pubsub to pass all the notifications to the controllers? jQuery maybe?
$scope does not exist in this context as that's something injected when a controller is created and a new child scope is made. However, $rootScope is available at the time you need.
Also, be aware $emit() goes upward and your controller scopes wont see it. You would either need to switch to $broadcast() so the event goes downwards or inject $rootScope as well to the controllers you want to be able to subscribe to 'numberOfIncidents'
Check out the angular docs and a useful wiki on scopes.
Here is a great example showing how to wrap the proxy in a service and use $rootScope for event pub/sub.
http://sravi-kiran.blogspot.com/2013/09/ABetterWayOfUsingAspNetSignalRWithAngularJs.html
As already noted in johlrich's answer, $scope is not avaliable inside proxy.on. However, just switching to $rootScope will most likely not work. The reason for this is because the event handlers regisrered with proxy.on are called by code outside the angular framework, and thus angular will not detect changes to variables. The same applies to $rootScope.$on event handlers that are triggered by events broadcasted from the SignalR event handlers. See https://docs.angularjs.org/error/$rootScope/inprog for some more details.
Thus you want to call $rootScope.$apply() from the SignalR event handler, either explicitly
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$scope.$apply(function () {
$rootScope.$emit('numberOfIncidents', numOfIncident);
});
});
or possibly implicitly through $timeout
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$timeout(function () {
$rootScope.$emit('numberOfIncidents', numOfIncident);
}, 0);
});
I tried to use $apply() after changing value, i tried to use $apply(functuin() {value = 3}), and also i tried to use $emit and $broadcast for changing value and it doesn't help.
But i found solution we need in html after in controller you can use
var scope2 = angular.element("#test").scope();
scope2.point.WarmData.push(result);
$scope.$apply();
P.s. I understand that it is very old question, but may by smb, as i, need this solution.

Resources