So I'm new in Angular, and I've looked over various other solutions, but no one seemed to work for me. My app needs to take some data from mongodb database and show it to the client. The thing is I get
Error: [$resource:badcfg] Error in resource configuration for action query. Expected response to contain an array but got an object
Here is my SchoolCtrl.js on the client
app.controller('SchoolsCtrl', function($scope, SchoolResource) {
$scope.schools = SchoolResource.query();
});
Here is my ngResource
app.factory('SchoolResource', function($resource) {
var SchoolResource = $resource('/api/schools/:id', {id: '#id'}, { update: {method: 'PUT', isArray: false}});
return SchoolResource;
});
This is my SchoolsController on the server
var School = require('mongoose').model('School');
module.exports.getAllSchools = function(req, res, next) {
School.find({}).exec(function(err, collection) {
if(err) {
console.log('Schools could not be loaded: ' + err);
}
res.send(collection);
})
};
I tried adding IsArray: true, tried adding [] after 'SchoolResource' in the resource, tried changing routes, nothing worked. I wanted to see what actually is returned, that the query complained is not array, so I turned it to string and this was the result:
function Resource(value) { shallowClearAndCopy(value || {}, this); }
I have no idea why it returns a function. Can anyone help me?
That error message usually means your server is returning JSON representing a single object, such as:
{"some": "object", "with": "properties"}
when Angular is expecting JSON representing an array, like:
[ {"some": "object", "with": "properties"}, {"another": "object", "with": "stuff"} ]
Even if there is only a single result, query expects JSON for an array:
[ {"a": "single", "result": "object"} ]
You can verify this by simply loading your API call into the browser and checking it out. If there aren't square brackets around the whole JSON response, it isn't an array.
It happened to me too, but then I printed the object in the console and found out that there was something like this:
{ options:{....},results:[ ...the Array I was looking for... ]}
so all you need to do there is
res.send(collection.results);
I hope this helps
Related
I am creating a google chrome extension, and when I make a get request to this web page https://www.rightmove.co.uk/property-for-sale/find.html?locationIdentifier=REGION%5E27675&maxBedrooms=2&minBedrooms=2&sortType=6&propertyTypes=&mustHave=&dontShow=&furnishTypes=&keywords=, I get the webpage HTML as a response which is what I want (the website I am requesting info from does not have an API and I cannot web scrape for reasons too long to explain here). This response comes in the form of a string. When I attempt to split this string at a certain point, bis_skin_checked, I am returned an array of length 1, meaning that there was no match and nothing has been split. But when I look at the string returned it has it included.
I have tried things like removing spaces and carriage returns but nothing seems to be working. This is my GET request code:
function getNewPage(url) {
let returnedValue = fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html',
},
})
.then(response => response.text())
.then(text => {
return text
})
return returnedValue
}
I then go on to resolve the promise which is returnedValue:
let newURL = getHousePrices(currentUrl) // Get Promise of new page as a string
newURL.then(function(value) { // Resolve promise and do stuff
console.log(value.split('bis_skin_checked').length)
})
And then work with the string which looks like this: (I have attached an image as I cannot copy the text from the popup)
Image Link To API Request return
I'm assuming you want to get the home values, given certain search parameters.
Scraping raw text is usually not the way to go. I took a look at the site and saw it uses uniform network requests that you can modify to capture the data you need directly instead of scraping the raw HTML.
I built a solution that allows you to dynamically pass whatever parameters you want to the getHomes() function. Passing nothing will use the default params that you can use as a baseline while you try to adjust the request for any additional modifications/use cases.
Install the below solution and run getHomes() from the service worker.
Here's a brief video I made explaining the solution: https://vimeo.com/772275354/07fd1025ed
--- manifest.JSON ---
{
"name": "UK Housing - Stackoverflow",
"description": "Example for how to make network requests and mimic them in background.js to avoid web scraping raw text",
"version": "1.0.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"*://*.rightmove.co.uk/*"
]
}
--- background.js ---
async function getHomes(passedParams) {
const newParams = passedParams ? passedParams : {}; // set to an empty object if no new params passed - avoid error in object.entries loop.
// starts with default params for a working request, you can then pass params to override the defaults to test new requests.
var params = {
"locationIdentifier": "REGION%5E27675",
"maxBedrooms": "2",
"minBedrooms": "1",
"numberOfPropertiesPerPage": "25",
"radius": "0.0",
"sortType": "6",
"index": "0",
"viewType": "LIST",
"channel": "BUY",
"areaSizeUnit": "sqft",
"currencyCode": "GBP",
"isFetching": "false"
}
Object.entries(params).forEach(([key, value]) => {
// we iterate through each key in our default params to check if we passed a new value for that key.
// we then set the params object to the new value if we did.
if (newParams[key]) params[key] = newParams[key];
});
const rightMoveAPISearch = `https://www.rightmove.co.uk/api/_search?
locationIdentifier=${params['locationIdentifier']}
&maxBedrooms=${params['maxBedrooms']}
&minBedrooms=${params['minBedrooms']}
&numberOfPropertiesPerPage=${params['numberOfPropertiesPerPage']}
&radius=${params['radius']}
&sortType=${params['sortType']}
&index=${params['index']}
&viewType=${params['viewType']}
&channel=${params['channel']}
&areaSizeUnit=${params['areaSizeUnit']}
¤cyCode=${params['currencyCode']}
&isFetching=${params['isFetching']}
`.replace(/\s/g, ''); // removes the whitespaces we added to make it look nicer / easier to adjust.
const data = await
fetch(rightMoveAPISearch, {
"method": "GET",
})
.then(data => data.json())
.then(res => { return res })
if (data.resultCount) {
console.log('\x1b[32m%s\x1b[0m', 'Request successful! Result count: ', parseInt(data.resultCount));
console.log('All data: ', data);
console.log('Properties: ', data.properties);
}
else console.log('\x1b[31m%s\x1b[0m', `Issue with the request:`, data)
return data
}
Hope this is helpful. Let me know if you need anything else.
According to the react-native-fcm package you can include a custom nested object within the data object for a FCM messaging payload.
according to this post by the package author
Like this:
var payload = {
data: {
custom_notification: {
title: 'title',
body: 'body',
priority: 'high',
id: 'id',
group: 'group'
}
}
};
This is for the purpose of receiving heads up notifications in all app states, which will not happen if you do just a notification payload or data payload.
When I implement this in my cloud function I get the following error:
Error: Messaging payload contains an invalid value for the "data.custom_notification" property. Values must be strings.
So I'm at a loss as to how others can be using this successfully?
I wonder if there's some issue going on with my environment or something as the following test payload which was given to me by firebase support (and is in the docs) errors out:
var payload = {
"to":"FCM_TOKEN",
"data": {
"type":"MEASURE_CHANGE",
"body": "test body",
"title": "test title",
"color":"#00ACD4",
"priority":"high",
"id": "id",
"show_in_foreground": true
}
};
I get the following error:
Error sending message stringify: {"code":"messaging/invalid-payload","message":"Messaging payload contains an invalid \"to\" property. Valid properties are \"data\" and \"notification\"."}
Been at this for days so hopefully I can get a bit of help on this.
Thanks in advance!
So I realised just now (after days of searching) that the package react-native-fcm is using a different send method than admin.messaging().sendToDevice(token, payload, options). I had been using that for a while now and didn't realize that it was not actually what was intended to be used with this library or atleast in this scenario. Mainly because everything was working fairly well using admin.messaging() up until I wanted heads up notifications in all app states.
The other method is like this
sendData(token) {
let body = {
"to": token,
"data":{
"title": "Simple FCM Client",
"body": "This is a notification with only DATA.",
"sound": "default",
"click_action": "fcm.ACTION.HELLO",
"remote": true
},
"priority": "normal"
}
this._send(JSON.stringify(body), "data");
}
_send(body, type) {
let headers = new Headers({
"Content-Type": "application/json",
"Content-Length": parseInt(body.length),
"Authorization": "key=" + FirebaseConstants.KEY
});
fetch(API_URL, { method: "POST", headers, body })
.then(response => console.log("Send " + type + " response", response))
.catch(error => console.log("Error sending " + type, error));
}
You can use nested objects within the data object using this method. The documentation is not super clear on this unfortunately, I didn't even realise there was an example until now. Of course this could have just been me.
When using the data message payload, it is stated to use key value pairs that are String, so what you could do is have the value of your custom_notification as JSON String by enclosing them in " ".
For the sample payload provided, are you actually using the FCM_TOKEN in the to parameter? You're supposed to replace it with an actual token.
I'm following along on the Angular JS Tutorial and I was wondering if there is an alternate approach to how I'm modifying it.
Currently, I am returning data with a factory that is defined as such:
angular.
module('core.card').
factory('Card', ['$resource',
function($resource){
return $resource('cards/:cardId.json', {}, {
query: {
method: 'GET',
params: {cardId: 'cards'},
isArray: true
}
});
}
]);
This is all good and working, as cards.json has all of the cards available and that's exactly what I want to return.
The method that they're describing, such as dealing with a RESTful service, assumes that there are multiple other specific JSON files that could get returned based on the route. I understand how to use that with an actual service, but let's say I wanted to alter the returned JSON data before it gets bound to my module so I don't have a bunch of extra data that I don't need?
Lets say /cards/foo.json contains something like this:
[{
"id": "foo",
"name": "Bar",
"img": "foobar.png",
"unnecessaryKey": "remove me"
}]
But where would I write a function that only returns:
[{
"id": "foo",
"name": "Bar",
"img": "foobar.png"
}]
Would I assign it in the same place where the query function is, such as:
...
return $resource('cards/:cardId.json', {}, {
query: {
method: 'GET',
params: {cardId: 'cards'},
isArray: true
},
alterReturnedData: {
// doStuffToFormatData
}
});
...
Or would it be best to just modify it in my Component as I'm doing now?
function alterReturnedData(data){
// doStuffToFormatData
}
var unmodified = Card.get({cardId:'foo'}, function(){
self.data = alterReturnedData(unmodified);
});
I just feel like it'd be better to return the data from the Service I actually want to the Component Controller instead of having a lot of logic in there to skew it around.
Is my approach OK to run this function in the Controller?
Or is it best to alter it in the Service, and how would I do so?
I am wondering how can I parse Array of JSON objects in NodeJS?
I want to post JSON array to the server, and be able to use the received array as a regualar JavaScript array.
Thanks in advance.
This is my front-end part that I am converting Array to String using stringify function
document.getElementById("sendJson").addEventListener("click", function () {
$.post("/echo", JSON.stringify(QuestionsArray), function (data) {
alert(data);
});
})
This my back-end part that I am trying to convert Array of JSON object to Array
app.post('/echo', function (req, res) {
var Array = JSON.parse(JSON.stringify(req.toString()));
res.end(Array[0]["QuestionText"].toString());
});
This is Array that I am trying to sent to the server:
[
{
"QuestionText":"What is your Name",
"QuestionType":1
},
{
"QuestionText":"Where are you from",
"QuestionType":2,
"ChoiceList":[
"US",
"UK"
]
},
{
"QuestionText":"Are you married",
"QuestionType":3,
"ChoiceList":[
"Yes",
"No"
]
}
]
Here is the source code
In your app.js:
var bodyParser = require("body-parser");
...
app.use(bodyParser.urlencoded({extended: true}));
Then you can just use req.body to get the posted values:
app.post('/echo', function (req, res) {
var Array = req.body.data;
res.end(Array[0]["QuestionText"].toString());
});
In front-end, don't do any stringifying:
$.post("/echo", {data: QuestionsArray}, function (data) {
alert(data);
});
I'll try to explain this. First of all, you are crating a json string on the client.
JSON.stringify(QuestionsArray)
Then on the server, you are doing the same again:
JSON.stringify(req.toString()) // this is not needed
Then you parse the double stringifyed json string to a javascript object:
JSON.parse(JSON.stringify(req.toString()))
So now you actually have to parse it twice :). If you just stringify it on the server as you are now, and just call:
var arr = JSON.parse(req.toString());
You will get a javascript object that you can access like this:
res.end(arr[0].QuestionText.toString());
Have a look at this jsFiddle and open your developer tools. Look at the console when it runs and you will see where the problem is: example
You may actually send the JSON directly to server.
$.ajax({
url: "/echo",
type: 'POST',
data: JSON.stringify(QuestionsArray),
processData: false,
contentType: 'application/json'
}).success(function (data) {
alert(data);
});
And in node.js, use bodyParser.json to get it back.
app.use(bodyParser.json({}));
app.post('/echo', function (req, res) {
var array = req.body;
res.end(array[0]["QuestionText"].toString());
});
By the way, do not use Array as variable name because Array represent the Array class used by JavaScript and your code has overwritten it.
I'm trying to send a JSON file to my Node.js app via Protocol.HTTP. The file is correctly sent, but I can't access to an array. Here is the relevant code :
Client side
(...)
var vectorProtocol = new OpenLayers.Protocol.HTTP({
url: '/getcoords',
format: vectorFormat,
readWithPOST: true,
params: {
"code_company": "8501",
"data_company": [
{
"origin": "2013P00109",
"type": "LJ",
"naf": "5610A",
},
{
"origin": "2013P00110",
"type": "FJ",
"naf": "5481"
}
]
}
});
(...)
Server side, I try to build an array with only the "origin" field of my array "data_company":
function getCoords(params, callback) {
var arrOrigin = params.data_company.map(function(d) {
return d.origin;
});
(...)
}
And I get this error :
TypeError: Object [object Object] has no method "map"
It seems that my "data_company" is not recognized as an array but as an object. I tried to JSON.parse(params) before but I get another error :
SyntaxError: Unexpected token o
Anyway, I'm stuck. Do you have any clue to help me to solve this ?
Thanks