problem Description
I am dealing with an optional variable called ByteRange. For this purpose I added 2 different URLs in $resource.
When I did that I got the following error:
Message:
Error in parsing: "tools/test/retrieve/retrieve.service.js", Line 24: Duplicate data property in object literal not allowed in strict mode
Details:
domain: [object Object]
domainThrown: true
I understood that I can't have 2 gets. Is there another way to deal with optional variables in Javascript?
source code
(function () {
'use strict';
angular
.module('MyApp')
.factory('retrieve', retrieveObject);
retrieveObject.$inject = ['$resource'];
function retrieveObject($resource) {
this.resource = $resource('api/tools/test', {}, {
'list': {
method: 'GET',
url: 'api/tools/test/list/:chain/:path/:dataCenter'
},
'get': {
method: 'GET',
url: 'api/tools/test/get/:chain/:dataCenter/:path/:byteRange'
},
'get': {
method: 'GET',
url: 'api/tools/test/get/:chain/:dataCenter/:path'
},
});
return this;
}
})();
Okay this is not perfect but it must help you, probably use regex instead of replace is a better idea.
function resolveUrl(url, params) {
let resolvedUrl = url;
for(let i in params) {
resolvedUrl = resolvedUrl.replace(i, params[i]);
}
return resolvedUrl;
}
let params = {
':chain': 'aaa',
':dataCenter': 'bbb',
':path': 'ccc',
':byteRange': '',
};
let result = resolveUrl('api/tools/test/get/:chain/:dataCenter/:path/:byteRange', params);
console.log(result);
// Output : "api/tools/test/get/aaa/bbb/ccc/"
params = {
':chain': 'aaa',
':dataCenter': 'bbb',
':path': 'ccc',
':byteRange': 'yyy',
};
result = resolveUrl('api/tools/test/get/:chain/:dataCenter/:path/:byteRange', params);
console.log(result);
// Output : "api/tools/test/get/aaa/bbb/ccc/yyy"
Related
I have a javascript object which is structured like the following:
var object = {
item1: {
name: 'item1',
details: {
name: 'detail1',
warn_lvl: 1
},
check: function(res) {
console.log('Warn lvl of item1 = '+//access var?)
}
}
}
In my console.log() from my check() function, I would like to print the warn_lvl of item1, but I can't figure out how to access it.
I tried several things with this, but nothing which work.
What is the proper way to access this var?
Edit
To be more precise (sorry, my mistake), I call the check function from another Javascript file, like this (only relevant parts):
var fetchMetrics = function (config, metrics) {
Object.keys(metrics).forEach(function(section) {
var metric = metrics[section];
doRequests(metric, section);
};
var doRequests = function(metric, section) {
$.ajax({
dataType: "json",
url: metric.url,
data: metric.params,
var result;
if (!query_result.length) {
result = state_retriever.RESULT_UNKNOWN
} else {
result = metric.check(query_result);
}
};
If you're calling check via object.item1.check("res arg here"), then you can use this.details.warn_lvl:
var object = {
item1: {
name: 'item1',
details: {
name: 'detail1',
warn_lvl: 1
},
check: function(res) {
console.log('Warn lvl of item1 = ' + this.details.warn_lvl)
}
}
};
object.item1.check();
But you've said you're calling check a different way. You can reliably access it via object.item1.details.warn_lvl regardless of how you call check:
var object = {
item1: {
name: 'item1',
details: {
name: 'detail1',
warn_lvl: 1
},
check: function(res) {
console.log('Warn lvl of item1 = ' + object.item1.details.warn_lvl);
}
}
};
var c = object.item1.check;
c();
I'm trying to move all the business logic from my controller to the factory, but I'm having some trouble passing fields data.
factory.js
app.factory("Quote", function ($resource) {
// TODO: this shouldn't start with /en/
var quoteStatus = [];
var quoteLanguage = [];
var Quote = $resource("/en/quote/api/quote/:id", {}, {
retrieve: {
method: 'GET',
params: {},
isArray: true
},
query: {
method: 'GET',
params: {},
isArray: true,
url: '/en/quote/api/quote/'
},
fields: {
method: 'GET',
url: '/en/quote/api/quote/fields/ '
},
update: {
method: 'PATCH',
},
});
Quote.fields().$promise.then(function (fields) {
var tempObj = [];
for (key in fields.status) {
// must create a temp object to set the key using a variable
tempObj[key] = fields.status[key];
quoteStatus.push({
value: key,
text: tempObj[key]
});
}
for (key in fields.language) {
// must create a temp object to set the key using a variable
tempObj[key] = fields.language[key];
quoteLanguage.push({
value: key,
text: tempObj[key]
});
}
//$scope.addLanguage($scope.language);
Quote.status = quoteStatus;
Quote.language = quoteLanguage;
});
return Quote;
});
controller.js
$scope.quoteStatus = Quote.status;
However this is not working since $scope.quoteStatus is undefined. What am I missing?
Thanks in advance.
You can't expect async operation to behave in synchronous way.
Basically when controller inject Quote in its factory function that time Quote service object gets created & then calls Quote.fields(). hen you ask Quote.status inside a controller will always return undefined. You are not maintaining promise anywhere so that controller will come to know that the data is ready or not.
I think you should introduce $q.when flag there to check the Quote.fields() operation completed or not & then do get the desired variable there.
For implementing above mention thing you need to store the promise of Quote.fields() call somewhere in service. like below
var quoteFieldsPromise = Quote.fields().$promise.then(function (fields) {
/// the code will be same here
};
Then add new method which will hold of quoteFieldsPromise promise object and return the value of quoteStatus & quoteLanguage.
var getQuoteDetails = function(){
$q.when(quoteFieldsPromise).then(function(){
return { quoteStatus: Quote.quoteStatus, quoteLanguage: Quote.quoteLanguage };
})
}
But the way you have returned whole Quote object, which only has $resource object which needs to be changed. I mean to say that the getQuoteDetails method which I've created can not be return with Quote object. So I'd rather rather refactor service to below.
Service
app.factory("Quote", function($resource, $q) {
// TODO: this shouldn't start with /en/
var quoteStatus = [], //kept private if needed
quoteFieldsPromise,
quoteLanguage = [];//kept private if needed
var QuoteApi = $resource("/en/quote/api/quote/:id", {}, {
//inner code is as is
});
//preserve promise of .fields() call
quoteFieldsPromise = Quote.fields().$promise.then(function(fields) {
//inner code is as is
//below lines are only changed.
Quote.status = quoteStatus;
Quote.language = quoteLanguage;
});
var getQuoteDetails = function() {
return $q.when(quoteFieldsPromise).then(function() {
return {
quoteStatus: quoteStatus,
quoteLanguage: quoteLanguage
};
})
};
return {
QuoteApi: QuoteApi,
getQuoteDetails: getQuoteDetails
};
});
Controller
Quote.getQuoteDetails().then(function(quoteDetails){
$scope.quoteStatus = quoteDetails.quoteStatus;
$scope.quoteStatus = quoteDetails.quoteLanguage;
});
I've created a basic AngularJS app that consumes Yelp's API and am having trouble using $httpProvider.interceptors to parse my response.
Here is my app:
var app = angular.module("restaurantList", []);
My yelpAPI service (not pictured) authenticates my API request and generates a HTTP request. I then output the data received to the Web Console like so:
app.controller("mainCtrl", ["$scope", "yelpAPI", function ($scope, yelpAPI) {
$scope.restaurants = [];
yelpAPI.get(function (data) {
$scope.restaurant = data;
console.log($scope.restaurant);
});
}]);
Here is data from my request:
Object {region: Object, total: 37, businesses: Array[20]}
I want the array located in the businesses property. So, I figured it would be a good idea to use $httpProvider.interceptors to parse the objects found, in the Object.businesses array.
Here is what $httpProvider.interceptors looked like, when I made my initial request:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function () {
return {
response: function (response) {
return response;
}
}
});
});
Here is what $httpProvider.interceptors looks like now:
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
response: function(response) {
var old_response = response.businesses,
new_response = [];
for (var i = 0; i < old_response.length; i++) {
var obj = old_response[i],
new_obj = {
restaurant_name: obj.name,
phone_number: obj.display_phone,
yelp_rating: obj.rating,
reservation_url: obj.reservation_url
};
new_response.push(new_obj);
}
return new_response;
}
}
});
});
Now, I'm receiving an error that says TypeError: Cannot read property 'businesses' of undefined. Is there something I'm overlooking?
EDIT #1
I used console.log(response) inside the interceptor to print my response and found that response.businesses should actually be response.data.businesses. Which resolves my error but now my $http call returns undefined. Any idea what my new problem could be?
EDIT #2
app.factory("yelpAPI", function($http, nounce) {
return {
get: function(callback) {
var method = "GET",
url = "http://api.yelp.com/v2/search";
var params = {
callback: "angular.callbacks._0",
oauth_consumer_key: "my_oauth_consumer_key",
oauth_token: "my_oauth_token",
oauth_signature_method: "HMAC-SHA1",
oauth_timestamp: new Date().getTime(),
oauth_nonce: nounce.generate(),
term: "American",
sort: 2,
limit: 20,
radius_filter: 4000,
deals_filter: true,
actionlinks: true
};
var consumerSecret = "my_consumer_secret",
tokenSecret = "my_token_secret",
signature = oauthSignature.generate(method, url, params, consumerSecret, tokenSecret, {
encodeSignature: false
});
params["oauth_signature"] = signature;
$http.jsonp(url, {
params: params
}).success(callback);
}
}
});
In return angular wait object with {data : }:
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
response: function(res) {
var old_response = res.businesses,
new_response = [];
for (var i = 0; i < old_response.length; i++) {
var obj = old_response[i],
new_obj = {
restaurant_name: obj.name,
phone_number: obj.display_phone,
yelp_rating: obj.rating,
reservation_url: obj.reservation_url
};
new_response.push(new_obj);
}
return {data : new_response};
}
}
});
});
Return as {data : new_response}
Emir helped me resolve this issue but essentially, whenever you use $httpProvider.interceptors you have to update response.data and return the entire response object (i.e. not just a new array, as I did) because $http selects the data property for you.
I have an array with 3 Objects, however the second Object does not have an array inside the 'Data' Object.
I need to do an ng-repeat for each 'Artist' in the correct order, however the second object is causing issues. How would I combine each Object together?
In my Factory, I set up a call to receive three response from three different API. I set a promise for each one so they come in a the exact order I call them.
FACTORY
.factory('timeline', function($http, $q) {
var promise1 = $http({
method: "GET",
url: "http://api.example.com/last/3/?limit=3"
});
var promise2 = $http({
method: "GET",
url: "http://api.example.com/current/3/"
});
var promise3 = $http({
method: "GET",
url: "http://api.example.com/next/3/?limit=3"
});
return {
data: $q.all([promise1, promise2, promise3])
}
})
In my controller, I get the response like so.
[
Object
config
data: [Array 3]
-0: Object
artist : 'Artist'
title : 'Title'
-1: Object
-2: Object
,
Object
config
data: Object
artist : 'Artist'
title : 'Title
,
Object
config
data: [Array 3]
-0: Object
artist : 'Artist'
title : 'Title'
-1: Object
-2: Object
]
CONTROLLER
My Attempt to filter using Underscore.
.controller('StationCtrl', function($scope, $stateParams, $http, timeline) {
timeline.data.then(function(musicData) {
var row = [];
for (var i = 0; i < musicData.length; i++) {
var data = _.filter(musicData[i].data, function(x){
row.push(x);
})
}
})
})
My Goal eventually if possible would be to combine everything in order
Object
data: [Array 7]
-0: Object
-1: Object
-2: Object
-3: Object
-4: Object
-5: Object
-6: Object
,
I am still trying to figure out how to work with Objects & Arrays, any help/tips would be great.
This is a simple approach of how you can solve your problem without underscore. You just need to check whether your data is an object or an array.
var arr = [
{ data: [{ artist: 'Artist' }, { artist: 'Artist2' }]},
{ data: { artist: 'Artist3' } },
{ data: [{ artist: 'Artist4' }]}
];
var flattened = [];
arr.forEach(function (el) {
if(Array.isArray(el.data)) {
flattened = flattened.concat(el.data);
} else {
flattened.push(el.data);
}
});
See example on jsbin.
Ideally, I think you should send an array only for 2nd object with its length as 1. If the API is not in your control i.e. 3rd party or anything else then we can look forward to solve the issue in other way.
You could strip out the underscore and just do a nested for:
.controller('StationCtrl', function($scope, $stateParams, $http, timeline) {
timeline.data.then(function(musicData) {
var row = [];
var dataElement;
var i;
var j;
for (i = 0; i < musicData.length; i++) {
dataElement = musicData[i].data;
if(typeof dataElement === 'object') {
row.push(dataElement)
} else if(typeof dataElement === 'array') {
for(j = 0; j < dataElement.length; j++) {
row.push(dataElement[j]);
}
}
}
})
})
I want to create a Custom Action on Resource, which will process the results received from the server.
angular.module('problem', ['ngRoute', 'ngResource'])
.factory('Abc', function ($resource) {
return $resource('api/abc/:abcId', {abcId: '#id'}, {
customQuery: {
method: "GET",
isArray: true,
interceptor: {
response: function (response) {
// some operations that manipulate data based od response
response.data = [5, 6]; // for simplifity
console.log("manipulation finished"); // log is saved
return response;
}
}
}
}
);
})
;
But when I use the Custom Action, I get the unmodified results instead of processed.
Here is the code showing the expected behavior (and in comments relevant errors):
describe('Abc', function () {
beforeEach(module('problem'));
var $httpBackend;
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
})
});
it('should return converted array when customQuery called', inject(function (Abc) {
$httpBackend
.expectGET('api/abc')
.respond([
{id: 'uid1', name: 'name1'},
{id: 'uid2', name: 'name2'},
{id: 'uid3', name: 'name3'},
{id: 'uid4', name: 'name4'}
]);
var result = Abc.customQuery();
$httpBackend.flush();
expect(result.length).toBe(2); // fails with "Expected 4 to be 2."
expect(result[0]).toBe(5); // fails with "Expected { id : 'uid1', name : 'name1' } to be 5."
expect(result[1]).toBe(6); // fails with "Expected { id : 'uid2', name : 'name2' } to be 6."
}));
});
Maybe what you need is to add a new transformResponse transformation, you can easily do that like this (remember to inject $http):
transformResponse: $http.defaults.transformResponse.concat([
function (data, headersGetter) {
return data.objects
}
Difference is, result will be substituted by response.resource, while the return value of the interceptor is the value that is resolving result.$promise
thank you for the "interceptor" idea!
in my opinion
response.data = [5, 6]; // for simplifity
return response;
should return a response-object, with a property-array "data", so
result.length;
should fail.
For manipulating the results of a resource response better use response.resource instead of response.data - there are the real REST objects (with CRUD methods)