Angular Js how to refresh a new factory instance - javascript

I am using a factory to get list of folders and display it in the front end. Also in the front end i have form where i can add new folder to the existing list. After adding a folder i want to refresh my factory instance and display the updated folder list.
// factory
angular.module('myapp').factory('archiveService', ['$http', 'ApiUrl', function($http, ApiUrl) {
var archiveService = function() {
this.repos = [];
this.busy = false;
this.page = 0;
};
archiveService.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = ApiUrl.url + '/folders/?page=' + this.page;
$http.get(url).then(function(res) {
console.log(res);
this.repos = res.data;
if (items.repos == 0) {
return;
}
this.page += 1
this.busy = false;
}.bind(this)).catch(function(data) {
}.bind(this));
};
return {
archiveService: archiveService,
}
}]);
// this is my controller
angular.module('myapp').controller('archiveModalController', ['$rootScope', '$scope','archiveService', function($rootScope, $scope, archiveService) {
// I want to refresh this and show new data on update
$scope.archivelist = new archiveService.archiveService();
}])
I would like to know how can i refresh so i can get the new updated data
$scope.archivelist = new archiveService.archiveService();

Angular services follow the singleton pattern meaning the instantiation of a class is restricted to a single object.
Also, since you are using a factory:
angular.module('myapp').factory('archiveService', [<dependencies>, function () {
function nextPage() {
//code here
);
return {
nextPage: nextPage
}
}]);
Then in your controller you simply:
archiveService.nextPage();
Seeing the variables in place I believe nextPage could simply receive the page as a parameter and since repos is an array I guess you meant to add the new fetched data to that array?
Which would be:
this.repos.push(res.data;) instead of this.repos = res.data;
Point is, each time you want to request new data you should call the correct service/factory method from the controller.
So at your controller init, you only need:
(function init() {
$scope.archivelist = archiveService.nextPage();
})();
Although like I stated, you should probably have an initial value like nextPage(1) and from there send the desired page you want to the factory method to be handled correctly.

Related

angular().service() wont get a update in the object I pass as parameter

Well fist time here, so sorry for the mess or the long text.
I pass $scope.alertsensorconfigs as tableData to my service, and wen I delete a item, my $scope.alertsensorconfigs changes but wen It try to reload the table I see that tableData had not changed.
Detailed explanation:
I have a site that uses NgTable to create a lot of tables in different pages, but all the logic to convert and filter the data that I want to show in the page was coded in the pages and it repeated a lot of times but it was working, so i decided to thy make a service that do all that.
So my problem is, I created the service, it i almost all working like before, but I notices that wen I delete a item from the table, it deletes the data but keep showing it on the table until I reload the page.
I notice that it is because even that my object on the page have changed after delete, wen it is inside my service the object apear to be the unchanged one, since I passed my object as parameter to my service i took it wold pass like a reference to the object and wen it changes on the page it wold apear changed in my service, but looks like it is not the case, it looks like wen it calls for the fist time my service it makes a copy of my object and wen it calls again it wont get the updated one.
My page code.
$scope.funcFilter = function(pfilter){
return function (item) {
return item.name.toUpperCase().indexOf(pfilter) >= 0
||
item.criticality1.toString().toUpperCase().indexOf(pfilter) >= 0
||
item.criticality2.toString().toUpperCase().indexOf(pfilter) >= 0
|| $filter('translate')
(item.active.toString().toUpperCase()).indexOf(pfilter) >= 0;
}
}
$scope.searchTable = {filter: ""};
$scope.tableParams =
NgTableDataService.getGenericTableParams($scope.alertsensorconfigs,
$scope.funcFilter, $scope.searchTable.filter)
Delete function in my page:
AlertSensorConfig.remove({id: obj.id}, function () {
$scope.alertsensorconfigs.splice($scope.alertsensorconfigs.indexOf(obj), 1);
$scope.tableParams.reload().then(function(data) {
if (data.length === 0 && $scope.tableParams.total() > 0) {
$scope.tableParams.page($scope.tableParams.page() - 1);
$scope.tableParams.reload();
}
});
},
My service:
angular.module('control-room').service('NgTableDataService',
function ($filter, NgTableParams, Session) {
var session = Session.get();
this.getGenericTableParams = function(tableData, funcFilter, searchTableFilter){
return new NgTableParams({
count: session.user.tablePagination,
filter: searchTableFilter
}, {
counts: rowsPerPageTemplate,
getData: function (params) {
if (params.filter() == ''){
var pfilter = '';
}else{
var pfilter = params.filter().filter.toUpperCase();
}
let filteredData = params.filter() ? $filter('filter')(tableData, funcFilter(pfilter)) : tableData;
if (!!filteredData && filteredData.length >= 0) {
params.total(filteredData.length);
var rowsPerPageTemplateWithAllData = rowsPerPageTemplate.slice();
var isFound = rowsPerPageTemplateWithAllData.some(function (element) {
return element === filteredData.length;
});
params.settings().counts = rowsPerPageTemplateWithAllData.filter(item=>item<filteredData.length)
if (filteredData.length >= 5){
params.settings().counts.push(filteredData.length);
}
rowsPerPageTemplateWithAllData.push(filteredData.length + (isFound ? 1 : 0));
if (session.user.tablePagination >= params.settings().counts[params.settings().counts.length-1]){
params.settings().count = params.settings().counts[params.settings().counts.length-1];
}else{
params.settings().count = session.user.tablePagination;
}
if (params.total() <= params.count()) {
params.page(1);
}
var x = $filter('orderBy')(filteredData, params.orderBy());
var y = x.slice((params.page() - 1) * params.count(), params.page() * params.count());
return y;
} else {
params.settings().counts = [];
return null;
}
}
});
};
And the ng-table function that reload the table after delete:
this.reload = function() {
var self = this,
pData = null;
settings.$loading = true;
prevParamsMemento = angular.copy(createComparableParams());
isCommittedDataset = true;
if (self.hasGroup()) {
pData = runInterceptorPipeline($q.when(settings.getGroups(self)));
} else {
pData = runInterceptorPipeline($q.when(settings.getData(self)));
}
log('ngTable: reload data');
var oldData = self.data;
return pData.then(function(data) {
settings.$loading = false;
errParamsMemento = null;
self.data = data;
event even when data === oldData
ngTableEventsChannel.publishAfterReloadData(self, data, oldData);
self.reloadPages();
return data;
}).catch(function(reason){
errParamsMemento = prevParamsMemento;
// "rethrow"
return $q.reject(reason);
});
};
there is some way to make sure the object I pas as parameter to my service is updated every time I call it, like some binding?
I think i manage to solve it.
The NgTable have his own reload(), but it wasn't working because what it does is, get the current filteredData and replace with the one tableData, but the tableData on my service was only set wen it was called the fist time and wasn't getting updated wen I updated the $scope.alertsensorconfigs.
So what I manage to do after a lot of headache, was to create in my service:
A var serviceTableData that receives the $scope.alertsensorconfigs wen the service is called so it is global in my service.
And my own reload() function that I replaced in every place that my controller used the NgTable reload().
My service reload() wen called receives as parameter from the controller the $scope.alertsensorconfigs after the controller delete or edit the item, then it will set the serviceTableData to the updated data received as param and then calls the NgTable reload().
So it ended like this:
angular.module().service('NgTableDataService',
function ($filter, NgTableParams, Session, MY_CONSTANTS) {
var session = Session.get();
var serviceTableParam = null;
var serviceTableData = null;
this.reloadTableData = function (tableDataAtt){
serviceTableData = tableDataAtt;
serviceTableParam.reload().then(function(data) {
if (data.length === 0 && serviceTableData.total() > 0) {
serviceTableParam.page(serviceTableParam.page() - 1);
serviceTableParam.reload();
}
return this.tableData = tableDataAtt;
});
};
this.getGenericTableParams = function(tableData, searchTableFilter, funcFilter){
serviceTableData = tableData;
return serviceTableParam = new NgTableParams({
// etc...
Edit : I misunderstood the question
According to your comment what you want is to update a variable in your service and retrieve it later.
What you can do is :
In your service
angular
.module('app.core')
.factory('', dataservice);
dataservice.$inject = ['$http'];
function dataservice($http) {
return {
myFunction: yourServiceFunction,
myData: myData
};
var myData = [];
function yourServiceFunction(tableData) {
// DO SOMETHING
myData = tableData;
// DO SOMETHING
}
}
Like this you can access to myData in your service or in your controller which is updated in your function with the value of tableData that you passed when you called your function.
And if you want to update myData in your service everytime $scope.alertsensorconfigs changes, add a $watch to this variable and create a function in your service who is called whenever your $scope value change and which simply update myData in your service.
I hope that I answered to the right question this time :)
.
.
.
Here after : old response which do not answer to the question
there is some way to make sure the object I pas as parameter to my service is updated every time I call it, like some binding?
Yes.
I am sorry to not answer with your code but it is a bit hard to get into it.
The simpliest way is to do something like this :
$scope.alertsensorconfigs =
yourService.yourMethod($scope.alertsensorconfigs, param2, param3).tableData;
While your services' method does something like :
yourMethod = function (tableData, param2, param3) {
// DO SOMETHING
return { tableData : tableDataAfterModification, elem2 : 'elem2', elem3 : 'elem3' };
};

Array not showing up in View after being generated from function in AngularJS

So I have this simple function that groups products into their respective categories. You can also see I'm calling the function within a timeout.
Problem is, even though the console.log() works like it should, I can't see anything in my view.
function sortByCategories(){
var temp = [];
angular.forEach(admin.categories, function(category, value){
angular.forEach(admin.products, function(product, value){
angular.forEach(product.categories, function(prodCat, value){
if(prodCat.text == category.text)
{
if(!temp[category.text])
{
temp[category.text] = [];
}
temp[category.text].push(product);
}
})
})
})
admin.sortedProducts = temp;
}
$timeout(function(){
sortByCategories();
console.log(admin.sortedProducts); // This shows me what I expect.
}, 3500);
My html in my view is simply just <pre>{{admin.sortedProducts | json}}</pre>
All I see in my view is []
What am I missing?
UPDATE
I moved this function into a service and placed it as a resolve in my route. I am still facing the same issue. Here is what I have.
.factory('Products', function($rootScope, $timeout, $q, fbUrl, $firebaseArray){
return {
sortByCategories: function() {
var temp = [];
var deferred = $q.defer();
var ref = new Firebase(fbUrl);
ref.once('value', function(snapshot){
var categories = snapshot.child('categories').val();
var products = snapshot.child('products').val();
angular.forEach(categories, function(category, value) {
angular.forEach(products, function(product, value){
angular.forEach(product.categories, function(prodCat, value){
if(prodCat.text == category.url)
{
if(!temp[category.url])
{
temp[category.url] = [];
}
temp[category.url].push(product);
}
})
});
});
deferred.resolve(temp);
})
return deferred.promise;
}
}
})
And then in my route I have this in a resolve:
...
resolve: {
categorySortedProducts : function(Products){
return Products.sortByCategories();
}
}
...
Then that's loaded in my controller
.controller('productsCtrl', function($scope, categorySortedProducts){
var admin = this;
$scope.sortedProducts = categorySortedProducts;
console.log($scope.sortedProducts);
...
})
(^^ The console.log works by the way!)
Then finally in my html view I simply have this:
<pre>{{sortedProducts | json}}</pre>
I do have the controller loaded in my route and it is loaded as admin. (productsCtrl as admin).
UPDATE 2
So it looks like the console.log() is kind of weird. This is what it looks like:
Array[0]
category-one[12]
categroy-two[15]
Is that normal, for the parent to show Array[0]? Just trying to find issues.
I changed in my promise the following.
var temp = [];
to
var temp = {};
And now everything shows up in my View. I'm guessing it's because my data isn't setup sequentially like [] is typically used for.
Your view is looking on the scope but your admin variable is locally-defined and not attached to Angular's scope.
Change your code:
$scope.admin.sortedProducts = temp;
So why did it work in a console.log and not in your view? Because the view is looking on the controller's scope. But your console.log was looking in the local namespace which doesn't need to be on Angular's scope.

AngularJS service inheritance issues

I have a base service which looks like this:
.service('BaseImageService', ['$q', 'ApiHandler', 'UploadService', function ($q, api, uploadService) {
// Get our api path
var apiPath = 'logos';
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Create our service
var service = {
// Update our api path
updateApiPath: function (path) {
// Set the api path
apiPath = path;
},
// Gets a list of logos
list: function (t) {
if (t) {
console.log(apiPath);
}
// Get our logo
return api.get(apiPath);
},
// Get a single logo
get: function (id) {
// Get our logo
return api.get(apiPath, { id: id });
},
// Create our logo
save: function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
},
// Deletes our logo
delete: function (id) {
// Delete our logo
return api.delete(apiPath, { id: id });
},
// Prepare for editing
prepareForEditing: function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
}
};
// Return our service
return service;
}])
and then I have a few services which "inherit" this service, like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'images';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'logos';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'playerText';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
I thought that this was working fine. But I have this page that calls all 3 services (ImageService, LogoService and PlayerTextService) sequentially. On the first view of the page everything is fine, if I navigate away and then come back the images service actually pulls back thing from the player text service. Now I know this is because of services being singletons but I am not sure how to fix my issue.
Can anyone give me a hand?
I have added a codepen with an attempted solution:
http://codepen.io/r3plica/pen/ONVBJO
Attempt 2
http://codepen.io/r3plica/pen/jqPeMQ?editors=1010
The solution you tried doesn't work because the BaseService is a singleton. So you inject exactly the same instance into all three service registration functions and all of them configure the same object. So basically the last one wins.
Looks like you want to have separate services with different configurations. This is what providers are used for. They allow a two-step process of building a service instance. Please see this great Stackoverflow answer on the topic:
AngularJS: Service vs provider vs factory
For reference, Restangular is a library that needs to achieve exactly the same as you want. You could use this as a blueprint and see how Restangular handles this requirement:
https://github.com/mgonto/restangular#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one
Please be aware that these concepts are based on AngularJS 1 and you need to handle this differently when you want to use AngularJS 2 later on.
After a lot of messing around; I finally found a solution adapting this bit of code
My base service looks like this:
.factory('BaseImageService', ['$q', 'ApiHandler', 'UploadService', 'vectorExtensions', function ($q, api, uploadService, vectorExtensions) {
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(BaseImageService.apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(BaseImageService.apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
model.fileName = response.fileName;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Addes a property to the image array to state if they are vector images or not
var _addVectorProperties = function (images) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var image = _addVectorProperty(images[i]);
}
// Return our images
return images;
};
// Adds a property to the image to state if it is vector or not
var _addVectorProperty = function (image) {
// Vector flag
var vector = false;
// Get our file extension
var parts = image.path.split('.');
// If we have any parts
if (parts.length) {
// Get our last part
var ext = parts[parts.length - 1],
index = vectorExtensions.indexOf(ext);
// If our extension exists in our vector array
if (index > -1) {
// Change our vector property
vector = true;
}
}
// Update our image with the new property
image.vector = vector;
// Return our image
return image;
};
// Create our service
var BaseImageService = function (path) {
// Set our apiPath
this.apiPath = path;
// Update our api path
this.updateApiPath = function (path) {
// Set the api path
apiPath = path;
};
// Gets a list of logos
this.list = function () {
// Get our logo
return api.get(this.apiPath).then(_addVectorProperties);
};
// Get a single logo
this.get = function (id) {
// Get our logo
return api.get(this.apiPath, { id: id }).then(_addVectorProperty);
};
// Create our logo
this.save = function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
};
// Deletes our logo
this.delete = function (id) {
// Delete our logo
return api.delete(this.apiPath, { id: id });
};
// Set our active image
this.setActive = function (images, image) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var current = images[i];
// Set whether we are active or not
current.active = image.id === current.id ? true : false;
}
};
// Prepare for editing
this.prepareForEditing = function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
};
};
// Return our service
return BaseImageService;
}])
and the child services look like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('images');
// Return our new service
return child;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('logos');
// Return our new service
return child;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('playerText');
// Return our new service
return child;
}])
That works fine.
Yes it append because it's singleton. You have to perform inheritance if you want to do this.
Here is a code that i use and append to angular :
angular.baseResourceServiceMaker = function(service){
return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint',
function($injector, $resource,TypeService, $http, _, BackEndpoint){
//skipping not interesting code
// sample fields to inherits
this.sample = "test";
this.sampleFN = function(){[...]}
// THE line that does the magic
$injector.invoke(service, this);
}
Now time of usage
.service('MyService',angular.baseResourceServiceMaker(['$http', function($http){
// overriding fields
this.sample="inherits";
this.sampleFN = function(){[...]}
}]));
So basically what do we have here ? A function baseResourceServiceMaker which represent a generic particular service. The $injector that call the service we want to instantiate and set the scope to the generic service, so the this on the child class will bind to the same reference than the generic class. The generic service will be instantiated as many times you call it, no confict.
I personally use this code for the resource module of angular to define some basic methods having a custom serializer / deserializer than handle dates and some other stuffs. In your case the baseResourceServiceMaker will be your baseImageService with ending with the $injector.invoke(service, this).
EDIT : found a link with something probably cleaner : AngularJS service inheritance
It becomes very easy if you use (or switch to) ES6 or TypeScript.
export class Base {
// . . .
}
Then:
import {app} from '../app';
import {Base} from './base';
export class Child extends Base {
// . . .
}
app.service('child', Child);
From your Attempt 1:
your BaseService looking for apiPath at global level,
while angular preparing dependency your last dependency is ImageService,
first it will prepare dependency then it will execute list() method,
all ways your apiPath will refer global level declared value, i.e apiPath = 'images'; from step2
Solution: use this operator in front of apiPath under BaseService and list().
working Plunker

Can you use the same view and angular controller without overwriting the data?

I have an application using a PDF viewer-pdf angularjs made, this viewer uses a html template
with its own controller angularjs, this viewer want to use it in different catalogs (ie it is
global) viewer, I work well when I open one PDF catalog and position, but when you open another
I unlocked the other loads the pdf in the previous window and no where open, ie data overwrites me
Is there any way to resolve this, open the same viewer in several catalogs without affecting others?
If you make the PDF viewer a directive you should be able to achieve this because directives operate within their own scope.
That's why I created a service as shown:
angular.module('myModule',['pdf']).service('pdfService', function () {
var pdfURL = "";
var defineURL = function (url) {
pdfURL = url;
};
var getURL = function () {
return pdfURL;
};
return {
defineURL: defineURL,
getURL: getURL
};
}).controller('DocControlador', ['$scope', 'pdfService', function ($scope, pdfService) {
$scope.pdfName = '';
$scope.pdfUrl = pdfService.getURL();
$scope.scroll = 0;
$scope.loading = 'cargando...';
$scope.getNavStyle = function (scroll) {
if (scroll > 100) return 'pdf-controls fixed';
else return 'pdf-controls';
}
$scope.onError = function (error) {
console.log(error);
}
$scope.onLoad = function () {
$scope.loading = '';
}
$scope.onProgress = function (progress) {
console.log(progress);
}
}])
and here I pass I assign values to the variables of service from another controller:
pdfService.defineURL("\\Files\\1\\Charts\\" + $scope.fileName);

Set factory variable from state

I am using factories to get feed data for different categories in wordpress using the Rest-API and I am wondering if I am making a complete mess of it.
I have around 13 different categories that I am making just a basic call to that look like this:
.factory('Articles1', function ($http) {
var articles1 = [];
storageKey = "articles1";
function _getCache() {
var cache = localStorage.getItem(storageKey );
if (cache)
articles = angular.fromJson(cache);
}
return {
all: function () {
return $http.get("http://www.example.com/wp-json/posts?filter[category_name]=category1").then(function (response) {
articles1 = response.data;
console.log(response.data);
return articles1;
});
},
get: function (articleId) {
if (!articles1.length)
_getCache();
for (var i = 0; i < articles1.length; i++) {
if (parseInt(articles1[i].ID) === parseInt(article1Id)) {
return articles1[i];
}
}
return null;
}
}
})
there is a separate call for each of the 13 categories to get articles.
Is there a way to set the [category_name] to a variable, possibly the state name or something to that I can just make 1 call to wordpress and the url will rely on what state the user has chosen? I have been trying to learn angular and from what I have seen I am sure there must be a more elegant way of doing something like this?
Inject the Angular $location provider and you will have access to every part of your current URL (you can also use it to listen to the state changes). Then just build the URL to Wordpress dynamically:
.factory('Articles1', function ($http, $location) {
// ...
return {
all: function () {
var currentCategory = $location.path().split('/')[1]; // Assuming the category is part of the path and at the second place
var wpUrl = 'http://www.example.com/wp-json/posts?filter[category_name]=' + currentCategory;
return $http.get(wpUrl).then(function (response) { // ...

Categories

Resources