Related
I'm working on an app developed by other people, I don't have any documentation or any way to contact them, and I have this problem:
It's an angular application that need to be refreshed every minute, In other sections of the app, they are using $interval, but in this page if I try to use it, the calls to my WebAPI stop working and I recive this error:
WebApiFactory.getNoStatusOrders is not a function
I need your help to know why is this happening, here is my code:
angular.module('AssemblyLive').controller('overviewCarsWithoutStatus', [
'$scope', '$rootScope', '$location', 'WebApiFactory', "$interval", 'StatusPointMonitorFactory',
function ($scope, $rootScope, $location, $interval, WebApiFactory, StatusPointMonitorFactory) {
...
var areas = {
bodyShop1:"B000,B001,B020,B031,B200,B211,B220,B231,B300,B311,B320,B331,B370,B371,B375,B376,B380,B381,B383,B386,B390,B400,B401,B410,B420,B421,B425,B426,B430",
bodyShop2: "B431,B435,B436,B440,B451,B500,B521,B540,B551,B553,B600,B750,B760,B770,B781,B783,B790,B791,B800,B811,B813,B900",
paintShop1: "D000,D011,D012,D051,D100,D130,D131,D132,D151,D152,D230,D251,D252,D300,D351,D352,D360,D381,D390,D400,D451,D452,D470",
paintShop2: "D471,D472,D481,D482,D500,D501,D531,D532,D540,D561,D562,D600,D620,D650,D670,D691,D692,D700,D731,D741,D750,D800,D812,D821,D822,D841,D842",
assembly: "F000,F100,F150,F200,F250,F300,F350,F400,F450,F500,F550,F600,F650,F700,F750,F800,F850,F900"
};
...
//Start the creation of the overview.
function initializeOverview() {
$scope.counters = [];
//Iterates over the configuration.
//Get all the status to call the query.
var status = $scope.headers.map(function(a) {return a.status.toString();});
//Call to create the knrs b y area.
getKNRWithoutStatus(status.toString());
for(var status in areas){
getCarsWithNoStatus(areas[status]);
}
getNoStatusCounters("B000,C000,D000,E000,E500,F000,F100,F950,G000,G900");
}
// Funtion to set the counters in the table headers
function getNoStatusCounters(status) {
WebApiFactory.getNoStatusOrdersCounter(status).then(function (resultData) {
Object.getOwnPropertyNames(resultData.NoStatusOrders).forEach(function(name) {
...
function getCarsWithNoStatus(status) {
// Fetch all the counters from the WebAPI
WebApiFactory.getNoStatusOrdersCounter(status).then(function (resultData) {
...
function getKNRWithoutStatus(status) {
var orders = [];
//Query the status monitor.
WebApiFactory.getNoStatusOrders(status).then(function(statusMonData) {
...
/** To refresh data when the clock in the footer refresh. */
$rootScope.$on('carsWithoutStatusOverview', function(){
initializeOverview();
$interval(initializeOverview, Config.dataRefreshInterval * 1000)
});
Note: My page works perfectly without adding $interval
Review your dependency injection. You put WebApiFactory and $interval in different positions in the $inject list and in the parameters. (So the $interval variable points to your WebApiFactory service and vice versa.)
In order to retain a $rootScope value on refresh[F5] we can use $route.reload in controller as below:
$scope.reloadCtrl = function(){ console.log('reloading...'); $route.reload(); }
As i am using so many controllers, is there any way to use commonly in app.config()?
By refreshing the page you will wipe your $rootscope from memory. Your application restarts.
You can use some kind of storage. That way you can save a users preference and use it again when he comes back to you application.
You can use for example $cookies or sessionStorage / localStorage.
If you want to detect refresh on your app.run you can do by this way:
In the app.run() block inject '$window' dependency and add:
app.run(['$rootScope', '$location', '$window',function($rootScope,$location, $window) {
window.onbeforeunload = function() {
// handle the exit event
};
// you can detect change in route
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (!current) {
// insert segment you want here
}
});
}]);`
You can use a angular factory instead to have all the values across controllers
Use the below code
var app = angular.module('myApp', []);
app.factory('myService', function() {
var v1;
var v2;
var v3;
return{
v1:v1,
v2:v2,
v3:v3
});
app.controller('Ctrl1', function($scope,myService) {
});
app.controller('Ctrl2', function($scope,myService) {
});
If your using constants in $rootscope u can even use this
app.constant('myConfig',
{
v1:v1,
v2:v2,
v3:v3
});
I don't know what it is about injecting factories, but I am having the most difficult time.
I've simulated what I'm attempting to do via this sample plunk http://plnkr.co/edit/I6MJRx?p=preview, which creates a kendo treelist - it works fine.
I have an onChange event in script.js which just writes to the console. That's also working.
My plunk loads the following:
1) Inits the app module, and creates the main controller myCtrl (script.js)
2) Injects widgetLinkingFactory int myCtrl
3) Injects MyService into widgetLinkingFactory
The order in which I load the files in index.html appears to be VERY important.
Again, the above plunk is NOT the real application. It demonstrates how I'm injecting factories and services.
My actual code is giving me grief. I'm having much trouble inject factories/services into other factories.
For example,
when debugging inside function linking() below, I can see neither 'CalculatorService' nor 'MyService' services. However, I can see the 'reportsContext' service.
(function () {
// ******************************
// Factory: 'widgetLinkingFactory'
// ******************************
'use strict';
app.factory('widgetLinkingFactory', ['reportsContext', 'MyService', linking]);
function linking(reportsContext, MyService) {
var service = {
linkCharts: linkCharts
};
return service;
function linkCharts(parId, widgets, parentWidgetData) {
// *** WHEN DEBUGGING HERE, ***
// I CANNOT SEE 'CalculatorService' AND 'MyService'
// HOWEVER I CAN SEE 'reportsContext'
if (parentWidgetData.parentObj === undefined) {
// user clicked on root node of grid/treelist
}
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
// REFRESH HERE...
}
});
}
}
})();
A snippet of reportsContext'service :
(function () {
'use strict';
var app = angular.module('rage');
app.service('reportsContext', ['$http', reportsContext]);
function reportsContext($http) {
this.encodeRageURL = function (sourceURL) {
var encodedURL = sourceURL.replace(/ /g, "%20");
encodedURL = encodedURL.replace(/</g, "%3C");
encodedURL = encodedURL.replace(/>/g, "%3E");
return encodedURL;
}
// SAVE CHART DATA TO LOCAL CACHE
this.saveChartCategoryAxisToLocalStorage = function (data) {
window.localStorage.setItem("chartCategoryAxis", JSON.stringify(data));
}
}
})();
One other point is that in my main directive code, I can a $broadcast event which calls the WidgetLinking factory :
Notice how I'm passing in the widgetLinkingFactory in scope.$on. Is this a problem ?
// Called from my DataModel factory :
$rootScope.$broadcast('refreshLinkedWidgets', id, widgetLinkingFactory, dataModelOptions);
// Watcher setup in my directive code :
scope.$on('refreshLinkedWidgets', function (event, parentWidgetId, widgetLinkingFactory, dataModelOptions) {
widgetLinkingFactory.linkCharts(parentWidgetId, scope.widgets, dataModelOptions);
});
I am wasting a lot of time with these injections, and it's driving me crazy.
Thanks ahead of time for your assistance.
regards,
Bob
I think you might want to read up on factories/services, but the following will work:
var app = angular.module('rage')
app.factory('hi', [function(){
var service = {};
service.sayHi = function(){return 'hi'}
return service;
}];
app.factory('bye', [function(){
var service = {};
service.sayBye = function(){return 'bye'}
return service;
}];
app.factory('combine', ['hi', 'bye', function(hi, bye){
var service = {};
service.sayHi = hi.sayHi;
service.sayBye = bye.sayBye;
return service;
}];
And in controller...
app.controller('test', ['combine', function(combine){
console.log(combine.sayHi());
console.log(combine.sayBye());
}];
So it would be most helpful if you created a plunk or something where we could fork your code and test a fix. Looking over your services it doen't seem that they are returning anything. I typically set up all of my services using the "factory" method as shown below
var app = angular.module('Bret.ApiM', ['ngRoute', 'angularFileUpload']);
app.factory('Bret.Api', ['$http', function ($http: ng.IHttpService) {
var adminService = new Bret.Api($http);
return adminService;
}]);
As you can see I give it a name and define what services it needs and then I create an object that is my service and return it to be consumed by something else. The above syntax is TypeScript which plays very nice with Angular as that is what the Angular team uses.
My application initializes an object graph in $rootScope, like this ...
var myApp = angular.module('myApp', []);
myApp.run(function ($rootScope) {
$rootScope.myObject = { value: 1 };
});
... and then consumes data from that object graph (1-way binding only), like this ...
<p>The value is: {{myObject.value}}</p>
This works fine, but if I subsequently (after page rendering has completed) try to update the $rootScope and replace the original object with a new one, it is ignored. I initially assumed that this was because AngularJS keeps a reference to the original object, even though I have replaced it.
However, if I wrap the the consuming HTML in a controller, I am able to repeatedly update its scope in the intended manner and the modifications are correctly reflected in the page.
myApp.controller('MyController', function ($scope, $timeout) {
$scope.myObject = { value: 3 };
$timeout(function() {
$scope.myObject = { value: 4 };
$timeout(function () {
$scope.myObject = { value: 5 };
}, 1000);
}, 1000);
});
Is there any way to accomplish this via the $rootScope, or can it only be done inside a controller? Also, is there a more recommended pattern for implementing such operations? Specifically, I need a way to replace complete object graphs that are consumed by AngularJS from outside of AngularJS code.
Thanks, in advance, for your suggestions,
Tim
Edit: As suggested in comments, I have tried executing the change inside $apply, but it doesn't help:
setTimeout(function() {
var injector = angular.injector(["ng", "myApp"]);
var rootScope = injector.get("$rootScope");
rootScope.$apply(function () {
rootScope.myObject = { value: 6 };
});
console.log("rootScope updated");
}, 5000);
Except for very, very rare cases or debugging purposes, doing this is just BAD practice (or an indication of BAD application design)!
For the very, very rare cases (or debugging), you can do it like this:
Access an element that you know is part of the app and wrap it as a jqLite/jQuery element.
Get the element's Scope and then the $rootScope by accessing .scope().$root. (There are other ways as well.)
Do whatever you do, but wrap it in $rootScope.$apply(), so Angular will know something is going on and do its magic.
E.g.:
function badPractice() {
var $body = angular.element(document.body); // 1
var $rootScope = $body.scope().$root; // 2
$rootScope.$apply(function () { // 3
$rootScope.someText = 'This is BAD practice :(';
});
}
See, also, this short demo.
EDIT
Angular 1.3.x introduced an option to disable debug-info from being attached to DOM elements (including the scope): $compileProvider.debugInfoEnabled()
It is advisable to disable debug-info in production (for performance's sake), which means that the above method would not work any more.
If you just want to debug a live (production) instance, you can call angular.reloadWithDebugInfo(), which will reload the page with debug-info enabled.
Alternatively, you can go with Plan B (accessing the $rootScope through an element's injector):
function badPracticePlanB() {
var $body = angular.element(document.body); // 1
var $rootScope = $body.injector().get('$rootScope'); // 2b
$rootScope.$apply(function () { // 3
$rootScope.someText = 'This is BAD practice too :(';
});
}
After you update the $rootScope call $rootScope.$apply() to update the bindings.
Think of modifying the scopes as an atomic operation and $apply() commits those changes.
If you want to update root scope's object, inject $rootScope into your controller:
myApp.controller('MyController', function ($scope, $timeout, $rootScope) {
$rootScope.myObject = { value: 3 };
$timeout(function() {
$rootScope.myObject = { value: 4 };
$timeout(function () {
$rootScope.myObject = { value: 5 };
}, 1000);
}, 1000);
});
Demo fiddle
I'm building a pretty simple app where I have a GlobalController (on body element), and will have another sub-controller below. This is a templated site with multiple, physical pages such that the sub-controller will be different, but at most there will only be a top-level Global one and a single sub-one.
I'm trying to make global functions that any sub-controller can use to run code that each needs to run without having to duplicate the functionality in each sub-controller.
One way I could do this would be to include $rootScope and then emit() messages to the GlobalController who is listening for them using $on().
I gather this is not a "good" way to do this. Rather, I've learned that it's better to use a service for this. I'm now stuck on how to implement this service.
I currently have a Global Controller like so:
var globalModule = angular.module('DoSquareStuff', ["ngRoute","list", "savings-video"]);
// there will be a long list of modules that will be added after "savings-video"
globalModule.factory('squareMgr', function() {
var squares = SUYS.squares; // global obj with earned[] and placed[]
return {
getSquaresEarned: function() {
return squares.earned;
},
getSquaresPlaced: function() {
return squares.placed;
},
setThisSquareEarned: function(value) {
squares.earned.push(value);
},
setThisSquarePlaced: function(value) {
squares.placed.push(value);
},
earnedThisSquare: function(squareNum) {
return ~squares.earned.indexOf(squareNum);
},
placedThisSquare: function(squareNum) {
return ~squares.placed.indexOf(squareNum);
}
}
});
globalModule.controller('GlobalController', function($scope, $squareMgr){
// this would be easy... but it doesn't work
// $rootScope.$on('signal.missionComplete', function (event, missionId) {
// console.log("parentScope receive notice that mission " + missionId + " is complete.");
// });
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned()); //broken
});
Then, reading the docs, I tried:
globalModule.controller('GlobalController', ['squareMgr', function($squareMgr){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
In your last code block, you need to inject $scope as well. You can inject any number of services that you need:
globalModule.controller('GlobalController', ['squareMgr', '$scope',
function($squareMgr, scope){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
And a minor point, I wouldn't put a $ in front of squareMgr, the $ implies it is a built in angular service.
Try
globalModule.controller('GlobalController', ['squareMgr', '$scope', function($scope, squareMgr){ .....
The $ sign is used to differentiate between Angular services and your own