How to get model on onInit? - odata

In manifest.json, I have following model definition:
{
"sap.ui5": {
"models": {
"SalesInvoices": {
"type": "sap.ui.model.odata.v2.ODataModel",
"settings": {
"defaultOperationMode": "Server",
"defaultCountMode": "Request"
},
"dataSource": "ZAM_SALES_STATISTICS_CDS",
"preload": true
}
}
}
}
As you can see, SalesInvoices is connected to the OData service.
Now on the onInit function in the controller, I am trying to get Metadata from OData as following:
{ // Controller
onInit: function() {
const oPayerModel = this.getView().getModel("SalesInvoices");
console.log(oPayerModel.getMetadata());
setTimeout(() => {
const oPayerModel = this.getView().getModel("SalesInvoices");
console.log(oPayerModel.getMetadata());
}, 600);
},
// ...
}
As you can see, I have to delay to get the OData instance.
setTimeout is not recommended to use in SAPUI5, how can I do it better?

You can avoid setTimeout, as mentioned in this answer, by using the v2.ODataModel API metadataLoaded instead which returns a promise. The promise is fulfilled once the service metadata is loaded successfully.
onInit: async function() {
const oPayerModel = this.getOwnerComponent().getModel("SalesInvoices");
try {
await oPayerModel.metadataLoaded(true);
const oServiceMetadata = oPayerModel.getServiceMetadata(); // NOT .getMetadata()
// ...
} catch (oError) {/* ... */}
},
About the model being undefined in onInit, here are answers with better explanations:
Nabi's answer
My other answer

I think you are running into the issue I reported some time ago: Component + default OData model: this.getView().getModel() returns undefined in onInit() of controllers:
don't use this.getView().getModel() directly in onInit()
instead use this.getOwnerComponent().getModel() in onInit()
anywhere else in the controller you can use this.getView().getModel()
In your case you should be fine changing the suggestion of #boghyon slightly:
onInit: function() {
const oPayerModel = this.getOwnerComponent().getModel("SalesInvoices");
oPayerModel.metadataLoaded().then(this.onMetadataLoaded.bind(this, oPayerModel));
},
onMetadataLoaded: function(myODataModel) {
const metadata = myODataModel.getServiceMetadata(); // NOT .getMetadata()
// ...
},
This way you can get rid of setTimeout(...).

Related

How to setup custom middleware in strapi?

I just wanted to setup a simple custom middleware in strapi. I have tried what they are writing in docs but I found that environments folder and inside configurations are removed. Follwing that currently I have writtent.
/config/environments/development/middleware.json
{
"subscribers": {
"enabled": true
}
}
/config/middleware.json
{
"timeout": 100,
"load": {
"before": ["responseTime", "logger", "cors", "responses", "gzip"],
"order": ["parser", "subscribers"],
"after": ["router"]
}
}
/middlewares/subscribers/index.js
module.exports = (strapi) => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
console.log("I have been called!");
await next();
});
},
};
};
Please help me to implement a middleware in strapi api.Thanks beforehand.
I just did what is written in the docs and I will do the same in my answer!
Initially I was reading from an older version of documentation which is mentioned by #Derrick Mehaffy. I found the correct docs url and read through its middleware implementation. [LINK TO THE DOCS] (Below explanations are obtained from docs)
------------------------------------------------------------------------------------
Examples: Create your custom middleware. [Path — ./middlewares/timer/index.js]
module.exports = strapi => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
const start = Date.now();
// I just add custom code that logs `I have been called!`
console.log('I have been called!');
await next();
const delta = Math.ceil(Date.now() - start);
ctx.set('X-Response-Time', delta + 'ms');
});
},
};
};
Enable the middleware in environments settings.
Load a middleware at the very first place - !You can do at the proper order
Path — ./config/middleware.js
module.exports = {
load: {
before: ["timer", "responseTime", "logger", "cors", "responses", "gzip"],
order: ["parser", ],
after: ["router", ],
},
settings: {
timer: {
enabled: true,
},
},
};
Basically I just copied and pasted the answer from docs, but it might be helpful for future use that's I have left the question

Video as a background image not working in Gatsby PWA on iOS

I created a opt-in app for potential interims for our company, i worked with Gatsby and for now am quite satisfied with the result. I made it an Progressive Web App as that is fairly easy with the gatsby plugin.
The PWA works great on Android and shows the background video as expected, but on iOS the video doesn't show.
I updated all the packages and dependencies to the last versions but that doesn't change a thing. I tried googling the issue but got a lot of search results off people trying to let a PWA play video in the background when the app is closed (not my case).
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Afstuderen bij Arcady`,
short_name: `Afstuderen`,
start_url: `/`,
background_color: `#FFF`,
theme_color: `#00a667`,
display: `standalone`,
icon: `src/images/bear_green.png`,
},
},
'gatsby-plugin-offline',
And the content of the service worker
importScripts("workbox-v3.6.3/workbox-sw.js");
workbox.setConfig({modulePathPrefix: "workbox-v3.6.3"});
workbox.core.setCacheNameDetails({prefix: "gatsby-plugin-offline"});
workbox.skipWaiting();
workbox.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
*/
self.__precacheManifest = [
{
"url": "webpack-runtime-aec2408fe3a97f1352af.js"
},
{
"url": "app-5b624d17337895ddf874.js"
},
{
"url": "component---node-modules-gatsby-plugin-offline-app-shell-js-b97c345e19bb442c644f.js"
},
{
"url": "offline-plugin-app-shell-fallback/index.html",
"revision": "ac0d57f6ce61fac4bfa64e7e08d076c2"
},
{
"url": "0-d2c3040ae352cda7b69f.js"
},
{
"url": "component---src-pages-404-js-cf647f7c3110eab2f912.js"
},
{
"url": "static/d/285/path---404-html-516-62a-0SUcWyAf8ecbYDsMhQkEfPzV8.json"
},
{
"url": "static/d/604/path---offline-plugin-app-shell-fallback-a-30-c5a-BawJvyh36KKFwbrWPg4a4aYuc8.json"
},
{
"url": "manifest.webmanifest",
"revision": "5a580d53785b72eace989a49ea1e24f7"
}
].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerRoute(/(\.js$|\.css$|static\/)/, workbox.strategies.cacheFirst(), 'GET');
workbox.routing.registerRoute(/^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/, workbox.strategies.staleWhileRevalidate(), 'GET');
workbox.routing.registerRoute(/^https?:\/\/fonts\.googleapis\.com\/css/, workbox.strategies.staleWhileRevalidate(), 'GET');
/* global importScripts, workbox, idbKeyval */
importScripts(`idb-keyval-iife.min.js`)
const WHITELIST_KEY = `custom-navigation-whitelist`
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => {
const { pathname } = new URL(event.request.url)
return idbKeyval.get(WHITELIST_KEY).then((customWhitelist = []) => {
// Respond with the offline shell if we match the custom whitelist
if (customWhitelist.includes(pathname)) {
const offlineShell = `/offline-plugin-app-shell-fallback/index.html`
const cacheName = workbox.core.cacheNames.precache
return caches.match(offlineShell, { cacheName }).then(cachedResponse => {
if (cachedResponse) return cachedResponse
console.error(
`The offline shell (${offlineShell}) was not found ` +
`while attempting to serve a response for ${pathname}`
)
return fetch(offlineShell).then(response => {
if (response.ok) {
return caches.open(cacheName).then(cache =>
// Clone is needed because put() consumes the response body.
cache.put(offlineShell, response.clone()).then(() => response)
)
} else {
return fetch(event.request)
}
})
})
}
return fetch(event.request)
})
})
workbox.routing.registerRoute(navigationRoute)
let updatingWhitelist = null
function rawWhitelistPathnames(pathnames) {
if (updatingWhitelist !== null) {
// Prevent the whitelist from being updated twice at the same time
return updatingWhitelist.then(() => rawWhitelistPathnames(pathnames))
}
updatingWhitelist = idbKeyval
.get(WHITELIST_KEY)
.then((customWhitelist = []) => {
pathnames.forEach(pathname => {
if (!customWhitelist.includes(pathname)) customWhitelist.push(pathname)
})
return idbKeyval.set(WHITELIST_KEY, customWhitelist)
})
.then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
function rawResetWhitelist() {
if (updatingWhitelist !== null) {
return updatingWhitelist.then(() => rawResetWhitelist())
}
updatingWhitelist = idbKeyval.set(WHITELIST_KEY, []).then(() => {
updatingWhitelist = null
})
return updatingWhitelist
}
const messageApi = {
whitelistPathnames(event) {
let { pathnames } = event.data
pathnames = pathnames.map(({ pathname, includesPrefix }) => {
if (!includesPrefix) {
return `${pathname}`
} else {
return pathname
}
})
event.waitUntil(rawWhitelistPathnames(pathnames))
},
resetWhitelist(event) {
event.waitUntil(rawResetWhitelist())
},
}
self.addEventListener(`message`, event => {
const { gatsbyApi } = event.data
if (gatsbyApi) messageApi[gatsbyApi](event)
})
I expect the iOS PWA (safari) to show the video as it does on Android but instead it gives a grey screen.
I hope some one can help me out or point me in the right direction.
How big is your video ?
Last time I checked, iOS has a limit of 50MB for the cache of a PWA, so if your video is bigger than 50MB, that may be the reason it works only on Android (which doesn't have such restrictions).
I found this blog post that helped me fix this problem
To add Range request handling to gatsby-plugin-offline, I added a script called range-request-handler.js with the following:
// range-request-handler.js
// Define workbox globally
importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');
// Bring in workbox libs
const { registerRoute } = require('workbox-routing');
const { CacheFirst } = require('workbox-strategies');
const { RangeRequestsPlugin } = require('workbox-range-requests'); // The fix
// Add Range Request support to fetching videos from cache
registerRoute(
/(\.webm$|\.mp4$)/,
new CacheFirst({
plugins: [
new RangeRequestsPlugin(),
],
})
);
Then in my gatsby-config.js I configured the plugin to append the above script:
// gatsby-config.js
module.exports = {
// ...
plugins: [
// ...plugins
{
resolve: 'gatsby-plugin-offline',
options: {
appendScript: require.resolve('./range-request-handler.js'),
},
},
// ...plugins
],
// ...
};
Videos now work in the Safari browser for me. If there is a better way to implement this, I am all ears. For now it works as intended

SAPUI5 OData V4 Read

Can you please tell how to perform OData Read in OData V4 in SAPUI5 ?
I can do this very easily in OData V2, how do I achieve similar things with oData V4 ?
this_.getOwnerComponent().getModel("myModel").read("/zprojects", {
"async": true,
"success": function (oData) {
console.log(oData);
},
"error": function (oError) {
console.log(oError);
}
});
The problem for me is I want to massage the data to add additional values before I bind to view. Here is my complete code of oData V2:
this_.getOwnerComponent().getModel("myModel").read("/zprojects", {
"async": true,
"success": function (oData) {
var myArray = [];
var pos;
for (var i = 0; i < oData.results.length; i++) {
pos = myArray.map(function (e) {
return e.ID;
}).indexOf(oData.results[i].PROJECTID);
if (pos === -1) {
myArray.push({
ID: oData.results[i].PROJECTID,
PROJECT_DESC: oData.results[i].PROJECT_DESC
});
}
}
myArray.sort((a, b) => (a.PROJECT_DESC > b.PROJECT_DESC) ? 1 : -1);
myArray.unshift({
ID: "-1",
PROJECT_DESC: "Please select Project ID"
oModel = new sap.ui.model.json.JSONModel(myArray);
sap.ui.core.Fragment.byId("idFragment", "project").setModel(oModel);
},
"error": function (oError) {
console.log(oError);
}
});
From the documentation:
The OData V4 model only supports data access using bindings. It does not provide any direct access to the data.
You can get around that by creating fake bindings and listening to the dataReceived event, but I'd rather suggest using jQuery's ajax features to request the data, until the v4.ODataModel supports direct access to the data:
$.get({
url: "<your_service_url>/zprojects",
success: function(data) {
// your success logic
},
error: function(error) {
// your error logic
}
});

Apollo Subscription doesn't seem to get called on Mutation

New to Apollo, so I decided to take the most simple example I found and try to work it in a slightly different way. My code can be found here.
The problem I am having is that the Subscription doesn't seem to get called when I call the Mutation createTask(). The Mutation and Subscription are defined in schema.graphql as:
type Mutation {
createTask(
text: String!
): Task
}
type Subscription {
taskCreated: Task
}
And in resolvers.js as:
Mutation: {
createTask(_, { text }) {
const task = { id: nextTaskId(), text, isComplete: false };
tasks.push(task);
pubsub.publish('taskCreated', task);
return task;
},
},
Subscription: {
taskCreated(task) {
console.log(`Subscript called for new task ID ${task.id}`);
return task;
},
},
What I am expecting to happen is that I would get a console.log in the server every time I run the following in the client:
mutation Mutation($text: String!) {
createTask(text:$text) {
id
text
isComplete
}
}
But nothing happens. What am I missing?
The subscription resolver function is called when there is actually a subscription to the GraphQL Subscription.
As you did not add a client which uses subscriptions-transport-ws and the SubscriptionClient for subscribing to your websocket and the subscription it will not work.
What you could do is add the subscription Channel to the setupFunctions of the SubscriptionManager and therein you get the value that the pubsub.publish function delivers.
Could look like this:
...
const WS_PORT = 8080;
const websocketServer = createServer((request, response) => {
response.writeHead(404);
response.end();
});
websocketServer.listen(WS_PORT, () => console.log( // eslint-disable-line no-console
`Websocket Server is now running on http://localhost:${WS_PORT}`
));
const subscriptionManager = new SubscriptionManager({
schema: executableSchema,
pubsub: pubsub,
setupFunctions: testRunChanged: (options, args) => {
return {
taskCreated: {
filter: (task) => {
console.log(task); // sould be log when the pubsub is called
return true;
}
},
};
},
,
});
subscriptionServer = new SubscriptionServer({
subscriptionManager: subscriptionManager
}, {
server: websocketServer,
path: '/',
});
...

filterFn Seems to have no effect on WSAPI Query

I passed a function as a filter in my WSAPI query, however it seems to have no effect on the results returned. Are there fields which cannot be filtered upon by using this method?
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
Ext.create('Rally.data.WsapiDataStore', {
model : 'TestCase',
fetch : ['TestCases'],
filters : [
function(item) {
return item.FormattedID.indexOf('10') !== -1;
}
]
}).load({
callback: function(records) {
//All records returned, no filter applied
}
});
}
});
I also expected your code to work, but perhaps callback happens before the client side filter is applied. Here is a modified version of the code where the filter is applied and only one record is returned as expected:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var myStore = Ext.create('Rally.data.WsapiDataStore', {
model : 'TestCase',
fetch : ['FormattedID']
});
myStore.load({
callback: function(records) {
myStore.filterBy(function(item) {
return item.get('FormattedID').indexOf('10') !== -1;
});
console.log(myStore.getRange()); //one record
}
});
}
});

Resources