angularjs factories seem to be created lazily - angularjs

Today I am experiencing something really odd which I have never read about.
Just think the debugger is on the "var startDateOfWeek..." line.
With my mouse pointer I hover over the wizardDataFactory object. This object is not instantiated when there is not the line of code "var x = ..." which comes later...
WHY is that? I have never read something about factories are somehow lazily instantiated?
Well I do not want to complain because actually thats a good thing. Do not instantiate when the factory is unused in code.
Can someone please share a link where I can read about that?
'use strict';
angular.module('iplanmylessons').service('periodService', function ($q, $http, datetimeFactory, weeklyDataGridViewModelFactory, wizardDataFactory) {
this.getWeeklyPeriods = function (schoolyearId, firstDayOfWeek) {
var startDateOfWeek = datetimeFactory.getFirstDateOfWeek(firstDayOfWeek);
var endDateOfWeek = datetimeFactory.getLastDateOfWeek(firstDayOfWeek);
var x = wizardDataFactory.transform();
return [];
};
});

Your understanding is not correct.
wizardDataFactory is initialized as soon as any other component requests it as a dependency.
The hovering undefined is probably due to a browser error.

Related

AngularJS shared service not acting as singleton between modules

I have seen numerous flavors of this question, but cannot seem to find the right answer for my issue.
The problem with the following is the service cannot share data between the other modules/controllers. It does not act as a singleton. Meaning, if I add windows in dashboard and call windowService.getWindows() I will see the windows I added. But if I do the same from myWidget after adding them via the dashboard, or vice versa, the collection in the service is empty as if the other module/controller hasn't added anything. What am I doing wrong here? I expected a single collection of windows to be retained by the service and be accessible from either of my other modules.
Module 1
var dashboard = angular.module('dashboard', [windowServiceModule]);
dashboard.controller('DashboardController', function DashboardController(windowService) {...stuff});
Module 2
var myWidget = angular.module('myWidget', [windowServiceModule]);
myWidget.controller('MyWidgetController', function MyWidgetController(windowService) {...stuff});
Service
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function() {
var windows = [];
this.addWindow = function(win)
{
this.windows.push(win)
}
this.getWindows = function()
{
return this.windows;
}
});
EDIT: while walking through other answers, I tried this: Share a single service between multiple angular.js apps
That exposed a part of the problem, I think. The above suggests iterating over a $rootScope collection, but in my case, there is only one $rootScope in the collection and it is not the same between dashboard and myWidget. As it is, myWidget is in an iframe and now I'm thinking I need to take a different approach, currently testing a solution similar to the link I left in the comments.
Ok, I'm pretty sure my initial code would have worked fine if the myWidget wasn't being loaded in an iframe. I have a dashboard that can open windows embedded or popped out and I wanted to use the service to manage the windows and handle passing state back and forth since popping out the iframe requires a reload of the page.
Anyways, I now have something like this in place in the service and it is working:
var windowServiceModule = angular.module('windowService', []);
windowServiceModule.service('windowService', function($window) {
...stuff
var parentScope = $window.parent.getScope();
...other stuff
After that you can access anything in the parent's scope with parentScope. I pass in the angular $window.
Just for reference, getScope() already existed in the parent JSP file. This is what is looks like:
function getScope()
{
return angular.element($("#DashboardController")).scope();
}

AngularJS 1.5 calling a method from another in component's controller

I am using angularJS 1.5 component in my application. sample code of component's controller as bellow:
function testController()
{
var vm = this;
activate(); // this will be called on first load on component.
vm.testMethod = function ()
{
return "test Method";
}
function activate()
{
console.log(vm.testMethod());
}
when I execute this I am getting error
TypeError: vm.testMethod is not a function.
I know I can create a local function to controller not appending vm., however, in my need, I have a vm.testMethod() used in template to get return some text, which is working properly. e.g.
--template code
{{$ctrl.testMethod()}} // This works properly and display 'test Method' on page.
Due to some reason, I am trying to call vm.testMethod() inside another method e.g. activate(), however getting an error mentioned above?
May I know if am missing anything or trying something which is not possible.
Your issues does not have anything to do with Angular :-)
Your activate function is hoisted because it is a function declaration. That why you can call it "before your wrote it". BUT, vm.testMethod is a function expression and won't get hoisted.
Here is a super simple example that shows the issue your having:
var vm = {};
console.log(vm.hello);
vm.hello = function () {};
console.log(vm.hello);
I would recommend you to read this article for a better understanding of how expressions and declarations work in JavaScript. Also, in order to prevent this from happening again you should follow this advice from John Papa:
Always write function declarations at the bottom of your controller and assign them at the top when you defined your vm variable.

Passing data to new page using Onsenui

I am trying to call an API end point once a user clicks a button holding a myNavigator.pushPage() request. However,I can not get the $scope data generated from the $http.get request to be passed to the new page.
If I test using console.log('test'); inside the .success of the $http.get request I successfully get the log info in the console but any data held in $scope.var = 'something'; does not gets passed to the page! Really confused!
$scope.historyDetails = function(id){
var options = {
animation: 'slide',
onTransitionEnd: function() {
$http.get('http://xxx-env.us-east-1.elasticbeanstalk.com/apiget/testresult/testId/'+id).success(function(data) {
$scope.testscore = 'something'; // this is not getting passed to page!
console.log('bahh'); // But I see this in console
});
}
};
myNavigator.pushPage("activity.html", options);
}
Page:
<ons-page ng-controller="HistoryController">
...
<span style="font-size:1.2em">{{testscore}} </span><span style="font-size:0.5em;color:#555"></span>
...
</ons-page>
Yes, that's so because both pages has different controllers, resulting in different scopes. One can not access variables from one scope to another.
Hence one solution in this case can be using rootScope service.
Root Scope is parent scope for all scopes in your angular application.
Hence you can access variable of root scopes from any other scope, provided that you are injecting $rootScope service in that controller.
to know more about rootScope check this link.
Good luck.
Update 1:
check these articles
http://www.dotnet-tricks.com/Tutorial/angularjs/UVDE100914-Understanding-AngularJS-$rootScope-and-$scope.html
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
As Yogesh said the reason you're not getting your values is because if you look at $scope.testscore and try to find where is the $scope defined you will see that it's an argument for the controller function (thus it's only for that controller).
However we can see that the controller is attached to the page and you are pushing another page.
So in that case you have several options:
Use the $rootScope service as Yogesh suggested (in that case accept his answer).
Create your own service/factory/etc doing something similar to $rootScope.
(function(){
var historyData = {};
myApp.factory('historyData', function() {
return historyData;
});
})();
Technically you could probably make it more meaningful, but maybe these things are better described in some angular guides.
If you have multiple components sharing the same data then maybe you could just define your controller on a level higher - for example the ons-navigator - that way it will include all the pages. That would be ok only if your app is really small though - it's not recommended for large apps.
If this data is required only in activity.html you could just get it in that page's controller. For example:
myApp.controller('activityController', function($scope, $http) {
$http.get(...).success(function(data) {
$scope.data = data;
});
}
But I guess you would still need to get some id. Anyway it's probably better if you do the request here, now you just need the id, not the data.
You could actually cheat it with the var directive. If you give the activity page <ons-page var="myActivityPage"> then you will be able to access it through the myActivityPage variable.
And the thing you've been searching for - when you do
myNavigator.pushPage("activity.html", options);
actually the options is saved inside the ons-page of activity.html.
So you can do
myNavigator.pushPage("activity.html", {data: {id: 33}, animation: 'slide'});
And in the other controller your id will be myActivityPage.options.data.id.
If you still insist on passing all the data instead of an id - here's a simple example. In the newer versions of the 2.0 beta (I think since beta 6 or 7) all methods pushPage, popPage etc return a promise - which resolve to the ons-page, making things easier.
$scope.historyDetails = function(id){
myNavigator.pushPage("activity.html", {animation: 'slide'}).then(function(page) {
$http.get('...' + id).success(function(data) {
page.options.data = data;
});
});
});
Side note: You may want to close the question which you posted 5 days ago, as it's a duplicate of this one (I must've missed it at that time).

TypeError: Attempted to assign to readonly property. in Angularjs application on iOS8 Safari

Our Mobile App is getting "TypeError: Attempted to assign to readonly property." only on IOS 8 and the stack traces are not helpful and seem to be in Angular code.
This might be happening because of the "use strict" on top level of Angularjs code.
My questions are (1) why did it start happening only on IOS8? is this an IOS8 bug?
(2) or is this an angular bug surfaced on IOS8? (3) Or maybe we are violating strict mode rules but only IOS8 started to catch them! I am skeptical about the third option because strict mode is supported by other major browsers.
I have found one similar reported issue here.
Looks like this is an IOS8 bug
At least the guys at ember.js will temporarily strip the 'use strict' from their code until it's fixed.
Ember.js issue
SOLUTION -
Note:
angular.copy didn't work for me
lodash cloneDeep didn't work either.
removing "use strict"; didn't help - It just removed the error being logged in a try/catch arround the "erroneous" assignment to a readonly value
For us, the value that was being passed back from the SQL promise value was an array of objects.
The values that we were getting "read only" error on were strings, bools, and numbers.
console.log('this will log');
sqlReturnArray[0].name = "Bob"; // read only error here
console.log('wouldnt get to here to log');
I figured, well, if I can't assign values to properties on THOSE objects within the array, why can't I just copy over all the objects' values? Assignment of objects is by reference so just doing a loop over the array won't help. You need to loop over the keys and values.
This solved our problem
// where sqlReturnArray is the object given from the SQL promise
var newArrayOfObjects = [];
angular.forEach(sqlReturnArray, function (obj) {
var newObj = {};
angular.forEach(obj, function (val, key) {
newObj[key] = val;
});
newArrayOfObjects.push(newObj);
});
I just had this error and the fix was pretty simple.
My Code was-
$http.get('/api/v1/somedata').then(function(result){
$scope.data.somedata = result.data.list;
});
I get the TypeError in the assign. Because I haven't defined $scope.data.somedata anywhere and it got $scope.data undefined and trying to assign some property to it initiated the error.
I simply put $scope.data = {somedata : [] } in the top of the controller. I can also get rid of this error by changing $scope.data.somedata to $scope.somedata because any property of the $scope object that is undefined will end up to the $rootscope and won't give any error. I found this is pretty hard to debug my view-model. And also for this TypeError I can find out what exactly is wrong.
I had it with a form.
my issue was because of binding an SQL Return Object to an input.
after coping with angular.copy() problem resolved.
try to copy your return values and retry.
$scope.yourFunc= function(result) {
var item = angular.copy(result.rows.item(0));
...
};

Load-time exception handling in AngularJS

I need to execute a function which is defined in controller in load-time, in order to gain json data from another place right after page is loaded.
I've tried to call the func immediately within controller, now i feel it was bad idea.
When something bad is happen and exception is raised - the controller stops working.
Well, not big surprise, but at the moment i don't have idea how work it out.
Ofcourse, i can wrap possible dangerous code in try-catch, but that's definetely not best solution imho.Here's the sample code:
app.controller("ServerStatusCtrl",
function($scope) {
$scope.reloadFunc = function()
{
throw "dat bad exception";
}
$scope.reloadFunc(); // Let's pretend that it's needed 2 call this function in load-time.
});
And example on jsfiddle
I advice you to use $q's way of notifying that something happen: return promise and reject it after something wrong happen.
This is the way how exception handling is done in async/promise way.
General idea is:
Instead of returning result, function should return promise
When you have your data ready (loaded from server) you resolve promise
If something bad happen you reject it.
function someFunc() {
var d = $q.defer();
do.somethingAsync(function(result) {
if (somethingWrong) d.reject(result);
else d.resolve(result);
});
return d.promise;
}
And in controller:
$scope.myData = someFunc().then(function ok(result) { return ok.data; }, function faled() { handle...});
This gives a good control on error handling/recovery.
Found easier solution for this.
Just discovered a ngInit directive which solved the whole problem.
Also, i think that module.run(fn) would be also applicable for this kind of tasks.

Resources