Exercise 3: JavaScript-based Mock Data with Custom Logic
Books.js
,
Chapters.js
,
Reviews.js
,
Currencies.js
, etc.
3.1 Implement JavaScript Mock Data
In this file you can implement several methods to control the behavior of the entity set.
-
Using
getInitialDataSetyou can generate the initial data set dynamically. This is an alternative approach to create mock data with more control over the data and the logic. -
Using
executeActionyou can handle the custom actions. This is the entry point for all actions and functions. -
You can use base API methods like
fetchEntries,updateEntry,addEntry, etc. to manipulate the data.
1. Create
Books.js
with dynamic data generation:
Create
webapp/localService/mainService/data/Books.js
:
module.exports = {
// Generate initial dataset dynamically
getInitialDataSet: function (contextId) {
const books = [];
const authors = ['Jane Austen', 'Mark Twain', 'Charles Dickens', 'Ernest Hemingway', 'Virginia Woolf'];
const genres = ['Fiction', 'Mystery', 'Romance', 'Science Fiction', 'Biography'];
for (let i = 0; i < 20; i++) {
const randomAuthor = authors[Math.floor(Math.random() * authors.length)];
const randomGenre = genres[Math.floor(Math.random() * genres.length)];
books.push({
ID: `550e8400-e29b-41d4-a716-44665544${String(i).padStart(4, '0')}`,
title: `${randomGenre} Book ${i + 1}`,
author: randomAuthor,
price: Math.floor(Math.random() * 50) + 10,
currency_code: Math.random() > 0.5 ? 'EUR' : 'USD',
stock: Math.floor(Math.random() * 100),
description: `A compelling ${randomGenre.toLowerCase()} story by ${randomAuthor}.`,
coverUrl: `https://picsum.photos/300/400?random=${i}`,
createdAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'admin',
modifiedAt: new Date().toISOString(),
modifiedBy: 'admin',
IsActiveEntity: true,
HasActiveEntity: false,
HasDraftEntity: Math.random() > 0.8, // 20% chance of being in draft
});
}
return books;
},
// Handle custom actions
executeAction: async function (actionDefinition, actionData, keys) {
console.log('Executing action:', actionDefinition.name);
switch (actionDefinition.name) {
case 'setDiscount':
// Get current entry and apply discount based on parameters
const currentEntries = await this.base.fetchEntries(keys);
if (currentEntries.length > 0) {
const currentBook = currentEntries[0];
const discountPercentage = actionData.percentage || 10; // Default 10% if not provided
const discountMultiplier = (100 - discountPercentage) / 100;
const newPrice = Math.round(currentBook.price * discountMultiplier * 100) / 100;
// Update the book with new price
await this.base.updateEntry(keys, { price: newPrice });
return {
message: `${discountPercentage}% discount applied to "${currentBook.title}"`,
newPrice: newPrice,
originalPrice: currentBook.price,
reason: actionData.reason || 'Mock discount applied',
};
}
break;
case 'promoteBook':
const entry = await this.base.fetchEntries(keys); // expecting only one entry
const firstEntry = entry[0];
// Mark book as promoted
await this.base.updateEntry(keys, {
description: 'PROMOTED: ' + firstEntry.description,
stock: firstEntry.stock + 10,
});
return {
message: 'Book has been promoted successfully!',
promoted: true,
};
default:
this.throwError(`Action ${actionDefinition.name} not implemented`, 501);
}
},
};
2. Start the application:
npm run start-mock
# or from root folder
npm run start:ex3
The app shows data generated by the JavaScript file.
You can test the actions by clicking on the actions in the action bar.
3.2 Debugging
You can use the debugger to step through the code and see the data in the console:
- IDE Debug Terminal : Create a JavaScript Debug Terminal in VS Code for debugging
Breakpoint locations:
-
getInitialDataSet- Called when application starts and loads initial data -
executeAction- Called when user clicks action buttons (setDiscount, promoteBook, etc.)
Watching for changes:
You can watch for changes in the data and the JavaScript file by setting
watch: true
in the
ui5-mock.yaml
file. This will reload the application when the data or the JavaScript file is changed.
3.3 Simulate Slow Responses (optional)
Add a small wait in action handlers to mimic slower backend responses when testing UI behavior (e.g. busy indicators):
// In webapp/localService/mainService/data/Books.js
module.exports = {
// ...
executeAction: async function (actionDefinition, actionData, keys) {
// Simulate latency (e.g., 800ms)
await new Promise((r) => setTimeout(r, 800))
switch (actionDefinition.name) {
case 'setDiscount':
// existing logic
break
default:
this.throwError(`Action ${actionDefinition.name} not implemented`, 501)
}
},
}
Remove the delay once you no longer need to simulate latency.
Next Steps
Exercise 4 will show how to configure multiple OData services for microservices-style applications with distinct metadata and datasets.