Hope, my question itself, conveys what I am look for.
Will put the words in detail
1. Created the Module.
var ang = angular.module('myApp', []);
I have a controller called controller1, and includes the 'campaign' factory.
//controllerone.js
ang.controller('controller1', function(campaign){
$scope.campaigns = new campaign();
//Here the whole campaign object is displayed with data, refer the Image 1 attached
console.log($scope.campaigns);
});
ang.factory('campaign', function($http){
var campaign = function(){
this.timePeriodList = buildTimePeriodList();
...
...
this.campaignList = [];
};
Campaigns.prototype.fetchCampaigns = function() {
//Some service call to load the data in this.campaignList
};
});
Now trying to call the same campaign factory in the second controller, getting only the object structure, not getting the data.
//controlertwo.js
ang.controller('controller2', function(campaign){
$scope.campaigns = new campaign();
//Here only the campaign object structure is displayed, but no data for campaignList, ref image 2 attached
console.log($scope.campaigns);
});
Since, factory service is a singleton object, I was expecting for same result as I got in controllerone.js,
Image 1:
Image 2:
In angular factory I would suggest a different approach.Try not to attach anything to the Prototype of a object. Instead you can create an object in the scope of your angular factory, attach what you want to it and return it. For example:
ang.factory('campaign', function ($http) {
var campaign = {};
campaign.method1 = function () {
//..
}
campaign.campaignList = [];
//...
campaign.fetchCampaigns = function () {
//Some service call to load the data in this.campaignList
};
});
//Than in your controllers if campaign is injected you can use it that way:
ang.controller('controller2', function (campaign) {
campaign.fetchCampaigns();// This will fill the list and it will remain filled when other controllers use this factory...
console.log(compaign.campaignList);
});
Anything you dont want to be exposed out of the factory simply do not attach it to the campaign object.
Create campaign in a service. Eg-campaignService
Updated code---
var ang = angular.module('myApp', []);
ang.service('campaignService', function($scope){
var campaign = function(){
this.timePeriodList = buildTimePeriodList();
...
...
this.campaignList = [];
}
return campaign;
});
ang.controller("controller1", ["campaignService",
function($scope, $rootScope, campaignService){
$scope.campaigns=campaignService.campaign();
}
]);
ang.controller("controller2", ["campaignService",
function($scope, $rootScope, campaignService){
$scope.campaigns=campaignService.campaign();
console.log($scope.campaigns);
}
]);
Related
How can I access $scope data from view to my factory in angularjs? I can access $scope.items from my controller, but when I need to use it in my factory to use the data and generate a pdf I cannot access it.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
$scope.link = "http://localhost:3450/loading.html";
testFactory.all().then(
function(res){
$scope.link = res;
},
function(err){
console.log(err);
}
);
})
.factory('testFactory', function($q){
var pdfInfo = {
content: [
//data should be here...
]
};
var link = {};
function _all(){
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc){
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I used factory when I click the generate button from my view, it will wait until the pdf is generated. Coz when I did not do it this way before, I need to click the button twice just to get the pdf generated.
You can just pass the data to your factory as a
function parameter.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
var pdfInfo = {
content: $scope.items
};
$scope.link = "http://localhost:3450/loading.html";
testFactory.all(pdfInfo).then(
function(res) {
$scope.link = res;
},
function(err) {
console.log(err);
}
);
})
.factory('testFactory', function($q) {
var link = {};
function _all(pdfInfo) {
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc) {
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I did it. I forgot to send the $scope.items to my factory. So what i did is I added testFactory.all($scope.items) in my controller instead of just plain testFactory.all().
Then in my factory,
I used function _all(value), so I can used the values passed by the views through controller. I am not sure if this is the proper way, but it works. Please suggest good practice if you have.
It is a bad practice to move around $scope to other services, as they may change it and effect your controller logic. It will make a coupling between controllers to other services.
If your factory requires data from the controller, it is better to just pass those parameters to the factory's function.
EDIT: I see you managed to do that, and yes - passing $scope.items is the preferred way (and not, for example, passing $scope).
I have one service for handling data, and one for logic.
app.service('DataService', function() {
this.stuff = [false];
this.setStuff = function(s){
this.stuff = angular.copy(s);
}
});
The data service has a set function and a data property.
app.service('LogicService', function(DataService, $http) {
DataService.setStuff(["apple", "banana"]);
$http.get("./data.json").then(function(res){
DataService.setStuff(res.data.stuff);
});
});
I am assigning a property of the data service to the controller for binding to the DOM.
app.controller('MainCtrl', function($scope, DataService, LogicService ) {
$scope.message = "Hello, World!";
$scope.stuff = DataService.stuff;
//This is the only way I could get it to work, but isn't this JANKY?
//$scope.$watch(
// function(){
// return DataService.stuff
// },
// function(n,o){
// $scope.stuff = n;
// })
})
If I 'seed' the data service when the logic service is instantiated, and then later update it following an $http call, the DOM reflects the 'seeded' or initial value, but does not update.
Is there something fundamental I am missing in my understanding of the digest loop?
If I add a $watch function in my controller, all is well, but this seems yucky.
//FIXED//
#scott-schwalbe 's method of using Object.asign() works nicely, preserves my original structure, and is one line.
this.setStuff = function(s){
Object.assign(this.stuff, s);
}
Working Plunker
(sorry for titlegore)
If your data property is an object and is binded to the scope, then the scope will update whenever the object changes as long as you don't dereference it (eg data = x). Are you reassigning data object on the $http call?
An alternative to your current code to keep the reference using Object.assign
app.service('DataService', function() {
this.stuff = [false];
this.setStuff = function(s){
Object.assign(this.stuff, s);
}
});
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, DataService) {
$scope.message = "Hello, World!";
//Get stuff data from your service, this way you stuff lives in your service
//And can be accessed everywhere in your app.
//It also makes your controller thin. Which is the top priority
$scope.stuff = DataService.getStuff();
//Or async
DataService.getStuffAsync()
.then(function(val){
$scope.asycStuff = val;
});
this.clickFromAButton = function(){
DataService.setStuff(["apple", "banana"]);
};
});
app.service('DataService', function() {
this.stuff = [false];
this.asyncStuff;
this.setStuff = function(s){
this.stuff = angular.copy(s);
};
this.getStuff = function(){
return this.stuff;
};
this.getStuffAsync = function(){
//If i already fetched the data from $http, get it from the service.
return this.asyncStuff || $http.get("./data.json").then(function(res){
//When i fetch it for the first time I set the data in my service
this.asyncStuff = res.data;
//and I return the data
return res.data;
});
};
});
This is a good 'pattern' to follow ;)
Instead of putting "stuff" on scope. Put your DataService object on scope.
app.controller('MainCtrl', function($scope, DataService, LogicService ) {
$scope.message = "Hello, World!";
$scope.DataService = DataService;
//$scope.stuff = DataService.stuff;
HTML
<body ng-controller="MainCtrl">
{{DataService.stuff}}
</body>
The $interpolate service will automatically places a $watch on DataService.stuff. Thus there is no need to do it inside your controller.
The DEMO on PLNKR.
I'm having some basic problems with angular at the moment. I just wrote a service that reads the temperature of an external device in an interval of five seconds. The service saves the new temperature into a variable and exposes it via a return statement. This looks kind of this (simplified code):
angular.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = 0,
requestTemp = function() {//some logic here},
onResponseTemp = function (temp) {
m_temp = temp;
},
//some other private functions and vars ...
foo = bar;
//request new temperture every 5s, calls onResponseTemp after new data got received
$interval(requestTemp, 5000);
return {
getTemp = function(){return m_temp;}
}
}]);
I use a controller to fetch the data from the service like this:
angular.controller("tempCtrl", ["$scope", "tempService", function ($scope, tempService) {
$scope.temp = tempService.getTemp();
}]);
In my view I access it like this:
<div ng-controller="tempCtrl">
<p>{{temp}}</p>
</div>
But I only get 0 and the value never changes. I have tried to implement a custom Pub/Sub pattern so that on a new temperature my service fires an event that my controller is waiting for to update the temperature on the scope. This approach works just fine but I'm not sure if this is the way to go as angular brings data-binding and I thought something this easy had to work by itself ;)
Help is really appreciated.
Please see here http://jsbin.com/wesucefofuyo/1/edit
var app = angular.module('app',[]);
app.service("tempService", ["$interval", function ($interval) {
//revealing module pattern
var m_temp = {
temp:0,
time:null
};
var requestTemp = function() {
m_temp.temp++;
m_temp.time = new Date();
};
var startTemp = function() {
$interval(requestTemp, 3000);
};
return {
startTemp :startTemp,
m_temp:m_temp
};
}]);
app.controller('fCtrl', function($scope,tempService){
$scope.temp = tempService;
$scope.temp.startTemp();
});
You are returning a primitive from your service, if you want to update an primative you need to reftech it. You should return an object, as on object is passed by reference, you get the actual values in your controller.
do this in your service:
return m_temp;
And this in your controller:
$scope.temp = tempService;
and your view will update as soon as the service gets updated.
Does this help you?
i think you should use $interval in controller ot in service
$interval(tempService.getTemp(), 5000);
I am building a subscriber/observer pattern for displaying data in realtime for my angular app.
The observer is built with a factory injected into the angular controller and whose role is to fetch data and update it. The basic code structure can he found in this fiddle: http://jsfiddle.net/ctrager/67QR7/3/
var myApp = angular.module('myApp', [])
.factory('MyFactory', [function () {
var Collection = {};
Collection.isLoaded = 0;
Collection.data = [1, 2];
Collection.username = "corey and eric";
Collection.update = function () {
Collection.data.push(new Date())
}
Collection.replace = function () {
// If you do Collection.data = []
// here you are doing the same thing
// as the empty collection bug. I can't
// tell you EXACTLY why this confuses angular
// but I'm 99% sure it's the same phenomenon
Collection.data = [new Date()]
}
Collection.replace_fixed = function () {
// This works
Collection.data.length = 0
Collection.data.push(new Date())
}
return Collection;
}])
function MyCtrl($scope, MyFactory) {
$scope.name = 'Eric';
$scope.items = MyFactory.data;
$scope.replace = function(){
console.log("replace")
MyFactory.replace()
//$scope.items = MyFactor.data;
}
$scope.replace_fixed = function(){
console.log("replace_fixed")
MyFactory.replace_fixed()
//$scope.items = MyFactor.data;
}
$scope.update = function(){
console.log("update")
MyFactory.update()
}
}
The factory (MyFactory) contains a collection (Collection.data). Any push (/splice) to that collection is reflected in the scope, but if I replace the entire collection (Collection.replace()) the change is no longer reflected in $scope. Any idea why?
This works:
http://jsfiddle.net/67QR7/4/
changed the thing stored on scope to be the factory instead of data. then the html repeat to do items.data.
So it looks like this is because you replaced the reference inside collection, but that doesn't change where $scope.items was pointing to.
So you are creating a reference to MyFactory.data from $scope.items. Angular puts a $watch on $scope.items and looks for changes. When you call MyFactory.replace, you change MyFactory.data, but $scope.items remains the same. So as far as your watch is concerned, nothing has happened.
You can fix this by using replace_fixed, or watch for changes to MyFactory.data. http://jsfiddle.net/KtB93/
$scope.MyFactory = MyFactory;
$scope.$watch("MyFactory.data", function(newData) {
console.log('myFactory.data changed');
$scope.items = newData;
});
Or alternatively (probably better), you can use a function as the watch expression so you don't have to plop MyFactory on the scope (http://jsfiddle.net/XAW54/1/):
$scope.$watch(function() {
return MyFactory.data;
}, function(newData) {
$scope.items = newData;
});
I am having a problem getting data from a service populated into my view. I have a service defined as such
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.nuke = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.nukes = data;
});
return nukeService.nukes;
};
return nukeService;
});
and my controller
function NavigationCtrl($scope, $http, nukeService){
/*$http.get('nukes/nukes.json').success(function(data) {
$scope.nukes = data;
});*/
$scope.nukes = nukeService.getNukes();
}
If I use the $http.get from the controller the data populates fine, however, if I try to call the data from the service, I get nothing. I understand that the query is asynchronous but I am having a hard time understanding how to populate the $scope variable once the data is returned. I could use $rootscope to broadcast an event and listen for it in the controller but this does not seem like the correct way to accomplish this. I would really appreciate any advice on how to do this the correct way.
I think this should solve your problem
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.data = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.data.nukes = data;
});
return nukeService.data;
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
$scope.data = nukeService.getNukes();
//then refer to nukes list as `data.nukes`
}
This is a problem with object reference.
when you calls nukeService.getNukes() you are getting a reference to a object a then your variable $scope.nukes refers that memory location.
After the remote server call when you set nukeService.nukes = data; you are not changing the object a instead you are changing nukeService.nukes from referencing object a to object b. But your $scope.nukes does not know about this reassignment and it still points to object a.
My solution in this case is to pass a object a with property data and then only change the data property instead of changing reference to a
This should be as follows. As mentioned by NickWiggill's comment, undefined will be assigned to nukeService.data if we do not return promise.
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
return $http.get('nukes/nukes.json');
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
nukeService.getNukes().then(function(response){
$scope.data = response.data;
});
}
What I do is that I expose the data straight from the service, and have a method which initializes this data. What is wrong with this?
Service:
app.factory('nukeService', function($scope, $http) {
var data = {};
data.nukes = [];
//Gets the list of nuclear weapons
var getNukes = function() {
$http.get('nukes/nukes.json').success(function(data) {
data.nukes = data;
});
};
// Fill the list with actual nukes, async why not.
getNukes();
return {
data : data
// expose more functions or data if you want
};
});
Controller:
function NavigationCtrl($scope, nukeService){
$scope.data = nukeService.data;
//then refer to nukes list as `$scope.data.nukes`
}