AngularJS $scope not accessible outside the function - javascript

I've written the following code to get the value from input fields and save it.
I defined a global variable and put the output in it to use in app.factory. the problem is "x" is only readable inside the "update function" and undefined anywhere outside it.
how can I solve this?
var app = angular.module('bomApp', ['bomGraph']);
app.controller('bomController', ['$scope', 'appService', '$rootScope', function ($scope, appService, $rootScope) {
var get = function () {
appService.get().then(function (promise) {
$scope.graph = {
options: {
"hierarchicalLayout": {
"direction": "UD"
},
"edges": {
"style":"arrow-center",
"color":"#c1c1c1"
},
"nodes": {
"shape":"oval",
"color":"#ccc"
}
},
data: {
nodes: promise.nodes,
edges: promise.edges
}
};
});
};
$scope.newNode = {
id: undefined,
label: undefined,
level: undefined,
parent: undefined,
};
$scope.arrNode = {};
$scope.update = function (nodes) {
$scope.arrNode = angular.copy(nodes);
$rootScope.x = angular.copy(nodes);
};
$scope.newEdge = {
id: undefined,
from: undefined,
to: undefined
};
$scope.arrEdge = {};
$scope.updateE = function (edges) {
$scope.arrEdge = angular.copy(edges);
};
get();
}]);
app.factory('appService', ['$q', '$http', '$rootScope', function ($q, $http, $rootScope) {
console.log($rootScope.x);
return {
get: function (method, url) {
var deferred = $q.defer();
$http.get('data.json')
.success(function (response) {
deferred.resolve(response);
})
return deferred.promise;
},
};
}]);

var x; is local to the controller alone. It can't be accessed in the factory.
Mostly if you want to share data between controllers you will store it in service. It's a bad practice also to share declare global variables in the controller
In your case, actually a closure is being created and x is a private variable. It can be accessed only within the controller.
In case you want to access variable x in service, then use $rootScope
example:
app.controller('bomController', ['$scope', 'appService', '$rootScope',function ($scope, appService,$rootScope) {
$scope.update = function (nodes) {
$scope.arrNode = angular.copy(nodes);
$rootScope.x = angular.copy(nodes);
};
});
In your service:
app.factory('appService', ['$q', '$http', '$rootScope', function ($q, $http, $rootScope) {
// you will have access to $rootScope.x
});

You can create a global $scope variable instead, $scope.x;

Declare x outside of the function:fiddle
<div ng-app="app">
<div ng-controller="ctrl">
<button ng-click="fn()">{{x}} click</button>
</div>
</div>
JS
angular.module("app", [])
.controller("ctrl", function ($scope, $rootScope) {
$scope.x = "hello";
$scope.fn = function () {
$scope.x = "world";
}
})

Related

AngularJS karma testing with chained promises

Here is the function in the Controller:
angular.module("MyMod")
.controller("MyController", function(UserData, mtcLogService, $state,
BroadcastService, $scope, $rootScope, ChartsService, PaxCountSummaryService) {
self.waveCountSummary = function() {
if (self.program.programID !== undefined) {
PaxCountSummaryService.getWaveCountChartSummary(self.program.programID)
.then(function(data) {
self.totalWaveCount = data[data.length - 1].count;
data.pop();
return (data || []).map(_.bind(ChartsService.tasksCountToChartData, this, _, "unknown"));
})
.then(function(chart) {
return self.replaceContentsOf(self.waveCountChartData, chart);
});
}
};
});
Here is my working test:
fit("My test", inject(function ($rootScope, $controller, $q) {
var results = [{
pop: sinon.spy(),
count: 1
}];
mockPaxCountSummaryService.getWaveCountChartSummary
.returns($q.resolve(results));
testController = $controller("PaxCountSummaryController", {
$state: state,
$scope: scope,
$rootScope: $rootScope,
PaxCountSummaryService: mockPaxCountSummaryService
});
testController.program = testProgram;
testController.totalWaveCount = null;
expect(testController.totalWaveCount).toBe(null);
testController.waveCountSummary();
scope.$apply();
expect(testController.totalWaveCount).toBe(1);
}));
Ok, this test works. But how would I get to the second THEN in the promise chain so I can test if REPLACECONTENTSOF is called?
Any help on this?
Well, this is one of those times where I got turned around with multiple issues but it does work as expected. I just needed to create a spy for REPLACECONTENTSOF.
Here is working final test:
fit("MyTest.", inject(function($controller, $q) {
var results = [{
pop: sinon.spy(),
count: 1
}];
mockPaxCountSummaryService.getWaveCountChartSummary
.returns($q.resolve(results));
testController = $controller("PaxCountSummaryController", {
$state: state,
$scope: scope,
PaxCountSummaryService: mockPaxCountSummaryService
});
testController.program = testProgram;
testController.replaceContentsOf = sinon.spy();
testController.totalWaveCount = null;
expect(testController.replaceContentsOf.callCount).toBe(0);
testController.waveCountSummary();
scope.$apply();
expect(testController.totalWaveCount).toBe(1);
expect(testController.replaceContentsOf.callCount).toBe(1);
}));

AngularJS: $routeParams to work within a service

I know this is easy, but I can't quite wrap my head around how to do this.
I need to do the API call within a service so that those variables can be accessed between two separate controllers.
The problem I am having is I can't access $routeParams (which I need for the get) within the service. I can't figure out know how to pass $routeParams from the controller to the service.
app.controller('Main', ['$scope', 'Page', '$routeParams', '$http', function($scope, Page, $routeParams, $http) {
$scope.Page = Page;
}]);
app.controller('Pages', ['$scope', 'Page', '$routeParams', '$http', function($scope, Page, $routeParams, $http) {
$scope.Page = Page.posts;
}]);
app.factory('Page', ['$routeParams', '$http', function($routeParams, $http) {
var posts = function posts() {
$http.get('wp-json/wp/v2/pages/?filter[name]='+ $routeParams.slug).success(function(res){
console.log(JSON.stringify(res) );
});
};
var description = '';
var title = '';
return {
title: function () { return title; },
setTitle: function (newTitle) { title = newTitle; },
description: function () { return description; },
setDescription: function (newDescription) { description = newDescription; },
posts
};
}]);
factory :
app.factory('Page', ['$http', function($http) {
var _posts = function posts(param) {
return $http.get('wp-json/wp/v2/pages/?filter[name]='+ param);
};
var description = '';
var title = '';
return {
title: function () { return title; },
setTitle: function (newTitle) { title = newTitle; },
description: function () { return description; },
setDescription: function (newDescription) { description = newDescription; },
posts : _posts
};
}]);
Controller :
app.controller('Pages', ['$scope', 'Page', '$routeParams', '$http', function($scope, Page, $routeParams, $http) {
Page.posts($routeParams.slug).then(function success(response) {
$scope.Page = response.data;
}, function error(reason) {
// do something
});
}]);
please note that success is deprecated in newer versions of Angular. I have updated the code with then

myfunction() function call from one controller to another in angularjs

i have used Angularjs and i wanna call getcustomer function from one controller to another controller i have so many doing gooogling but i don't have an idea that how to call that
i have write below code which i used
var app = angular.module('Napp', []);
app.controller('GetAlphabetical', function ($scope, $http) {
function getCutomers() {
$scope.loading = true;
$http.get('#Url.Content("~/Home/GetPesrons")').then(function (response) {
//var _data = angular.fromJson(response);
$scope.loading = false;
$scope.Customer = response.data; // please check the request response if list id in data object
}, function (error) {
throw error;
})
}
});
and second controller :
app.controller('MainCtrl', function ($scope, $http) {
getCutomers()
});
Mate, you will have to follow the following steps to resolve your problem. Firstly you have you create a factory
angular
.module('Napp')
.factory('CustomerFactory', ['$http', function ($http) {
var _factory = {};
_factory.getCustomers = function () {
return $http.get('#Url.Content("~/Home/GetPesrons")');
};
return _factory;
}]);
Then you can share data and functions between multiple controllers or services
GetAlphabetical Controller :
angular
.module('Napp')
.controller('GetAlphabetical', ['$scope', 'CustomerFactory', function ($scope, CustomerFactory) {
loadCustomers();
function loadCustomers() {
CustomerFactory.getCustomers().then(function (successResponse) {
$scope.Customer = successResponse.data; // please check the request response if list id in data object
}, function (errorResponse) {
throw error;
})
}
}]);
MainCtrl Controller :
angular
.module('Napp')
.controller('MainCtrl', ['$scope', 'CustomerFactory', function ($scope, CustomerFactory) {
loadCustomers();
function loadCustomers() {
CustomerFactory.getCustomers().then(function (successResponse) {
$scope.Customer = successResponse.data; // please check the request response if list id in data object
}, function (errorResponse) {
throw error;
})
}
}]);
This can be easily done by defining it as a service and injecting it as a dependency.
var app = angular.module('myApp', []);
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!"
};
});
app.controller('MainCtrl', function ($scope, $http, helloWorldFromService) {
app.controller('GetAlphabetical', function ($scope, $http, helloWorldFromService) {
Angular Service
What you want to do is to somehow communicate between two controllers. This can be easily be achieved using $broadcast & $on.
Incase there is a parent child relation between your controllers, use the following.
function firstCtrl($scope){
$scope.$broadcast('someEvent', [1,2,3]);
}
function secondCtrl($scope){
$scope.$on('someEvent', function(event, mass) {console.log(mass)});
}
If there is no parent child relationship between your controller, then inject $rootScope and broadcast using that.
related question - https://stackoverflow.com/a/14502755/1182982

Angular watch factory value

Say i have the following factory:
app.factory("categoryFactory", function (api, $http, $q) {
var selected = null;
var categoryList = [];
return {
getList: function () {
var d = $q.defer();
if(categoryList.length <= 0){
$http.get(api.getUrl('categoryStructure', null))
.success(function (response) {
categoryList = response;
d.resolve(categoryList);
});
}
else
{
d.resolve(categoryList)
}
return d.promise;
},
setSelected: function (category) {
selected = category;
},
getSelected: function () {
return selected;
}
}
});
now i have two controllers using this factory at the same time. Because of this both controllers has to be notified when updated for this i attempted the following:
app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api','categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {
$scope.selectedCategory = categoryFactory.getSelected();
}]);
While my other controller looks like this:
app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
$scope.categories = [];
categoryFactory.getList().then(function (response) {
$scope.categories = response;
});
$scope.selectCategory = function (category) {
categoryFactory.setSelected(category);
}
}]);
how ever when the NavController changed the value it was not changed in the DashboardController
My question is how can i either watch or in another way get notified when the value changes?
You can use an observer pattern, like so:
app.factory("categoryFactory", function (api, $http, $q) {
// the list of callbacks to call when something changes
var observerCallbacks = [];
// ...
function notifyObservers() {
angular.forEach(observerCallbacks, function(callback) {
callback();
});
}
return {
setSelected: function (category) {
selected = category;
// notify the observers after you change the value
notifyObservers();
},
registerObserver: function(callback) {
observerCallbacks.push(callback);
}
}
});
And then in your controllers:
app.controller('NavController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'FileUploader', 'categoryFactory', function ($http, $scope, $sessionStorage, $log, Session, api, FileUploader, categoryFactory) {
// ...
// init
(function() {
categoryFactory.registerObserver(function() {
categoryFactory.getList().then(function (response) {
$scope.categories = response;
});
});
})();
}]);
This way, any time setSelected is called, it calls each callback that you've registered in observerCallbacks. You can register these from any controller since factories are singletons and they will always be in the know.
Edit: just want to add that I may have put the notifyObservers() call in the wrong area (currently in setSelected) and that I may be putting the wrong update call in the controller (currently getList) but the architecture remains the same. In the registerObserver, put whatever you want to do when the values are updated and wherever you make changes that you want observers to know about call notifyObservers()
You could follow dot rule here so that prototypal inheritance will get followed.
Basically you need to have one object inside your service that will have selected variable, And will get rid of getSelected method.
Factory
app.factory("categoryFactory", function(api, $http, $q) {
var categoryFactory = {};
categoryFactory.getList = function() {
var d = $q.defer();
if (categoryList.length <= 0) {
$http.get(api.getUrl('categoryStructure', null))
.success(function(response) {
categoryList = response;
d.resolve(categoryList);
});
} else {
d.resolve(categoryList)
}
return d.promise;
}
categoryFactory.setSelected = function(category) {
categoryFactory.data.selected = category;
}
categoryFactory.data = {
selected: null
}
return categoryFactory;
});
Controller
app.controller('DashboardController', ['$http', '$scope', '$sessionStorage', '$log', 'Session', 'api', 'categoryFactory',
function($http, $scope, $sessionStorage, $log, Session, api, categoryFactory) {
//this will provide you binding without watcher
$scope.selection = categoryFactory.data;
}
]);
And then use {{selection.selected}} on html part will update a value when changes will occur in selection.

AngularJS scope not updating after data changes in factory method

I feel like I'm missing something obvious here, but I'm still stymied.
I update Thing on the scope by calling the create function on the ThingFactory. But when I reference the scope from PromoteController, the scope still contains the old version of Thing (with ID of 1).
This seems like a place where I'd want to use $scope.$apply(), but that causes the 'digest already in progress' error.
What am I missing?
var app = angular.module('app', ['ngRoute']);
app.factory('ThingFactory', ['$http', '$q', '$routeParams', function ($http, $q, $routeParams) {
var deferred = $q.defer();
return {
get: function(id) {
var thing = {
id: 393,
name: 'Can I be gotten?',
description: 'get'
};
deferred.resolve(thing);
return deferred.promise;
},
save: function (thing) {
console.log("ThingFactory -> CREATE");
var thing = {
id: 122,
name: 'after create.',
description: 'creatine'
};
deferred.resolve(thing);
return deferred.promise;
},
init: function() {
console.log("ThingFactory -> INIT");
var thing = {
id: 1,
name: 'initial value',
description: 'INIT'
};
deferred.resolve(thing);
return deferred.promise;
}
};
}]);
app.config(function ($routeProvider, $locationProvider, $httpProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/build', {
templateUrl: '/build.html',
controller: 'BuildController'
})
.when('/things/:id/promote', {
templateUrl: '/promote.html',
controller: 'PromoteController'
})
});
app.controller('BuildController', function ($scope, $http, $location, ThingFactory) {
// HERE I INITIALIZE THE THING
ThingFactory.init().then(function(thing) {
$scope.thing = thing;
});
$scope.saveNewThing = function() {
// HERE I 'SAVE' THE THING
ThingFactory.save($scope.thing).then(function(thing) {
$scope.thing = thing;
$location.path("/" + thing.id + "/promote");
})
}
});
app.controller('PromoteController', function ($scope, $http, $routeParams, ThingFactory) {
// HERE'S WHERE THE THING ON THE SCOPE SHOULD HAVE AN ID OF 122,
// BUT IS STILL 1
var id = $routeParams.id;
ThingFactory.get({id: id}).then(function(thing) {
$scope.thing = thing;
});
});
please create a var deferred = $q.defer(); for every method in you factory. otherwise you always use the same deferred and this is resolved with the value in your init function.

Categories