Define containing a deferred object - dojo

About require instruction, Dojo official doc says:
if a configuration object is provided, it is passed to the configuration API as described >in Configuration. Next, the dependencies listed in dependencies (if any) are resolved. >Finally, callback (if any) is executed with the resolved dependencies passed in as >arguments
With my example (below), I want to use users.json data in main program, but callback of the called module (monModule.js) is not executed and I obtain an empty object.
How can I proceed ?
Thanks to you.
1 )Main program (extract)
<script type="text/javascript">
require(["monModule"],function(monModule){
console.log(monModule);// returns {}
});
</script>
2) Called module (monModule.js) :
define(["dojo/request/xhr","dojo/json"],function(xhr,json){
xhr("users.json",{handleAs:"JSON"}).
then(function(data){console.log(return data;});
});
3) users.json
[
{‘ id’:"id1",’nom’:"nom1"},
{ ‘id’:"id2",’nom’:"nom2"},
{‘id’:"id3",’nom’:"nom3"}
]

the call in the then is also async so that is why you are getting back an empty object. you will have to add another deferred to your module for it to run and a then in your code
define( ["dojo/request/xhr","dojo/json" , "dojo/Deferred"], function( xhr, json , deferred) {
var def = new deferred();
xhr("users.json", { handleAs :"JSON" }).then(
function( data ) {
return def.resolve(data);
}
);
return def.promise;;
});
And in Code get it by
require(["custom/monModule"],function(monModule){
monModule.then(function(users){
console.log(users);
});
});

Related

DOM Snapshot warning when assertions declared in page object is called twice inside same Test in TestCafe

I have declared async function in page object which has few assertions defined to validate for the presence of elements on a page and I have to call this function twice in same test but I am getting warning - "You passed a DOM snapshot property to the assertion's 't.expect()' method" even when I am not using await keyword in t.expect().
Please let me know how can I get rid of this warning.
fixture('Assertions in Page Object test suite').beforeEach(async t => {
await t.useRole(login);
});
test('Validate UI for sample page, async t => {
await pageObject.validateTableUI(t);
await t.click(pageObject.changeTab);
await pageObject.validateTableUI(t);
});
The Page object method is :
export default class SamplePage {
constructor() {
this.table = Selector('table');
this.tableHeaders = Selector('table thead th);
this.changeTab = Selector('tab);
}
async validateTableUI(t){
await t
.expect(this.table.visible)
.ok()
.expect(this.tableHeaders.visible)
.ok()
.expect(this.tableHeaders.nth(1).textContent)
.contains('Column 1')
.expect(this.tableHeaders.nth(2).textContent)
.contains('Column 1');
}
Unfortunately, there is no official way to disable these warnings. We are aware of this bug. You can track our progress in this GitHub thread.
As a temporary workaround, you can rewrite your .expect calls as follows:
.expect(
this.table.visible
)

Loading custom module base on context in server side SuiteScript 2.0

I'm trying to load my custom module base on the condition.
CustomModule.js
define([], function(){
export {
run: function(){ log.debug('run in CustomModule' }
};
}
And here is my user event script
userevent.js
define(['N/record'], function(record){
...
var moduleName = record.getValue('custom_module_name'); // will return CustomModule.js
require([moduleName], function(customModule){
customModule.run();
});
});
But I got following error
{
type: "error.SuiteScriptModuleLoaderError",
name: "INCORRECT_SUITESCRIPT_CONFIGURATION",
message: "Incorrect SuiteScript configuration for module: CustomModule.js",
}
When I using preload like define(['CustomModule.js'), function(customModule){...})
is working, but this might not suitable for our scenario.
Any suggestion?
So the way you would call a custom module would be (sample snippets below):
CustomModule.js [Code in file cabinet]
define([], function() {
// You can have multiple functions
function testFunction() {
log.debug("Run in CustomModule");
return 123;
}
return { // Return all your functions here
testFunction: testFunction
};
});
And the way you would call the custom module functions in other scripts would be:
Sample User Event Script:
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope SameAccount
*/
define(['./CustomModule.js'], function(customModule) {
.....
function sampleFunction(customerId, recId, invNumberCounter) {
var storeNumber = customModule.testFunction(); // value of 'storeNumber' will be 123
}
.....
});
Additional Important Note:
When writing library scripts (custom modules), have the client-side supported modules in a different script and server-side supported modules in a different script.
This is because, if you incorporate a library script that has a module which is supported only by server-side scripts (eg. N/task module) into a client script, an error would be thrown saying that the module is not supported in this scrpt.
Hope this helps.

Angular2 - Dynamically load component from module

in my angular app I have the following:
export class MyComponent {
subcompPath = "path-to-subcomp#SubcompClassName";
#ViewChild("placeholder", { read: ViewComponentRef }) placeholderRef: ViewComponentRef;
/* Constructor where Compiler, ComponentFactoryResolver are injected */
loadSubcomponent() {
let [path, componentName] = this.subcompPath.split("#");
(<any>window).System.import(path)
.then((module: any) => module[componentName])
.then((type: any) => {
return this._compiler.compileComponentAsync(type)
})
.then((factory: any) => {
let componentRef = this.placeholderRef.createComponent(factory, 0);
});
}
}
My sub-component declares providers and stuff, directives and pipes.
And now RC6 is out to break everything yet again. Components can't declare directives and pipes, but they must be in the module where the component is declared.
So I have to load with SystemJS not the component itself but the module.
Ok, and then I should use
return this._compiler.compileModuleAndAllComponentsAsync(type)
Fine, but how do I get a reference to the factory of this specific component? That factory is all I need, the placeholderRef wants it in its createComponent method, right?
I tried to dig into the angular2 source code from github but it's quite vast, I should try from VS Code or something, with intellisense, but I'm lazy...and I should read this stuff from documentation, which is quite lackluster in angular.io for this particular argument, which is lazy loading of components and modules WITHOUT the router.
Any help is appreciated, I think the solution is simple to apply but hard to find without official documentation.
Update:
If you want to use it together with aot compilation you should manually provide Compiler like
export function createJitCompiler () {
return new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
}
...
providers: [
{ provide: Compiler, useFactory: createJitCompiler}
],
Example
Old version
It might help you:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({moduleFactory, componentFactories}) => {
const compFactory = componentFactories
.find(x => x.componentType === DynamicComponent);
const cmpRef = this.placeholderRef.createComponent(compFactory, 0);
See also
related answer with example
How to use variable to define templateUrl in Angular2
sample code from angular2 source code
Based on yurzui's answer I've come to the following code:
export class MyComponent {
subcompPath = "path-to-subcompMODULE#SubcompClassName";
#ViewChild("placeholder", { read: ViewComponentRef }) placeholderRef: ViewComponentRef;
/* Constructor where Compiler, ComponentFactoryResolver are injected */
loadSubcomponent() {
let [modulePath, componentName] = this.subcompPath.split("#");
(<any>window).System.import(modulePath)
.then((module: any) => module["default"]) // Or pass the module class name too
.then((type: any) => {
return this._compiler.compileModuleAndAllComponentsAsync(type)
})
.then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
const factory = moduleWithFactories.componentFactories.find(x => x.componentType.name === componentName); // Crucial: componentType.name, not componentType!!
let componentRef = this.placeholderRef.createComponent(factory, 0);
});
}
}

Prevent duplicate routes in express.js

Is there a nice way to prevent duplicate routes from being registered in express? I have a pretty large application with hundreds of routes across different files, and it gets difficult to know if I've already registered a certain route when I go to add a new one. For example, I'd like to throw an error when express gets to routes487.js:
File: routes1.js
var ctrl = require('../controllers/testctrl');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi);
app.get('/there', auth.getToken, ctrl.there);
};
File: routes487.js
var ctrl = require('../controllers/testctrl487');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi487);
};
You could try a custom solution by wrapping express methods with the validation. Consider the following modification to your express app:
// route-validation.js
module.exports = function (app) {
var existingRoutes = {}
, originalMethods = [];
// Returns true if the route is already registered.
function routeExists(verb, path) {
return existingRoutes[verb] &&
existingRoutes[verb].indexOf(path) > -1;
}
function registerRoute(verb, path) {
if (!existingRoutes[verb]) existingRoutes[verb] = [];
existingRoutes[verb].push(path);
}
// Return a new app method that will check repeated routes.
function validatedMethod(verb) {
return function() {
// If the route exists, app.VERB will throw.
if (routeExists(verb, arguments[0]) {
throw new Error("Can't register duplicate handler for path", arguments[0]);
}
// Otherwise, the route is saved and the original express method is called.
registerRoute(verb, arguments[0]);
originalMethods[verb].apply(app, arguments);
}
}
['get', 'post', 'put', 'delete', 'all'].forEach(function (verb) {
// Save original methods for internal use.
originalMethods[verb] = app[verb];
// Replace by our own route-validator methods.
app[verb] = validatedMethod(verb);
});
};
You just need to pass your app to this function after creation and duplicate route checking will be implemented. Note that you might need other "verbs" (OPTIONS, HEAD).
If you don't want to mess with express' methods (we don't know whether or how express itself or middleware modules will use them), you can use an intermediate layer (i.e., you actually wrap your app object instead of modifying its methods). I actually feel that would be a better solution, but I feel lazy to type it right now :)

Asynchronous confusion with ItemFileReadStore query

I want to save the results from a query using itemFileReadStore into an array called boxes, but the return value is empty (presumably because fetch is run asynchronously).
The gotItems function builds the array as I want it to, but I can't return that back to myself for any use! I could build the rest of my functionality into the gotItems part, but that would make my code unpretty.
How do I return an array for general use in my JavaScript from the gotItems function?
function getContentFile() {
contentStore = new dojo.data.ItemFileReadStore({
url: '../config/content.json',
preventCache : true
});
var boxes = new Array();
contentStore.fetch({query: {partner : 'enabled'}, onItem: gotItems });
return boxes;
}
function gotItems(item ) {
boxes.push( contentStore.getValue(item,'title') );
console.log( boxes );
return boxes;
}
dojo.addOnLoad( function() {
boxes = getContentFile();
console.log(boxes);
fadeIn('header', 500, 0);
});
Welcome to the world of asynchronous operations.
You'll need to do it with the "continuation-style" programming. ItemFileReadStore's fetch operations is asynchronous -- as you already know by passing the gotItems continuation to it.
contentStore.fetch({query: {partner : 'enabled'}, onItem: gotItems }) will return immediately. Your boxes will be empty at that point (because JavaScript is single-threaded). gotItems is executed after data arrived and subsequent to the function passed to dojo.addOnLoad returning.
You have to put your handling code:
console.log(boxes);
fadeIn('header', 500, 0);
inside the continuation gotItems itself. For example, something like:
function gotItems(item ) {
var boxes = [];
dojo.forEach(item, function(box) {
boxes.push( contentStore.getValue(box,'title') );
});
console.log(boxes); // You probably need to store "boxes" somewhere instead of just logging it
fadeIn('header', 500, 0);
}
Also, the data passed to onItems is an array, so you need to iterate it.
You don't have access to the results when the function returns because as you guessed, the fetch operation executes asynchronously.
You can either put the code that uses the results in your gotItems() function (as answered by Stephen), or you can use Deferreds and Promises. IMHO, that's a better alternative since it lets you organize your code better (once you get used to the idioms of dealing with promises, the code reads more naturally) and it allows you to transparently execute both synchronous and asynchronous operations.
See these two Dojo tutorials on the subject.
In your case, a possible solution involving deferreds would read like:
function getContentFile() {
contentStore = new dojo.data.ItemFileReadStore({
url: '../config/content.json',
preventCache: true
});
var dfd = new dojo.Deferred();
var boxes = new Array();
contentStore.fetch({
query: { partner : 'enabled' },
onItem: function(item) {
boxes.push( contentStore.getValue(item,'title') );
},
onComplete: function() {
// resolve the promise and execute the function in then()
dfd.callback(boxes);
}
});
return dfd;
}
dojo.addOnLoad( function() {
getContentFile().then(function(boxes) {
console.log(boxes);
fadeIn('header', 500, 0);
});
});