Derived attribute absent in JSON response in Sails.js - json

I'm writing an API for users in an example app. The api/models/User-file looks as follows:
module.exports = {
attributes: {
firstName: {
type: 'string',
required: true
},
lastName: {
type: 'string',
required: true
},
fullName: function () {
return this.firstName + ' ' + this.lastName;
}
}
};
However, when I find all my users, the derived attribute is nowhere to be found in the response:
[
{
"firstName": "Marlon",
"lastName": "Brando",
"createdAt": "2015-09-13T10:05:15.129Z",
"updatedAt": "2015-09-13T10:05:15.129Z",
"id": 8
},
{
"firstName": "Bjoern",
"lastName": "Gustavsson",
"createdAt": "2015-09-13T10:05:36.221Z",
"updatedAt": "2015-09-13T10:05:36.221Z",
"id": 10
},
{
"firstName": "Charlie",
"lastName": "Sheen",
"createdAt": "2015-09-13T10:06:59.999Z",
"updatedAt": "2015-09-13T10:06:59.999Z",
"id": 11
}
]
Am I missing something, or is it simply not possible to derive attributes like this?

When you are set attributes in Model with function it doesn't mean that it will be executed in resulting attribute. It means that you can call this function in your code. For instance, I have exactly your User model. I can make in my code smth like this:
// api/controllers/UserController.js
module.exports = {
index: function(req, res) {
User
.create({firstName: req.param('firstName'), lastName: req.param('lastName')})
.then(function(user) {
console.log(user.fullName());
return user;
})
.then(res.ok)
.catch(res.negotiate);
}
};
If you want to make it like a dynamic attribute, then you should take a look at toJSON method in your model. You can override it and implement your own logic. I think it will looks like this in your case:
// api/models/User.js
module.exports = {
attributes: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
fullName: function() {
return [this.firstName, this.lastName].join(' ');
},
toJSON: function() {
var obj = this.toObject();
obj.fullName = this.fullName();
return obj;
}
}
};
I didn't check this code but think that should work. You can play around with toJSON method and see what you got. Ping me in comments if code doesn't work.

Related

Stuck to figure out how to access a certain field to update

I have this JSON FILE
{
"_id": "GgCRguT8Ky8e4zxqF",
"services": {
"emails": [
{
"address": "Abunae#naa.com",
"verified": false,
"verifiedMail": "Toto#hotmail.com"
}
],
"profile": {
"name": "Janis"
},
"pushIds": []
}
I want to update my verifiedMail field but couldn't figure out how to do it in Meteor, it's always returning me an error
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'emails.verifiedEmail': emailRefactor} }, { upsert: true })
Couldn't figure out how to access the emails.verifiedEmail field
Tried this exemlpe worked like a charm
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'profile.name': emailRefactor} }, { upsert: true })
but couldn't figure out how to access emails.verifiedEmail .
Could you please help me ?
Emails is an array, while profile is an object. You have to access the first object of the email array instead
This updates the exact email address from emails
Meteor.users.update({
"emails.address": emailRefactor
}, {
$set: {
"emails.$.verified": true
}
});
Or update the first element
Meteor.users.update({
_id: user._id,
"emails.address": emailRefactor
}, {
$set: {
"emails.0.verified": true
}
});
You're trying to set verifiedEmail while the actual field is verifiedMail.

How to name the fields of related model in filter object?

In my project, two models "UserProfile" and "UserAccount" are with a relation that the former "has one" the later. The .json files look like:
userprofile.json:
{
"name": "Userprofile",
"base": "PersistedModel",
//...
"properties": {
"userid": {
"type": "Number"
},
"phoneno": {
"type": "String"
}
},
//...
"relations": {
"userAccounts": {
"type": "hasOne",
"model": "UserAccount",
"foreignKey": "id",
"options": {
"validate": true,
"forceId": false
}
}
}
}
useraccount.json:
{
"name": "UserAccount",
"base": "User",
"idInjection": true,
"restrictResetPasswordTokenScope": true,
"emailVerificationRequired": true,
"properties": {},
"relations": {}
//...
}
The models have corresponding tables in a MariaDB.
Now the quest is to "GET" UserProfile with a keyword that match any one field of UserProfile.phoneno or UserAccount.email (yes, the key point is or). In SQL terms, that is:
SELECT * FROM UserProfile INNER JOIN UserAccount
ON UserProfile.userid = UserAccount.id
WHERE UserProfile.phoneno LIKE '%keyword%'
OR UserAccount.email LIKE '%keyword%'
It should be a common and simple query in SQL but seems become difficult in LookBack. My implementation is:
userprofile.js:
'use strict';
module.exports = function (Userprofile) {
Userprofile.remoteMethod('profileByEmailOrPhoneno', {
description: '...',
http: {path:'/profileByEmailOrPhoneno', verb: 'get'},
accepts: {arg: 'keyword', type: 'string', required: true},
returns: {arg: 'profile', type: 'array' }
})
Userprofile.profileByEmailOrPhoneno = function (keyword, cb) {
let filter = {
fields: {userid: true, nickname: true, phoneno: true},
include: {
relation: 'userAccounts',
scope: {
fields: {username: true, email: true}
}
},
where: {or: [
{phoneno: {like: `%${keyword}%`}},
{'userAccount.email': {like: `%${keyword}%`}}
]}
}
Userprofile.find(
filter,
function (err, records) {
if (err) console.log(err)
else cb(null, records)
}
)
}
};
I tested it on StrongLoop API Explorer and it always returned the whole records in UserProfile no matter whatever keyword. If the criterium
{'userAccount.email': {like: `%${keyword}%`}}
was removed the codes worked correctly. I think this criterium is wrong so LookBack ignores it and evaluate the where section to be true. I modified it to:
{'email': {like: `%${keyword}%`}}
and it was still wrong.
So, I wonder how to correctly name the relation model's field (eg.'email'), or, how to write the correct filter. Anybody can give some help? I'll very appreciated for it. ^^
The include statement in Loopback is a left-outer-join, so the query will always return ALL the Userprofile records. Some will have userAccounts with an array of values, other's wont. You need to further filter the Userprofile records.
Also, you need to put he userAccoutns filter in the scope statement of your filter:
Userprofile.profileByEmailOrPhoneno = function (keyword, cb) {
let filter = {
fields: {userid: true, nickname: true, phoneno: true},
include: {
relation: 'userAccounts',
scope: {
fields: {username: true, email: true},
where: {'email':{'like': `%${keyword}%`}} // userAccounts filter goes here
}
},
where: {phoneno: {like: `%${keyword}%`}}
}
Userprofile.find(filter, function (err, records) {
if (err) console.log(err)
else {
// filter the records for those that have userAccounts
var filteredResults = records.filter(record =>
record.userAccounts &&
Array.isArray(record.userAccounts()) &&
record.userAccounts().length);
cb(null, filteredResults)
}
})
}

ExtJS 6 Rest Proxy where JSON Data Object is ID

I'm hoping someone can guide me on how to deal with this json data structure.
Here's an example of that: (I have zero control of this data)
{
"1": {
"name": "thing 01",
"attributes": {
"color": "red",
"brand": "ACME"
}
},
"2": {
"name": "thing 02",
"attributes": {
"color": "blue",
"brand": "ACME"
}
}
}
So I'm confused about how to get the records using the reader
Ext.define('MyApp.model.Thing', {
extend: 'Ext.data.Model'
fields: [
{ name: 'name' },
{ name: 'attributes', type: 'auto' }
],
proxy: {
type: 'rest',
url: 'http://example.com/api/things',
reader: {
type: 'json',
rootProperty: ??? // <--- How should this work?
}
}
});
I've wondered if there's a way to do something like...
rootProperty: '[id]'
Also is there a way to specify the ID when it is the data object? Maybe somehow using the idProperty config on the Model?
Should I use the reader.format method? That would seem a little gross...
Any ideas are apreciated. Thanks!
Write a custom reader class:
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.myreader',
config: {
transform: function (data) {
var ret = [],
key, o;
for (key in data) {
o = data[key];
o.id = key;
ret.push(o);
}
return ret;
}
}
});
Ext.define('Thing', {
extend: 'Ext.data.Model',
fields: ['name', 'attribute'],
proxy: {
type: 'ajax',
url: 'data1.json',
reader: {
type: 'myreader'
}
}
});
Example fiddle.
Your EXACT question is already answered here.
You should implement a custom reader and override the getResponseData method.
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.myreader',
getResponseData: function(response) {
var data = this.callParent([response]);
//do stuff here
return data;
}
});

Parse nested json with a proxy in a Sencha Touch 2 store using rootProperty

I have a JSON response that is nested like the following (simplified, but same format):
{
"response":{
"v":"1.0",
"users":[
{
"firstName":"Nicole",
"LastName":"A",
},
{
"firstName":"John",
"LastName":"B",
},
{
"firstName":"Bob",
"LastName":"C",
}
],
}
}
Here is the model:
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.Field'
],
config: {
fields: [
{
name: 'firstName'
},
{
name: 'lastName'
}
]
}
});
I am starting from the sencha architect tutorial for CityBars, so most of the code should be quite basic, and I am just trying to get the users from the json response loaded. Here is the controller:
Ext.define('MyApp.controller.User', {
extend: 'Ext.app.Controller',
launch: function() {
var me = this;
Ext.Viewport.setMasked({ message: 'Loading Attendees...' });
me.getUsers(function (store) {
me.getDataList().setStore(store);
});
},
getUsers: function(callback) {
var store = Ext.data.StoreManager.lookup('UserStore'),
url = 'http://urltogetjsonresponse'
store.getProxy().setUrl(url);
store.load(function() {
callback(store);
});
},
});
Here is the store:
Ext.define('MyApp.store.UserStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.User',
'Ext.data.proxy.JsonP',
'Ext.data.reader.Json'
],
config: {
model: 'MyApp.model.User',
storeId: 'UserStore',
proxy: {
type: 'jsonp',
reader: {
type: 'json',
rootProperty: 'response.user'
}
}
}
});
I tried 'response.user' but it did not work for me. I have already looked all over and know that using rootProperty: 'user' would work, if the users attribute were at the same level as "response" instead of nested under it. I have also tried adding record: 'users' but that did not seem to work either.
If anybody knows if this is doable and has an easy solution to this, that would be great. I don't actually understand how the proxy works, so if anybody can explain a bit about that, it would be helpful too. Thanks.
Taken from Sencha's documentation about the JSON reader :
{
"count": 1,
"ok": true,
"msg": "Users found",
"users": [{
"userId": 123,
"name": "Ed Spencer",
"email": "ed#sencha.com"
}],
"metaData": {
"idProperty": 'userId',
"rootProperty": "users",
"totalProperty": 'count',
"successProperty": 'ok',
"messageProperty": 'msg'
}
}
The rootProperty here is 'users', so you'll need to specify users (which is the name of the array containing your instances of model) and not user .

Change the Json Format dynamically

My JSON returns something like this
{
data: "Business & Investing (0)",
attr: {
id: "91",
rel: "file",
dataitem: null,
datatext: null
},
children: [ ]
},
{
data: "Stock Exchange & Money (0)",
attr: {
id: "92",
rel: "file",
dataitem: null,
datatext: null
},
children: [ ]
},
i want to change like this "Data" to "title" brfore passing it to view
{
title: "Business & Investing (0)",
attr: {
id: "91",
rel: "file",
dataitem: null,
datatext: null
},
children: [ ]
},
{
title: "Stock Exchange & Money (0)",
attr: {
id: "92",
rel: "file",
dataitem: null,
datatext: null
},
children: [ ]
},
My Dynatree :
function CreateCatTree(filter) {
$("#res_catBar").dynatree({
postProcess: function (data, dataType) {
alert("hey");
},
initAjax: {
type: "GET",
cache:false,
url: '#Url.Action("Catalog")',
data: {
filter:filter
},
},
onActivate: function (node) {
var id = node.data.attr.id;
event.preventDefault();
retrieveCatalog(id);
return false;
}
});
}
You may try this in JS:
if (yourJSON.hasOwnProperty("data")) {
yourJSON["title"] = yourJSON["data"];
delete yourJSON["data"];
}
Since you seem to use Dynatree:
you may also implement the postProcess(data, dataType) callback in JavaScript, and modify the Ajax result there, before it is passed to Dynatree.
You should make your own class which has your set of defined properties, and that, then can be serialized through Newtownsoft JSON.
I have a TreeNode.cs
public class TreeNode
{
[JsonProperty("title")]
public string title { get; set; }
This is called through a web service as:
List<TreeNode> myTaskListObj = new List<TreeNode>();
And then Deserialize or serializing the data, as needed:
//De Serialize
myTaskListObj = JsonConvert.DeserializeObject<List<TreeNode>>(jsonString);
//Serialize
jsonString = JsonConvert.SerializeObject(myTaskListObj, Formatting.Indented);
Hope this helps.