Service to call and update angular controller $scope from outside of controller? - javascript

controller function:
$scope.init = function () {
Words.get('init', $scope.randNum)
.error(function (data) {
console.log('init error: ' + data.message);
})
.success(function (data) {
$scope.board = data.settings.board;
$scope.tray = data.tray;
$scope.scores = data.pointsPerLetter;
$scope.totalScore = data.score.total;
console.log('init: ' + $scope.tray);
})
}
and my service:
angular.module('wordService', [])
.factory('Words', function ($http) {
var id;
return {
get: function (call, num) {
id = num;
return $http.get('http://xxxxx');
},
send: function (call, data) {
console.log('send: ' + data)
return $http.post('http://xxxxx' + call, data);
}
}
});
Now instead of ngAccess = angular.element(document.getElementById('ws')).scope();
to call ngAccess.init() or $scope.init
How would I add this call to a service and thus call it when needed while still updating the scope within the controller? The reason the above will not work is that I am using browserify and I dont yet have access to the scope.
Scenario: I need to be able to click a button and call a function that updates the scope.
Caveat: the button is created and added to a canvas. (shouldnt matter as I still have the click calls etc).
As always thanks in advance!

Move the data object into the service and assign a reference to a controller scope variable...
Your factory might look like:
.factory('Words', function ($http) {
var id;
var results = {};
var get = function (call, num) {
id = num;
return $http.get('http://xxxxx').then(function(response){
results.board = response.data.settings.board;
results.tray = response.data.tray;
results.scores = response.data.pointsPerLetter;
results.totalScore = response.data.score.total;
};
};
var send = function (call, data) {
console.log('send: ' + data)
return $http.post('http://xxxxx' + call, data);
};
return {
get: get,
send: send,
results: results
}
});
While your controller would then look like:
.controller(function($scope, Words){
$scope.words = Words.results;
$scope.init = function () {
Words.get('init', $scope.randNum).then(function(){
console.log($scope.words); // should log the data you want
}, function(response){ console.log(response)});
};
// still calling from controller but you could from any component and still
// have the local scope variable update based on its assignment to the
// service object
$scope.init();
})
Note that I did modify your factory a bit more to use the revealing module pattern. This way, you can make internal calls to your get/set functions in addition to calls from other components.
Now you should be able to add a button virtually anywhere else in the app (ie doesn't need prototypical inheritance from your controller's scope). For example, this directive would make a call and update the results, which would reflect in the controller scope variable
.directive('update', function(Words){
return function(scope) {
scope.update = function(){
Words.get('update', 'somevalue')
}
}
})
where it is declared in the view like this:
<button update ng-click="update()">Update</button>

Related

Passing variables between controllers using AngularJS

I have two controllers: Controller1 and Controller2
In Controller1's $scope, I have set up all my values I need. Using the data in $scope, I'm trying to run certain functions and pass the return values to Controller2.
I was thinking about making a factory to pass variable from Controller1 to Controller2. However, I realized all input values I need lives in Controller 1. I wonder whether factory can persist the data when it runs in Controller1 and return that data when it runs again in Controller2.
Thanks
Factory is a singleton so it can be used to share data among different controllers or directives. Take a look at the fiddler here. I have created a factory 'sharedContext' which can be used to share any key-value pair across controllers using different $scope.
Factory
myApp.factory("sharedContext", function() {
var context = [];
var addData = function(key, value) {
var data = {
key: key,
value: value
};
context.push(data);
}
var getData = function(key) {
var data = _.find(context, {
key: key
});
return data;
}
return {
addData: addData,
getData: getData
}
});
From the controller that needs to share the object can call the 'addData' method of the factory to store the data under a key. The other controllers/directives which are interested in accessing the shared data can do so by calling the 'getData' method and passing the correct key.
Controller (Sharing Data)
function MyCtrl_1($scope, sharedContext) {
$scope.input_1 = 5;
$scope.input_2 = 15;
$scope.add = function() {
$scope.result = $scope.input_1 + $scope.input_2;
sharedContext.addData("Result", $scope.result)
}
}
Controller (accessing shared data)
function MyCtrl_2($scope, sharedContext) {
$scope.getData = function() {
$scope.result = sharedContext.getData("Result").value;
}
}
The only assumption here is that both the controllers need to use the exact key to share the data. To streamline the process you can use a constant provider to share the keys. Also note that I have used underscore.js to look for the key in the shared context dictionary.
This is the simplest solution that you can come up with. As you can see the factory is a simple object and because of that construct it's passed by reference not by value that means in both controller dataFactory is the same
http://plnkr.co/edit/eB4g4SZyfcJrCQzqIieD?p=preview
var app = angular.module('plunker', []);
app.controller('ControllerOne', function (dataFactory) {
this.formFields = dataFactory
});
app.controller('ControllerTwo', function (dataFactory) {
this.formData = dataFactory
});
app.factory('dataFactory', function () {
return {};
})
edit
app.factory('dataFactory', function () {
var factory = {
method1: function (arg) {
console.log('method1: ', arg)
factory.method2('called from method1')
},
method2: function (arg) {
console.log('method2: ', arg)
}
}
return factory;
})

Unable to sync AngularJS service with controllers (angular.copy)

I have set up two controllers (Controller A and Controller B) and a service (Service). I am attempting to sync the data from controller A to the service, and present that information to Controller B.
Within my Service, I've established a variable confirmdata and get and set functions:
function setData(data) {
confirmdata = angular.copy(data);
}
function getData() {
return confirmdata;
}
In controller A I've created a function syncto sync information from the controller to the service:
this.sync = function () {
var data = {
payment: this.getpayment()
}
Service.setData(data);
In controller B I've assigned a function as:
this.sync = function () {
this.viewData = Service.getData();
console.log('TestingData', this.viewData);
For a reason I am unaware of; my console log simply returns undefined when it should be returning the results of the getpayment() function. Am I missing something here?
The fact that you are getting undefined would indicate that you haven't initialized 'confirmdata' in your service. Whether this is the actual issue though, isn't clear. For a simple example, I would design your service like this:
myApp.factory('sharedService', [function () {
var confirmdata = {};
return {
setData: function (newData) { confirmdata = newData; },
getData: function getData() { return confirmdata; }
}
}]);
Take a look at this plunker. It gives an example of data being shared between controllers via a service.

Angular/Javascript Scope

I'm trying to expose the data obtained from the success method of a promise. In short, I don't know how to grab $scope.storedData. As it is right now, it is undefined.
genericService.js
myApp.factory('genericService', function($http){
return $http.jsonp('http://foo.com/bar.json')
.success(function(data){
return data;
})
.error(function(err){
return err;
});
});
genericController.js
myApp.controller('genericController', ['$scope','genericService',
function($scope, genericService){
genericService.success(function(data){
$scope.storeData(data);
});
$scope.storedData; // Undefined here.
$scope.storeData = function(whatever){
$scope.storedData = whatever;
}
console.log('data stored is: ', $scope.storedData); // Still undefined
}]);
How do I expose $scope.storedData to the scope outside of storeData() or genericService.success()?
Note: I don't want to use $watch. I want to overcome this scope issue fairly un-Angularly... because it should be possible.
There are 2 things I typically do:
I use models that define the expected response and will generally init my controller with an empty model.
I use a variable to track my state.
Here's an example of what my controller might look like:
myApp.controller('genericController', GenericController);
GenericController.$inject = [
'$scope',
'genericService'
];
function GenericController(
$scope,
genericService
) {
$scope.loadData = loadData;
$scope.storeData = storeData;
init();
///////////////////
function init() {
$scope.isLoaded = false;
$scope.storedData = {}; // if you use a model class, a new instance of this works best.
}
function loadData() {
genericService.success(function(data){
$scope.storeData(data);
$scope.isLoaded = true;
});
}
function storeData(whatever) {
$scope.storedData = whatever;
}
}

How to include/inject functions which use $scope into a controller in angularjs?

I am trying to include a library of functions, held in a factory, into a controller.
Similar to questions like this:
Creating common controller functions
My main controller looks like this:
recipeApp.controller('recipeController', function ($scope, groceryInterface, ...){
$scope.groceryList = [];
// ...etc...
/* trying to retrieve the functions here */
$scope.groceryFunc = groceryInterface; // would call ng-click="groceryFunc.addToList()" in main view
/* Also tried this:
$scope.addToList = groceryInterface.addToList();
$scope.clearList = groceryInterface.clearList();
$scope.add = groceryInterface.add();
$scope.addUp = groceryInterface.addUp(); */
}
Then, in another .js file, I have created the factory groceryInterface. I've injected this factory into the controller above.
Factory
recipeApp.factory('groceryInterface', function(){
var factory = {};
factory.addToList = function(recipe){
$scope.groceryList.push(recipe);
... etc....
}
factory.clearList = function() {
var last = $scope.prevIngredients.pop();
.... etc...
}
factory.add = function() {
$scope.ingredientsList[0].amount = $scope.ingredientsList[0].amount + 5;
}
factory.addUp = function(){
etc...
}
return factory;
});
But in my console I keep getting ReferenceError: $scope is not defined
at Object.factory.addToList, etc. Obviously I'm guessing this has to do with the fact that I'm using $scope in my functions within the factory. How do I resolve this? I notice that in many other examples I've looked at, nobody ever uses $scope within their external factory functions. I've tried injecting $scope as a parameter in my factory, but that plain out did not work. (e.g. recipeApp.factory('groceryInterface', function(){ )
Any help is truly appreciated!
Your factory can't access your $scope, since it's not in the same scope.
Try this instead:
recipeApp.controller('recipeController', function ($scope, groceryInterface) {
$scope.addToList = groceryInterface.addToList;
$scope.clearList = groceryInterface.clearList;
$scope.add = groceryInterface.add;
$scope.addUp = groceryInterface.addUp;
}
recipeApp.factory('groceryInterface', function () {
var factory = {};
factory.addToList = function (recipe) {
this.groceryList.push(recipe);
}
factory.clearList = function() {
var last = this.prevIngredients.pop();
}
});
Alternatively, you can try using a more object oriented approach:
recipeApp.controller('recipeController', function ($scope, groceryInterface) {
$scope.groceryFunc = new groceryInterface($scope);
}
recipeApp.factory('groceryInterface', function () {
function Factory ($scope) {
this.$scope = $scope;
}
Factory.prototype.addToList = function (recipe) {
this.$scope.groceryList.push(recipe);
}
Factory.prototype.clearList = function() {
var last = this.$scope.prevIngredients.pop();
}
return Factory;
});
You cannot use $scope in a factory as it is not defined. Instead, in your factory functions change the properties of the object the factory is returning, e.g.
factory.addToList = function (recipe) {
this.groceryList.push(recipe);
}
these will then get passed on to your $scope variable
$scope.addToList = groceryInterface.addToList;
// ... = groceryInterface.addToList(); would assign to `$scope.addToList` what is returned, instead of the function itself.
This isn't the exact answer for this question, but I had a similar issues that I solved by simply passing $scope as an argument to a function in my factory. So it won't be the normal $scope, but $scope at the time the function in the factory is called.
app.controller('AppController', function($scope, AppService) {
$scope.getList = function(){
$scope.url = '/someurl'
// call to service to make rest api call to get data
AppService.getList($scope).then(function(res) {
// do some stuff
});
}
});
app.factory('AppService', function($http, $q){
var AppService = {
getList: function($scope){
return $http.get($scope.url).then(function(res){
return res;
});
},
}
return AppService;
});

Using angularjs JSONP when callback cant be defined

I'm attempting to use Angularjs to gather data from the USGS Earthquake feed. Typically you would need to tack ?callback=JSON_CALLBACK on to the end of the URL for Angular to use it, however the USGS feed does not recognize this option.
The URL I'm using is http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp and adding ?callback=JSON_CALLBACK (eg. http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp?callback=JSON_CALLBACK) returns a dataset wrapped in a function called eqfeed_callback.
Is there any way to use this data? I've got an eqfeed_callback function but it's not in scope which makes using Angular pointless.
Here's the code that I've got as it stands:
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
return data;
}
Is there any way to either get the data back into the scope, or get angular to use the eqfeed_callback function internally?
Another option would be defining the eqfeed_callback within the scope like this:
function QuakeCtrl($scope, $http) {
$scope.data = null;
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
window.eqfeed_callback = function(data) {
$scope.data = data
}
}
The only idea that comes to mind is (blech) to use a global, and then to manually trigger an Angular update, e.g.:
var callback$scope;
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
callback$scope = $scope;
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
if (callback$scope) {
// 1. Use callback$scope here
// 2. Set callback$scope to undefined or null
// 3. Trigger an Angular update (since it won't be automatic)
}
}
Not pretty, but...
Expanding on #MichaelVanRijn's answer:
In order to keep the "global peace", define the global function when you need it and nullify it right after.
.controller('QuakeCtrl', function($window, $scope, $http) {
$scope.get_quakes = function() {
$window.eqfeed_callback = function(data){
console.log("quake data", data)
};
// load your jsonp data
$http.jsonp('http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp')
.then(function(success) {
console.log(success);
$window.eqfeed_callback = null;
}, function(fail) {
console.log(fail);
$window.eqfeed_callback = null;
})
}
})

Categories