How to extend the NavigationListItem control in SAPUI5? - custom-controls

I want to extend the NavigationListItem control to accept sap.ui.core.Control objects in it's aggregation. I have created a separate file to extend the control and added a new aggregation called 'rows' with type sap.ui.core.Control in the metadata section. The extended control is getting called in the view, but it is not displaying any child controls which were added to the new aggregation 'rows'. Please advise, if I need to add anything more to the extension file.
Extension code:
sap.ui.define(["sap/ui/core/Control",
"sap/tnt/NavigationListItem"],
function(Control, NavigationListItem) {
"use strict";
return NavigationListItem.extend("ajacontrolExt.control.NavigationCustomListItem", {
metadata: {
properties: {
},
defaultAggregation: "rows",
aggregations: {
rows: {
type: "sap.ui.core.Control",
multiple: true,
singularName: "row"
}
}
}
});
});

Related

Is there a way to get a structure of a Strapi CMS Content Type?

A content-type "Product" having the following fields:
string title
int qty
string description
double price
Is there an API endpoint to retrieve the structure or schema of the "Product" content-type as opposed to getting the values?
For example: On endpoint localhost:1337/products, and response can be like:
[
{
field: "title",
type: "string",
other: "col-xs-12, col-5"
},
{
field: "qty",
type: "int"
},
{
field: "description",
type: "string"
},
{
field: "price",
type: "double"
}
]
where the structure of the schema or the table is sent instead of the actual values?
If not in Strapi CMS, is this possible on other headless CMS such as Hasura and Sanity?
You need to use Models, from the link:
Link is dead -> New link
Models are a representation of the database's structure. They are split into two separate files. A JavaScript file that contains the model options (e.g: lifecycle hooks), and a JSON file that represents the data structure stored in the database.
This is exactly what you are after.
The way I GET this info is by adding a custom endpoint - check my answers here for how to do this - https://stackoverflow.com/a/63283807/5064324 & https://stackoverflow.com/a/62634233/5064324.
For handlers you can do something like:
async getProductModel(ctx) {
return strapi.models['product'].allAttributes;
}
I needed the solution for all Content Types so I made a plugin with /modelStructure/* endpoints where you can supply the model name and then pass to a handler:
//more generic wrapper
async getModel(ctx) {
const { model } = ctx.params;
let data = strapi.models[model].allAttributes;
return data;
},
async getProductModel(ctx) {
ctx.params['model'] = "product"
return this.getModel(ctx)
},
//define all endpoints you need, like maybe a Page content type
async getPageModel(ctx) {
ctx.params['model'] = "page"
return this.getModel(ctx)
},
//finally I ended up writing a `allModels` handler
async getAllModels(ctx) {
Object.keys(strapi.models).forEach(key => {
//iterate through all models
//possibly filter some models
//iterate through all fields
Object.keys(strapi.models[key].allAttributes).forEach(fieldKey => {
//build the response - iterate through models and all their fields
}
}
//return your desired custom response
}
Comments & questions welcome
This answer pointed me in the right direction, but strapi.models was undefined for me on strapi 4.4.3.
What worked for me was a controller like so:
async getFields(ctx) {
const model = strapi.db.config.models.find( model => model.collectionName === 'clients' );
return model.attributes;
},
Where clients is replaced by the plural name of your content-type.

How to add a non-user-editable field to a content type in Strapi?

Say I have a post content type with the following 4 fields:
title (string)
content (string)
slug (string)
author (relationship)
How can I add a 5th field that depends on one of the above 4 fields for its value and isn't user-editable? Say, I wanted a wordCount field with the number of words in the content field as its value. What file should I consider exploring in order to incorporate this functionality?
P.S.: For what it's worth, I'm using MongoDB Atlas for my database needs.
You will have to create your wordCount attribute in your content type.
Then in the content manager link in the left menu, you will be able to custom the view of your edit/create page. Here you will be able to Disable or Remove the field from the page.
After that you will have to go in the ./api/post/models/Post.js file and update following functions.
If you are using NoSQL database (Mongo)
beforeSave: async (model) => {
if (model.content) {
model.wordCount = model.content.length;
}
},
beforeUpdate: async (model) => {
if (model.getUpdate().content) {
model.update({
wordCount: model.getUpdate().content.length
});
}
},
If you are using SQL (SQLite, Postgres, MySQL)
beforeSave: async (model, attrs, options) => {
if (attrs.content) {
attrs.wordCount = attrs.content.length;
}
},
(For Strapi 3.x; NoSQL and SQL)
Create a wordcount field on the content type
Configure the view of the content type and click on the wordcount field - set 'Editable field' to false.
Edit ./api/post/models/post.js
'use strict';
module.exports = {
lifecycles: {
async beforeCreate(data) {
data.wordcount = data.content.match(/(\w+)/g).length;
},
async beforeUpdate(params, data) {
data.wordcount = data.content.match(/(\w+)/g).length;
},
},
};

Relay how to write outputFields, getFatQuery, getConfigs for create new Item

How to write outputFields, getFatQuery, getConfigs for create new item and update items list
Please take a look gist or live
Questions are
getFatQuery() {
return Relay.QL`
???
`;
}
getConfigs() {
return [???];
}
outputFields: {
???
},
The outputFields in your schema make up the GraphQL type CreateActivityPayload that will be generated from your schema.js file. A mutation is like a regular query, but with side effects. In outputFields you get to decide what's queryable. Since your store is the only thing in your app that can change as a result of this mutation, we can start with that.
outputFields: {
store: {
type: storeType,
resolve: () => store,
},
}
The fat query operates on these output fields. Here you tell Relay what could possibly change as a result of this mutation. Adding an activity could change the following fields:
getFatQuery() {
return Relay.QL`
fragment on CreateActivityPayload #relay(pattern: true) {
store {
activities
}
}
`;
}
Finally, the config tells Relay what to do with the query when it gets it, or even if it needs to be made at all. Here, you're looking to update a field after creating a new activity. Use the FIELDS_CHANGE config to tell Relay to update your store.
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldsIDs: {
store: this.props.storeId,
},
}];
}
See more: https://facebook.github.io/relay/docs/guides-mutations.html

JayData Data model already created - add an OData action afterwards

I have an OData Web Api service where I create the controllers with a T4 template from the EF datamodel.
While doing that, I also create the Jaydata datamodel with T4.
But now, I have a partial class that will add an action to one of my controllers.
As the JayData file is also created by a T4 template, is there a way to add actions to one of the EntitySets later on?
What I managed to do now is the following:
My generated JayData context looks like the following:
$data.EntityContext.extend('myNameSpace.MyContext', {
'Cases': { type: $data.EntitySet, elementType: myNameSpace.Case},
// ... other Entitysets
}
Later, I extend this context like this:
myNameSpace.MyContext.extend('myNameSpace.MyExtendedContext', {
'Cases': { type: $data.EntitySet, elementType: myNameSpace.Case, actions: {
'Checkout': { type: $data.ServiceAction, returnType: 'myNameSpace.Case', IsBindable: true, 'EntitySet': 'Cases', IsAlwaysBindable: true, params: [{ name: 'Id', type: 'Edm.Guid' }] }
}
}
So I can use my action if I later use my extended context. I think this should be good enough.
My Typescript definitions for this look like this:
declare module myNamespace {
export class CaseExtensions extends $data.EntitySet<myNamespace.Case> {
Checkout: {
(Id: string, handler?: (result: myNamespace.Case) => void): $data.IPromise<Case>;
(params?: { Id?: string; }, handler?: (result: myNamespace.Case) => void): $data.IPromise<Case>;
};
}
export class MyExtendedContext extends MyContext {
Cases: CaseExtensions;
}
}

Translating JSON into custom dijit objects

I am looking for an example where JSON constructed from the server side is used to represent objects that are then translated into customized widgets in dojo. The JSON would have to be very specific in its structure, so it would not be a very general solution. Could someone point me to an example of this. It would essentially be the reverse of this
http://docs.dojocampus.org/dojo/formToJson
First of all let me point out that JSON produced by dojo.formToJson() is not enough to recreate the original widgets:
{"field1": "value1", "field2": "value2"}
field1 can be literally anything: a checkbox, a radio button, a select, a text area, a text box, or anything else. You have to be more specific what widgets to use to represent fields. And I am not even touching the whole UI presentation layer: placement, styling, and so on.
But it is possible to a certain degree.
If we want to use Dojo widgets (Dijits), we can leverage the fact that they all are created uniformly:
var myDijit = new dijit.form.DijitName(props, node);
In this line:
dijit.form.DijitName is a dijit's class.
props is a dijit-specific properties.
node is an anchor node where to place this dijit. It is optional, and you don't need to specify it, but at some point you have to insert your dijit manually.
So let's encode this information as a JSON string taking this dijit snippet as an example:
var myDijit = new dijit.form.DropDownSelect({
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
}, "myNode");
The corresponding JSON can be something like that:
{
type: "DropDownSelect",
props: {
options: [
{ label: 'foo', value: 'foo', selected: true },
{ label: 'bar', value: 'bar' }
]
},
node: "myNode"
}
And the code to parse it:
function createDijit(json){
if(!json.type){
throw new Error("type is missing!");
}
var cls = dojo.getObject(json.type, false, dijit.form);
if(!cls){
// we couldn't find the type in dijit.form
// dojox widget? custom widget? let's try the global scope
cls = dojo.getObject(json.type, false);
}
if(!cls){
throw new Error("cannot find your widget type!");
}
var myDijit = new cls(json.props, json.node);
return myDijit;
}
That's it. This snippet correctly handles the dot notation in types, and it is smart enough to check the global scope too, so you can use JSON like that for your custom dijits:
{
type: "my.form.Box",
props: {
label: "The answer is:",
value: 42
},
node: "answer"
}
You can treat DOM elements the same way by wrapping dojo.create() function, which unifies the creation of DOM elements:
var myWidget = dojo.create("input", {
type: "text",
value: "42"
}, "myNode", "replace");
Obviously you can specify any placement option, or no placement at all.
Now let's repeat the familiar procedure and create our JSON sample:
{
tag: "input",
props: {
type: "text",
value: 42
},
node: "myNode",
pos: "replace"
}
And the code to parse it is straightforward:
function createNode(json){
if(!json.tag){
throw new Error("tag is missing!");
}
var myNode = dojo.create(json.tag, json.props, json.node, json.pos);
return myNode;
}
You can even categorize JSON items dynamically:
function create(json){
if("tag" in json){
// this is a node definition
return createNode(json);
}
// otherwise it is a dijit definition
return createDijit(json);
}
You can represent your form as an array of JSON snippets we defined earlier and go over it creating your widgets:
function createForm(array){
dojo.forEach(array, create);
}
All functions are trivial and essentially one-liners — just how I like it ;-)
I hope it'll give you something to build on your own custom solution.

Resources