I'm using the selenium javascript webdriver, and I'd like to make a general 'getVisibleElement' function. I saw this version in the webdriver source code:
var link = driver.findElement(firstVisibleLink);
function firstVisibleLink(driver) {
var links = driver.findElements(By.tagName('a'));
return webdriver.promise.filter(links, function(link) {
return links.isDisplayed();
}).then(function(visibleLinks) {
return visibleLinks[0];
});
}
Is there a way that I can generalize this such that I can pass in any css selector as parameter and get just the first visible version of it? I've been having issues with 'invalid locator' and 'undefined is not a function' errors the ways i have been trying.
Page:
var webdriverUtils = require('../../utils/extendWebdriver_utils');
function contactCardPage(){
var elements = Object.create({}, {
viewToggle: { get: function(){
return webdriverUtils.getFirstVisibleElement('[name="viewToggle"]');
}
}
this.toggleView = function(){
return elements.viewToggle.click();
}
});
module.exports = new contactCardPage();
WebdriverUtils:
function webdriverUtils(){
this.getFirstVisibleElement = function(selectorString){
var elements = session.driver.findElements(webdriver.By.css(selectorString));
return webdriver.promise.filter(elements, function(el) {
return el.isDisplayed();
}).then(function(visibleElement) {
return visibleElement[0];
});
}
});
module.exports = new webdriverUtils();
Spec:
var expect = require('chai').expect;
var contactCardPage = require('../../pageObjects/contactCardPage.js');
describe('Contacts', function () {
it('Can be expanded', function(done){
contactCardPage.toggleView().then(function(){
//Expect here for making sure expanded elements are now displayed
done();
});
});
});
Currently this gives me an 'Invalid Locator' error.
Is there any way to pass in a selector and get just the visible elements this way?
Related
I've been trying to code up a search engine using angular js, but I can't copy one array to another. When I initiate the the code (in the service.FoundItems in the q.all then function) new array(foundArray) shows up as an empty array. I searched up how to copy one array to another and tried that method as you can see, but it isn't working. Please help, here is the code, and thank you.
P.S. if you need the html please tell me.
(function () {
'use strict';
angular.module('narrowDownMenuApp', [])
.controller('narrowItDownController', narrowItDownController)
.service('MenuSearchService', MenuSearchService)
.directive('searchResult', searchResultDirective);
function searchResultDirective() {
var ddo = {
templateUrl: 'searchResult.html',
scope: {
items: '<'
},
};
return ddo
}
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
console.log(menu.displayResult);
};
}
MenuSearchService.$inject = ['$http', '$q'];
function MenuSearchService($http, $q) {
var service = this;
service.getMatchedMenuItems = function(name, searchTerm) {
var deferred = $q.defer();
var foundItems = [];
var result = $http({
method: "GET",
url: ('https://davids-restaurant.herokuapp.com/menu_items.json'),
params: {
category: name
}
}).then(function (result) {
var items = result.data;
for (var i = 0; i < items.menu_items.length; i++) {
if (searchTerm === ""){
deferred.reject("Please enter search term");
i = items.menu_items.length;
}
else if (items.menu_items[i].name.toLowerCase().indexOf(searchTerm.toLowerCase()) ==! -1){
foundItems.push(items.menu_items[i].name)
deferred.resolve(foundItems);
}else {
console.log("doesn't match search");
}
}
});
return deferred.promise;
};
service.FoundItems = function (searchTerm, name) {
var searchResult = service.getMatchedMenuItems(name, searchTerm);
var foundArray = [];
$q.all([searchResult])
.then(function (foundItems) {
foundArray = foundItems[0].slice(0);
foundArray.reverse();
})
.catch(function (errorResponse) {
foundArray.push(errorResponse);
});
console.log(foundArray);
return foundArray;
};
};
})();
If the goal of the service.FoundItems function is to return a reference to an array that is later populated with results from the server, use angular.copy to copy the new array from the server to the existing array:
service.FoundItems = function (searchTerm, name) {
var foundArray = [];
var searchPromise = service.getMatchedMenuItems(name, searchTerm);
foundArray.$promise = searchPromise
.then(function (foundItems) {
angular.copy(foundItems, foundArray);
foundArray.reverse();
return foundArray;
})
.catch(function (errorResponse) {
return $q.reject(errorResponse);
})
.finally(function() {
console.log(foundArray);
});
return foundArray;
};
I recommend that the promise be attached to the array reference as a property named $promise so that it can be used to chain functions that depend on results from the server.
Frankly I don't recommend designing services that return array references that are later populated with results. If you insist on designing it that way, this is how it is done.
I tried the $promise thing that you recommended. I was wondering how you would get the value from it ie the array.
In the controller, use the .then method of the $promise to see the final value of the array:
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
̶c̶o̶n̶s̶o̶l̶e̶.̶l̶o̶g̶(̶m̶e̶n̶u̶.̶d̶i̶s̶p̶l̶a̶y̶R̶e̶s̶u̶l̶t̶)̶;̶
menu.displayResult.$promise
.then(function(foundArray) {
console.log(foundArray);
console.log(menu.displayResult);
}).catch(function(errorResponse) {
console.log("ERROR");
console.log(errorResponse);
});
};
}
To see the final result, the console.log needs to be moved inside the .then block of the promise.
Titus is right. The function always immediately returns the initial value of foundArray which is an empty array. The promise is executed asynchronously so by the time you are trying to change foundArray it is too late. You need to return the promise itself and then using .then() to retrieve the value just like you are currently doing inside the method.
From just quickly looking at your code I think you made have a simple error in there. Are you sure you want
foundArray = foundItems[0].slice(0);
instead of
foundArray = foundItems.slice(0);
This is probably a noob JavaScript question, but I'm looking to know if my solution to a problem I am having is 'correct'
I have created the following sample application that recreates my error:
Firstly in index.js
var processor = require('./fileProcessor/processor.js');
var container = {
source: "source.txt",
destination: "destination.txt"
};
new processor().process(container);
I create my container object which has the name of the source file and the name of the destination file. This is passed into the process function of the processor:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
this.process = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, this.process);
} else {
file.write(container, this.process);
}
}
};
};
As you can see this calls the read and write functions passing in the container and the process function as the callback, the fileProcessor looks like this:
var fs = require('fs');
module.exports = function() {
this.read = function(container, callback) {
fs.readFile(container.source, function (err, data) {
if(err) throw err;
container.body = data;
callback(container);
});
};
this.write = function(container, callback) {
fs.writeFile(container.destination, container.body, function(err) {
if(err) {
return console.log(err);
}
container.finished = true;
callback(container);
});
};
};
In simple terms the processor calls file.read, which reads the file and calls back into the process function, which then calls the write function. However at the end of the write function an error is thrown:
callback(container);
^
TypeError: object is not a function
Obviously when passing in this.process to file.write(container, this.process); the this isn't the this I intend it to be!
If I update my processor by adding a processFunction variable:
var fileProcessor = require('./fileProcessor.js');
module.exports = function Processor() {
var processFunction = function(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
this.process = function(container) {
processFunction(container);
};
};
Everything works fine. Is this a good way to do this or is there a better solution?
I think this is a fine way to do it. There is one possible modification that you might make. Since you are creating a new name in your scope just for the purpose of recursing, you could just name your function and refer to it by its name inside of the function.
module.exports = function Processor() {
this.process = function processFunction(container) {
var file = new fileProcessor();
if(container.finished === undefined) {
if(container.body === undefined) {
file.read(container, processFunction);
} else {
file.write(container, processFunction);
}
}
};
};
Then you can avoid creating a name (processFunction) that will be visible outside the function.
Take a look here for reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function#Named_function_expression
I have a Node.js application that, upon initialisation, reads two tables from an SQL database and reconstructs their relationship in memory. They're used for synchronously looking up data that changes (very) infrequently.
Problem: Sometimes I can't access the data, even though the application reports successfully loading it.
Code:
constants.js
module.exports = {
ready: function () { return false; }
};
var log = sysLog('core', 'constants')
, Geo = require('../models/geo.js');
var _ready = false
, _countries = []
, _carriers = [];
function reload() {
_ready = false;
var index = Object.create(null);
return Geo.Country.find().map(function (country) {
var obj = country.toPlainObject()
, id = obj.id;
delete obj.id;
index[id] = obj;
return Object.freeze(obj);
}).then(function (countries) {
log.debug('Loaded ' + countries.length + ' countries');
_countries = countries;
return Geo.Carrier.Descriptor.find().map(function (carrier) {
var obj = carrier.toPlainObject();
if (obj.country) {
obj.country = index[obj.country];
}
return Object.freeze(obj);
}).then(function (carriers) {
log.debug('Loaded ' + carriers.length + ' carriers');
_carriers = carriers;
});
}).finally(function () {
_ready = true;
});
}
reload().catch(function (err) {
log.crit({ message: 'Could not load constants', reason: err });
process.exit(-42);
}).done();
module.exports = {
reload : reload,
ready : function () { return _ready; },
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
utils.js
var log = sysLog('core', 'utils')
, constants = require('./constants');
module.exports = {
getCountryByISO: function(iso) {
if (!iso) {
return;
}
if ('string' != typeof iso) {
throw new Error('getCountryByISO requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
switch (iso.length) {
case 2:
return _.findWhere(constants.countries(), { 'iso2' : iso.toUpperCase() });
case 3:
return _.findWhere(constants.countries(), { 'iso3' : iso.toUpperCase() });
default:
throw new Error('getCountryByISO requires a 2 or 3 letter ISO code');
}
},
getCarrierByCode: function(code) {
if (!code) {
return;
}
if ('string' != typeof code) {
throw new Error('getCarrierByCode requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
return _.findWhere(constants.carriers(), { 'code' : code });
},
getCarrierByHandle: function(handle) {
if (!handle) {
return;
}
if ('string' != typeof handle) {
throw new Error('getCarrierByHandle requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
return _.findWhere(constants.carriers(), { 'handle' : handle });
}
};
Use case
if (data.handle) {
carrier = utils.getCarrierByHandle(data.handle);
if (_.isEmpty(carrier)) {
throw new InternalError('Unknown carrier', { handle: data.handle });
}
}
What's going on: All errors are logged; as soon as I see an error (i.e. "Unknown carrier") in the logs, I check the SQL database to see if it should've been recognised. That has always been the case so far, so I check the debug log to see if data was loaded. I always see "Loaded X countries" and "Loaded Y carriers" with correct values and no sign of "Could not load constants" or any other kind of trouble.
This happens around 10% of the time I start the application and the problem persists (i.e. didn't seem to go away after 12+ hours) and seems to occur regardless of input, leading me to think that the data isn't referenced correctly.
Questions:
Is there something wrong in constants.js or am I doing something very obviously wrong? I've tried setting it up for cyclical loading (even though I am not aware of that happening in this case).
Why can't I (sometimes) access my data?
What can I do to figure out what's wrong?
Is there any way I can work around this? Is there anything else I could to achieve the desired behaviour? Hard-coding the data in constants.js is excluded.
Additional information:
constants.reload() is never actually called from outside of constants.js.
constants.js is required only in utils.js.
utils.js is required in app.js (application entry); all files required before it do not require it.
SQL access is done through an in-house library built on top of knex.js and bluebird; so far it's been very stable.
Versions:
Node.js v0.10.33
underscore 1.7.0
bluebird 2.3.11
knex 0.6.22
}).finally(function () {
_ready = true;
});
Code in a finally will always get called, regardless of if an error was thrown up the promise chain. Additionally, your reload().catch(/* ... */) clause will never be reached, because finally swallows the error.
Geo.Country.find() or Geo.Carrier.Descriptor.find() could throw an error, and _ready would still be set to true, and the problem of your countries and carriers not being set would persist.
This problem would not have occurred if you had designed your system without a ready call, as I described in my previous post. Hopefully this informs you that the issue here is really beyond finally swallowing a catch. The real issue is relying on side-effects; the modification of free variables results in brittle systems, especially when asynchrony is involved. I highly recommend against it.
Try this
var log = sysLog('core', 'constants');
var Geo = require('../models/geo.js');
var index;
var _countries;
var _carriers;
function reload() {
index = Object.create(null);
_countries = Geo.Country.find().map(function (country) {
var obj = country.toPlainObject();
var id = obj.id;
delete obj.id;
index[id] = obj;
return Object.freeze(obj);
});
_carriers = _countries.then(function(countries) {
return Geo.Carrier.Descriptor.find().map(function (carrier) {
var obj = carrier.toPlainObject();
if (obj.country) {
obj.country = index[obj.country];
}
return Object.freeze(obj);
});
});
return _carriers;
}
reload().done();
module.exports = {
reload : reload,
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
constants.reload() is never actually called from outside of
constants.js.
That's your issue. constants.reload() reads from a database, which is an aysnchronous process. Node's require() is a synchronous process. At the time constants.js is required in utils.js and the module.exports value is returned, your database query is still running. And at whatever point in time that app.js reaches the point where it calls a method from the utils module, that query could still be running, resulting in the error.
You could say that requiring utils.js has the side-effect of requiring constants.js, which has the side-effect of executing a database query, which has the side-effect of concurrently modifying the free variables _countries and _carriers.
Initialize _countries and _carriers as unresolved promises. Have reload() resolve them. Make the utils.js api async.
promises.js:
// ...
var Promise = require('bluebird');
var countriesResolve
, carriersResolve;
var _ready = false
, _countries = new Promise(function (resolve) {
countriesResolve = resolve;
})
, _carriers = new Promise(function (resolve) {
carriersResolve = resolve;
});
function reload() {
_ready = false;
var index = Object.create(null);
return Geo.Country.find().map(function (country) {
// ...
}).then(function (countries) {
log.debug('Loaded ' + countries.length + ' countries');
countriesResolve(countries);
return Geo.Carrier.Descriptor.find().map(function (carrier) {
// ...
}).then(function (carriers) {
log.debug('Loaded ' + carriers.length + ' carriers');
carriersResolve(carriers);
});
}).finally(function () {
_ready = true;
});
}
reload().catch(function (err) {
log.crit({ message: 'Could not load constants', reason: err });
process.exit(-42);
}).done();
module.exports = {
reload : reload,
ready : function () { return _ready; },
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
utils.js
getCarrierByHandle: function(handle) {
// ...
return constants.carriers().then(function (carriers) {
return _.findWhere(carriers, { 'handle' : handle });
});
}
Use case:
utils.getCarrierByHandle(data.handle).then(function (carrier) {
if (_.isEmpty(carrier)) {
throw new InternalError('Unknown carrier', { handle: data.handle });
}
}).then(function () {
// ... next step in application logic
});
This design will also eliminate the need for a ready method.
Alternatively, you could call constants.reload() on initialization and hang all possibly-dependent operations until it completes. This approach would also obsolete the ready method.
What can I do to figure out what's wrong?
You could have analyzed your logs and observed that "Loaded X countries" and "Loaded Y carriers" were sometimes written after "Unknown carrier", helping you realize that the success of utils.getCarrierByHandle() was a race condition.
So I have this model :
'use strict';
var mongoose = require('mongoose');
var subscriberModel = function () {
var subscriberSchema = mongoose.Schema({
email: String
});
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
subscriberSchema.methods.validateSubscriber = function (subs, otherModel) {
var code = 0;
if (validateEmail(subs.email)) {
mongoose.model('Subscriber').findOne({'email': subs.email},
function(err, subscriber){
if (err) {
code = 2;
} else if (subscriber) {
code = 3;
} else {
subs.save(function(err){
if (err) {
code = 2;
}
code = 4;
});
}
});
} else {
code = 1;
}
};
return mongoose.model('Subscriber', subscriberSchema);
};
module.exports = new subscriberModel();
And I call it in another file that way :
var subscriberModel = require('../models/subscriber');
var subscriber = new subscriberModel({email: req.body.email.trim()});
var code = subscriber.validateSubscriber(subscriber, buyModel);
console.log('code = %s', code); // => displays "code = 0"
I guess it's a scope problem or something like that (because I don't use any callback -or maybe I should-) but I can't find nor the problem, nor the solution.
So, how can I get the returned code value ?
Thanks in advance.
[EDIT] : I found where the problem was and edited the code to explain it. So apparently my var is set in the callbacks of mongoose so now my question is; how do I wait for this callback to end as I need the result to display a message to the user in a webpage?
Okay so, I found the answer.
I edited my function validateSubscriber to send a callback: subscriberSchema.methods.validateSubscriber = function (subs, callback). And removed the otherModel occurences inside.
That way, when I call it in my other file I just have to do :
subscriber.validateSubscriber(subscriber, function(code){
buyModel.code = code;
console.log('code = %s', buyModel.code);
}
To get the code AND wait for the answer.
I hope it helped someone else.
What I have is simple CRUD operation. Items are listed on page, when user clicks button add, modal pops up, user enters data, and data is saved and should automatically (without refresh)be added to the list on page.
Service:
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).fail(getFailed);
},
addExerciseAndCategories: function(data, initialValues) {
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function(item) {
manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
});
saveChanges().fail(addFailed);
function addFailed() {
removeItem(items, item);
}
},
Controller:
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.queryItems = adminCrudService.querySucceeded(data);
var exerciseIds = _($scope.queryItems).pluck('ExerciseId').uniq().valueOf();
$scope.exerciseAndCategories = [];
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise : exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where($scope.queryItems, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
$scope.items.push(newItem);
});
$scope.$apply();
}
Here is how I add new data from controller:
adminCrudService.addExerciseAndCategories($scope.selectedCategories, { Name: $scope.NewName, Description: $scope.NewDesc });
So my question is, why list isn't updated in real time (when I hit save I must refresh page).
EDIT
Here is my querySuceeded
querySucceeded: function (data) {
items = [];
data.results.forEach(function(item) {
items.push(item);
});
return items;
}
EDIT 2
I believe I've narrowed my problem !
So PW Kad lost two hours with me trying to help me to fix this thing (ad I thank him very very very much for that), but unfortunately with no success. We mostly tried to fix my service, so when I returned to my PC, I've again tried to fix it. I believe my service is fine. (I've made some changes as Kad suggested in his answer).
I believe problem is in controller, I've logged $scope.items, and when I add new item they don't change, after that I've logged $scope.queryItems, and I've noticed that they change after adding new item (without refresh ofc.). So probably problem will be solved by somehow $watching $scope.queryItems after loading initial data, but at the moment I'm not quite sure how to do this.
Alright, I am going to post an answer that should guide you on how to tackle your issue. The issue does not appear to be with Breeze, nor with Angular, but the manner in which you have married the two up. I say this because it is important to understand what you are doing in order to understand the debug process.
Creating an entity adds it to the cache with an entityState of isAdded - that is a true statement, don't think otherwise.
Now for your code...
You don't have to chain your query execution with a promise, but in your case you are returning the data to your controller, and then passing it right back into some function in your service, which wasn't listed in your question. I added a function to replicate what yours probably looks like.
getAllIncluding: function(controllerAction, including) {
var query = breeze.EntityQuery.from(controllerAction).expand(including);
return manager.executeQuery(query).then(querySucceeded).fail(getFailed);
function querySucceeded(data) {
return data.results;
}
},
Now in your controller simply handle the results -
$scope.getAllExercisesAndCategories = function() {
adminCrudService.getAllIncluding("ExercisesAndCategories", "Exercise,ExerciseCategory")
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
// Set your object directly to the data.results, because that is what we are returning from the service
$scope.queryItems = data;
$scope.exerciseAndCategories = [];
Last, let's add the properties we create the entity and see if that gives Angular a chance to bind up properly -
_.forEach(data, function(item) {
var e = manager.createEntity("ExerciseAndCategory");
e.Exercise = addedExercise; e.Category: item.Category;
});
So I've managed to solve my problem ! Not sure if this is right solution but it works now.
I've moved everything to my service, which now looks like this:
function addCategoriesToExercise(tempdata) {
var dataToReturn = [];
var exerciseIds = _(tempdata).pluck('ExerciseId').uniq().valueOf();
var createItem = function (id, exercise) {
return {
ExerciseId: id,
Exercise: exercise,
ExerciseCategories: []
};
};
// cycle through ids
_.forEach(exerciseIds, function (id) {
// get all the queryItems that match
var temp = _.where(tempdata, {
'ExerciseId': id
});
// go to the next if nothing was found.
if (!temp.length) return;
// create a new (clean) item
var newItem = createItem(temp[0].ExerciseId, temp[0].Exercise);
// loop through the queryItems that matched
_.forEach(temp, function (i) {
// if the category has not been added , add it.
if (_.indexOf(newItem.ExerciseCategories, i.ExerciseCategory) < 0) {
newItem.ExerciseCategories.push(i.ExerciseCategory);
}
});
// Add the item to the collection
dataToReturn.push(newItem);
});
return dataToReturn;
}
addExerciseAndCategories: function (data, initialValues) {
newItems = [];
var addedExercise = manager.createEntity("Exercise", initialValues);
_.forEach(data, function (item) {
var entity = manager.createEntity("ExerciseAndCategory", { ExerciseId: addedExercise._backingStore.ExerciseId, CategoryId: item.CategoryId });
items.push(entity);
newItems.push(entity);
});
saveChanges().fail(addFailed);
var itemsToAdd = addCategoriesToExercise(newItems);
_.forEach(itemsToAdd, function (item) {
exerciseAndCategories.push(item);
});
function addFailed() {
removeItem(items, item);
}
}
getAllExercisesAndCategories: function () {
var query = breeze.EntityQuery.from("ExercisesAndCategories").expand("Exercise,ExerciseCategory");
return manager.executeQuery(query).then(getSuceeded).fail(getFailed);
},
function getSuceeded(data) {
items = [];
data.results.forEach(function (item) {
items.push(item);
});
exerciseAndCategories = addCategoriesToExercise(items);
return exerciseAndCategories;
}
And in controller I have only this:
$scope.getAllExercisesAndCategories = function () {
adminExerciseService.getAllExercisesAndCategories()
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
$scope.items = data;
$scope.$apply();
}