I don't know if I'm just blind or something but how can I do the following:
I have a User model with a hasOne relation to a UserData model. I only want one property of UserData directly in the results of User.
The relation in User looks like this:
"relations": {
"userData": {
"type": "hasOne",
"model": "UserData"
}
}
And the default scope in User:
"scope": {
"include": "userData"
}
So the result for one User is:
[
{
"id": 5,
"email": "example#example.com",
"name": "Example",
"userData": {
"id": 5,
"birthdate": "1971-09-06T00:00:00.000Z"
}
}
]
But what I want is this:
[
{
"id": 5,
"email": "example#example.com",
"name": "Example",
"birthdate": "1971-09-06T00:00:00.000Z"
}
]
How can I achive this?
Edit:
The two model definitions:
ChiliUser:
{
"name": "ChiliUser",
"base": "ChiliUserData",
"idInjection": true,
"options": {
"validateUpsert": true,
"mysql": {
"table": "person"
}
},
"properties": {
"id": {
"type": "number"
},
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"vorname": {
"type": "string"
},
"name": {
"type": "string"
},
"spitzname": {
"type": "string"
},
"strasse": {
"type": "string"
},
"plz": {
"type": "number"
},
"ort": {
"type": "string"
},
"geolat": {
"type": "string"
},
"geolng": {
"type": "string"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
ChiliUserData:
{
"name": "ChiliUserData",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true,
"mysql": {
"table": "person_data"
}
},
"properties": {
"id": {
"type": "number"
},
"person_id": {
"type": "number"
},
"birthdate": {
"type": "date"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
Are you using the built-in User model? If you are, you can simply extend it with your UserData model using base: "User", and the underlying schema will be as you desire:
{
"name": "UserData",
"base": "User",
"properties": {}
...
}
But this setup would mean you'd use UserData over User in your code. Since the built-in User model gets in the way, I'd recommend a different word like Person or Customer, in which case it would look like:
{
"name": "Person",
"base": "User",
"properties": {
...extra user data properties only...
}
...
}
This way, only Person will have all the extra "user data" fields on it, but will also include all the fields/methods defined for User inside node_modules/loopback/common/models/User.json. If you're using MySQL, the Person table will be created with the combination of fields from both User and Person.
Related
I have a collection called dish-category which contains many dishes, and a dish collection that contains many dish-options (another collection).
the list of dishes from each dish-category is available in the API but the nested collection of dish options from each dish is not available on strapi.
the following are the settings for my models:
dish-category.settings.json:
{
"kind": "collectionType",
"collectionName": "dish_categories",
"info": {
"name": "DishCategory",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Description": {
"type": "text"
},
"dishes": {
"collection": "dish",
"via": "dish_category"
}
}
}
dish.settings.json:
{
"kind": "collectionType",
"collectionName": "dishes",
"info": {
"name": "Dish",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Description": {
"type": "text"
},
"Total": {
"type": "decimal",
"min": 0,
"required": false,
"default": 0
},
"dish_categories": {
"collection": "dish-category"
},
"dish_options": {
"collection": "dish-option",
"via": "dish"
},
"dish_category": {
"via": "dishes",
"model": "dish-category"
}
}
}
dish-option.settings.json:
{
"kind": "collectionType",
"collectionName": "dish_options",
"info": {
"name": "DishOption",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"Name": {
"type": "string"
},
"Price": {
"type": "decimal",
"min": 0,
"default": 0
},
"dish": {
"via": "dish_options",
"model": "dish"
}
}
}
on the dish-category/controllers/dish-category.js file I tried populating the attribute:
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
* to customize this controller
*/
module.exports = {
async find(params, populate){
return strapi.query('dish-category').find(params, ["dishes","dish.dish_options"]);
}
};
I am having trouble displaying nested relations with multiple values, I have tried looking up solutions online, I came across this thread https://forum.strapi.io/t/simple-nested-array-issue/1045/4 but the solution doesn't work for me and it seems like the link to the example is no longer available.
I have this schema:
{
"type": "object",
"title": "Comment",
"required": [
"comments"
],
"properties": {
"comments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string",
"pattern": "^\\S+#\\S+$",
"description": "Email will be used for evil."
},
"spam": {
"title": "Spam",
"type": "boolean",
"default": true
},
"comment": {
"title": "Comment",
"type": "string",
"maxLength": 20,
"validationMessage": "Don't be greedy!"
}
},
"required": [
"name",
"comment"
]
}
}
}
}
This is an array of comments. I can add and remove comments.
How can I render 2 items of the array always by default ?
I've tried with maxItems and minItems but those parameters don't render items.
There are two ways to do it currently.
First is to set them in the default model so it looks something like:
$scope.model = {
"comments": [{},{}]
}
The second would be to use onChange on the array:
"onChange": function(val) { if(val.length < 2) val.push({}); }
The difference between the two being that the onChange will not allow it to drop below the minimum length you set while the first option is just for the initial default.
I will read user input objects which should be in well-formed.
That is, the input objects could now have any key or sub-structure that is not defined in the interface.
How could I throw an exception, if a user gives an invalid object?
Pre-defined interface
export interface InputStructureInterface {
"tableName": string,
"viewType": string,
"structureName": string,
"sections": Array<Section>,
}
interface Section{
"name": string,
"fields": Array<Field>
}
interface Field{
"fieldName": string,
"relationType": string,
"relationName": null,
"fieldUi": FieldUi
}
interface FieldUi {
"fieldType": string,
"label": strin
}
Valid input structure
This structure is a subset under the defined InputStructureInterface
{
"tableName": "User",
"viewType": "List View",
"structureName": "personal_data_settings_list_view",
"sections": [
{
"name": null,
"fields": [
{
"fieldName": "Name",
"relationType": null,
"relationName": null,
"fieldUi": {
"fieldType": "string",
"label": "Name"
},
}
]
}
]
}
Invalid input structure
Because viewTypeTHIS_IS_A_TYPO, nameTHIS_IS_A_TYPO are not present on the interface
{
"tableName": "User",
"viewTypeTHIS_IS_A_TYPO": "List View",
"structureName": "personal_data_settings_list_view",
"sections": [
{
"nameTHIS_IS_A_TYPO": null,
"fields": [
{
"fieldNameTHIS_IS_A_TYPO": "Name"
}
]
}
]
}
The TypeScript will just enforce the types in compile time. If you want to make this kind of validations you need to use some kind of json-schema validation library. Like this one for example: https://github.com/epoberezkin/ajv
UPDATE
For example, using this library (https://github.com/epoberezkin/ajv) you can do something like this:
import * as Ajv from 'ajv';
const ajv = new Ajv();
const schema = {
"type": "object",
"properties": {
"tableName": { "type": "string" },
"viewType": { "type": "string" },
"structureName": { "type": "string" },
"sections": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"name": { "type": ["string", "null"] },
"fields": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"fieldName": { "type": "string" },
"relationType": { "type": ["string", "null"] },
"relationName": { "type": ["string", "null"] },
"fieldUi": {
"fieldType": { "type": "string" },
"label": { "type": "string" }
}
},
"required": ["fieldName", "relationType", "relationName"],
"additionalProperties": false
}
]
}
},
"required": ["name", "fields"],
"additionalProperties": false
}
]
}
},
"required": ["tableName", "viewType", "structureName"],
"additionalProperties": false
};
const validate = ajv.compile(schema);
let valid = validate(data); // <-- pass your json object here
if (!valid) {
console.log(validate.errors);
}
To install the library: npm install ajv
I've been trying to create a collection that contains an array of objects in loopback.
I want a store data formatted like this:
{
id: "16356135616aaasad", //autogenerated by mongo
"name" : "a name",
"valores": [
{"valor": 567, "fecha": "2016-08-18T00:00:00.000Z"},
{"valor": 569, "fecha": "2016-08-19T00:00:00.000Z"},
...
]
}
I have the following configuration in loopback:
indicador.json
{
"name": "Indicador",
"plural": "indicadores",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true,
"autoId": true
},
"properties": {
"nombre": {
"type": "string",
"required": true
}
},
"relations": {
"historico": {
"type": "embedsMany",
"model": "Valor"
}
}
}
this is a base collection associated to another model (not persistent)
valor.json
{
"name": "Valor",
"plural": "valores",
"base": "Model",
"properties": {
"valor": {
"type": "number",
"required": true
},
"fecha": {
"type": "date",
"required": true
}
}
}
The problem it's when i try to send a post to the endpoint. If i send this data
{
"nombre": "UF",
"valores": [
{
"valor": 0,
"fecha": "2016-08-18"
}
]
}
The API responses this:
{
"error": {
"name": "ValidationError",
"status": 422,
"message": "The `Indicador` instance is not valid. Details: `valores` contains invalid item at index `0`: `id` is blank (value: [ { valor: 0, fecha: 2016...} ]).",
"statusCode": 422,
"details": {
"context": "Indicador",
"codes": {
"valores": [
"invalid"
]
},
"messages": {
"valores": [
"contains invalid item at index `0`: `id` is blank"
]
}
},
"stack": "ValidationError: The `Indicador` instance is not valid. Details: `valores` contains invalid item at index `0`: `id` is blank (value: [ { valor: 0, fecha: 2016...} ]).\n at /home/dev/app/node_modules/loopback-datasource-juggler/lib/dao.js:322:12\n at ModelConstructor.<anonymous> (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:492:11)\n at ModelConstructor.next (/home/dev/app/node_modules/loopback-datasource-juggler/lib/hooks.js:81:12)\n at ModelConstructor.<anonymous> (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:489:23)\n at ModelConstructor.trigger (/home/dev/app/node_modules/loopback-datasource-juggler/lib/hooks.js:71:12)\n at ModelConstructor.Validatable.isValid (/home/dev/app/node_modules/loopback-datasource-juggler/lib/validations.js:455:8)\n at /home/dev/app/node_modules/loopback-datasource-juggler/lib/dao.js:318:9\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)\n at doNotify (/home/dev/app/node_modules/loopback-datasource-juggler/lib/observer.js:98:49)"
}
}
¿Why i get this error?
The id inside the objects of the array that I need isn't necessary for me. I don't understand why happen this.
You need to specify that embedded model doe not need id
"relations": {
"historico": {
"type": "embedsMany",
"model": "Valor",
"options": {
"forceId": false,
"validate": true,
"persistent": false
}
}
}
You need to add "defaultFn":"uuid" to the id property in the json of the model that is going to be embeded in order to have the id generated. In your case it will look like the following:
{
"name": "Valor",
"plural": "valores",
"base": "Model",
"properties": {
"id":{
"type": "string",
"defaultFn":"uuid",
"id":true
},
"valor": {
"type": "number",
"required": true
},
"fecha": {
"type": "date",
"required": true
}
}
}
Hi I'm trying this example
https://docs.strongloop.com/display/public/LB/HasAndBelongsToMany+relations
I have these two models :
Part
{
"name": "Part",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"partNumber": {
"type": "number"
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "Assembly",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
Assembly
{
"name": "Assembly",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"relations": {
"parts": {
"type": "hasAndBelongsToMany",
"model": "Part",
"foreignKey": ""
}
},
"acls": [],
"methods": {}
}
When I try to add a element in assembly I have a undefined element.
I'm using
assembly.js
Assembly.parts.add(part, function(err) {
...
});
but assembly.parts is undefined.
What am I doing wrong?
Thanks
You used a static method on Assembly that is wrong.
Relation methods should used as prototype methods. That make sense.
So you can call like this :
assembly_instance.parts.add(...