multiple Async/await JS function - async-await

In my controller, the fn function executes return.suucess() before my variables are properly defined. In this case, the ytb object.
The sails.log of "items" is correct, but "ytb" did not return.
I also tried with Promise.then, and .all. Also, with async.each() and removing the await inside this function.
fn: async function (inputs, exits) {
var url = require('url');
var items = await Collection.find({});
_.each(items, async item => {
if (item.youtube_url) {
var ytbUrl = item.youtube_url;
var regexBaseUrlYtb = new RegExp('(https://www.youtube.com/channel/)');
if (regexBaseUrlYtb.test(ytbUrl)) {
var ytbIdChannel = ytbUrl.replace(regexBaseUrlYtb, '');
var ytbChannel = await sails.helpers.youtubeApi(ytbIdChannel);
item.ytb = {};
Object.assign(item.ytb, ytbChannel);
}
}
sails.log('items: ', item)
});
return exits.success({
items
});
}
I expect that the ytb object will also be passed with the object item at the view, but the items object is passed without the ytb object.
Also, in general, I expect the return exits.success() runs only once the function finished
Thanks.

Related

How to Access Parameters in New Form

I have a custom button that I have the below JavaScript attached to that opens an entity form and I am trying to pass viewName to it.
function sendContextToQC(selectedControl) {
var entityFormOptions = {};
entityFormOptions["entityName"] = "new_qrecipemasteritem";
entityFormOptions["useQuickCreateForm"] = true;
var currentView = selectedControl.getViewSelector().getCurrentView().name;
var formParameters = {};
formParameters["viewName"] = currentView
Xrm.Navigation.openForm(entityFormOptions, formParameters).then(
function (success) {
console.log(success);
},
function (error) {
console.log(error);
});
}
The form opens fine and I have the "Pass Execution Context as first parameter" checked, but I don't know how to access the formparameters object. Is it part of the executionContext? I even tried adding another parameter (formParameters), but that didn't work either.
2/14/23
You can access the form parameters from within the form by using the getFormContext() method. Here's an updated version of your code that shows how to access the formParameters object from within the form:
function sendContextToQC(selectedControl) {
var entityFormOptions = {};
entityFormOptions["entityName"] = "new_qrecipemasteritem";
entityFormOptions["useQuickCreateForm"] = true;
var currentView = selectedControl.getViewSelector().getCurrentView().name;
var formParameters = {};
formParameters["viewName"] = currentView;
Xrm.Navigation.openForm(entityFormOptions, formParameters).then(
function (success) {
var formContext = success.getFormContext();
var viewName = formContext.getAttribute("viewName").getValue();
console.log("View Name: " + viewName);
},
function (error) {
console.log(error);
});
}
In the success callback of the openForm method, you use getFormContext() to get a reference to the form context, and then use that reference to access the viewName attribute. You can then use the getValue() method on the attribute to retrieve its value.
In function Xrm.Navigation.openForm argument formParameters is an object holding field values. The function passes these values to the fields that are (should be) on the form opened by it.
It is possible to pass custom query string parameters to the form. These parameters can be retrieved accessing the window.location object.
See openForm - MS Learn and Configure a form to accept custom querystring parameters - MS Learn.
The (classic) Form Properties dialog has a Parameters tab where custom parameters can be declared. Custom parameters that have not been configured here can only be passed in a plain url by adding an extraqs parameter.
I figured it out. To send parameter:
function sendContextToQC(selectedControl) {
var entityFormOptions = {};
entityFormOptions["entityName"] = "new_qrecipemasteritem";
entityFormOptions["useQuickCreateForm"] = true;
var currentView = selectedControl.getViewSelector().getCurrentView().name;
var formParameters = {};
if (currentView.includes("Master") == true) {
formParameters["isMasterView"] = 1;
}
else {
formParameters["isMasterView"] = 0;
}
Xrm.Navigation.openForm(entityFormOptions, formParameters).then(
function (success) {
console.log(success);
},
function (error) {
console.log(error);
});
}
Then to pick it up onload:
this.hideUsedIn = function(executionContext) {
let formContext = executionContext.getFormContext();
let usedInAttribute = formContext.getAttribute("new_usedin");
let usedInControl = formContext.getControl("new_usedin");
let buttonData = {};
buttonData = Xrm.Utility.getPageContext().input.data;
let isMasterItem = buttonData.isMasterView;
if (isMasterItem === 1){
usedInAttribute.setRequiredLevel("none");
usedInControl.setVisible(false);
}
}

Jasmine spyOn function call inside function under test

I am trying to use Jasmine to test the following function:
var Pdba = Class.create();
Pdba.prototype = {
getChangeGroup: function(userId) {
var query = 'active=true^u_change_group=true^u_organization=false^';
var exGroup = new CompanyGroup();
var groups = exGroup.getGroupsByQuery(userId, query); //want to spy/mock this call
if (groups.next()) {
return groups.sys_id.toString();
}
return '';
}
type: 'Pdba'
};
I want to SpyOn the getGroupsByQuery() call, so that it doesn't make the actual call. Below is a collection of various things I have been trying, mostly just to see if I can "spy" and see that it has been called, then work on overriding so that I can replace the call with my own data.
describe('my suite of getChangeGroup tests', function() {
var expPdba;
var validUserId = 'user1';
var expGrp;
var ggbqMoc
beforeEach(function() {
expPdba = new global.Pdba();
coGrp = new CompanyGroup();
spyOn(coGrp, 'getGroupsByQuery');
ggbqMoc = jasmine.createSpy('getGroupsByQuery');
});
it('should return \'\' for empty userId', function() {
coPdba.getChangeGroup('');
expect(coGrp.getGroupsByQuery).toHaveBeenCalled();
expect(ggbqMoc).toHaveBeenCalled();
});
});
Is this possible or do I need to change the function under test to take a 'CompanyGroup' as a parameter?
Thank you
I'm assuming you are using jasmine v3. The syntax for creating a spy is pretty weird now- you have to pass a string that refers to the name of the variable you want to create a spy for, and then you pass an array of function names that should be spied on.
Try this:
describe('my suite of getChangeGroup tests', function() {
var expPdba;
var validUserId = 'user1';
var expGrp;
var spy;
beforeEach(function() {
expPdba = new global.Pdba();
coGrp = new CompanyGroup();
spy = jasmine.createSpyObj('coGrp', ['getGroupsByQuery'])
});
it('should return \'\' for empty userId', function() {
coPdba.getChangeGroup('');
expect(coGrp.getGroupsByQuery).toHaveBeenCalled();
expect(spy).toHaveBeenCalled();
});
});

Nativescript - Pass array from home-view-model to home.js

I´m having a hard time understanding how to perform this action(as the title says), and maybe someone could help me understand the process, my code is below:
My home-view-model:
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var http = require("http");
function createViewModel() {
http.getJSON("http://myJsonfile").then(function (r) {
var arrNoticias = new ObservableArray(r.data);
return arrNoticias;
}, function (e) {
});
}
exports.createViewModel = createViewModel;
I have done a console.log of the arrNoticias before i have putted it inside a callback function and it returns [object object] etc...and then i have done this:
console.log(arrNoticias.getItem(0).titulo);
and it returns the info i need!.
Then in my home.js file i have this:
var observableModule = require("data/observable")
var ObservableArray = require("data/observable-array").ObservableArray;
var arrNoticias = require('./home-view-model.js');
console.log(arrNoticias.getItem(0).titulo);
and the result in the console is:
TypeError: arrNoticias.getItem is not a function. (In 'arrNoticias.getItem(0)', 'arrNoticias.getItem' is undefined)
My question is, how does this action is perform? passing the data from view-model to the .js file?
Thanks for your time
Regards
As that function send a URL request so probably it's an async function, which is on hold while requesting so that's why you get undefined. Normally, you will want your function that sends a URL request to return a promise. Based on that promise, you will the result as expected after the request is done. So:
function createViewModel() {
return new Promise<>((resolve, reject) => {
http.getJSON("http://myJsonfile").then(function (r) {
var arrNoticias = new ObservableArray(r.data);
resolve(arrNoticias);
}, function(e) {
reject(e);
});
}), (e) => {
console.log(e);
})
}
In home.js:
var homeVM= require('./home-view-model.js');
var arrNoticias;
homeVM.createViewModel().then(function(r) {
arrNoticias = r;
});

How can I pass values loaded by ajax to components?

I'm passing a value as a parameter to a component.
<badge-button params="badge: oarBadge"></badge-button>
Here is the viewModel containing oarBadge:
function AppViewModel() {
var self = this;
self.oarBadge = ko.observable();
$.getJSON('/guy.json', function(data) {
var badge = new Badge('wood oar', data.badges.oar, false);
self.oarBadge(badge);
// self.oarBadge().has() returns true so the badge is being properly created with data
// returned by the ajax call
});
} // AppViewModel()
Here is the Badge constructor:
function Badge(name, has, active) {
var self = this;
self.name = ko.observable(name);
self.has = ko.observable(has);
self.active = ko.observable(active);
self.disabled = ko.computed(function() {
return self.has();
});
self.toggleActive = function() {
self.active(!self.active())
};
self.toggleHas = function() {
self.has(!self.has());
};
}
Here is the component's viewModel:
ko.components.register('badge-button', {
viewModel: function(params) {
var self = this;
self.badge = params.badge();
self.open = function() {
self.badge.toggleHas();
self.badge.toggleActive();
}
},
template:
'<img class="ui image" src="http://fakeimg.pl/300/" data-bind="click: open, css: { disabled: badge.disabled }" >'
});
When the page loads, I get an error telling me that badge is undefined.
Full example: https://gist.github.com/guyjacks/5a8763ff71f90e3fe8b4b153ed9a5283
Try setting a default object before the ajax call is completed, also you should assign the observable itself not the evaluation for the observable, so instead of doing this:
self.badge = params.badge();
You should do it like this:
self.badge = params.badge;
Otherwise your variable won't be updated once the ajax request is completed.
Here is a small example: https://jsfiddle.net/b0bdru1u/1/
Note: As far as I know the disable binding won't work in images

fetch returning promise rather than value

Hopefully the code below communicates the problem clearly. The issue is that in the module which uses the get method of fetchData, the value being returned is the actual Promise, rather than the JSON as desired. Any thoughts on this?
// fetchData.js module
var _ = require('lodash');
function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
return fetch(endpoint1)
.then((endpoint1Response) => {
return endpoint1Response.json()
.then((endpoint1JSON) => {
return fetch(endpoint2)
.then((endpoint2Response) => {
return endpoint2Response.json()
.then((endpoint2JSON) => {
var data = _.merge({}, {json1: endpoint1JSON}, {json2: endpoint2JSON});
console.log('data in fetch', data); // this logs the json
return data;
});
});
});
});
}
exports.get = get;
// module which uses get method of fetchData get
var fetchData = require('fetchData');
var data = fetchData.get();
console.log('returned from fetchData', data); // this logs a Promise
Yes, that's exactly what's supposed to happen. The whole point of promises is that their result value is not immediately available and that doesn't change just because you're obtaining one from a separate module.
You can access the value like this:
var fetchData = require('fetchData');
fetchData.get().then(data =>
console.log('returned from fetchData', data);
);
Also note that you are using promises in a non-idiomatic way and creating a "tower of doom." This is much easier on the eyes and accomplishes the same thing:
function fetchJson(endpoint) {
return fetch(endpoint)
.then(endpointResponse => endpointResponse.json());
}
function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
return Promise.all([fetchJson(endpoint1), fetchJson(endpoint2)])
.then(responses => {
var data = { json1: responses[0], json2: responses[1] };
console.log('data in fetch', data); // this logs the json
return data;
});
}
Edit I haven't used async/await in JavaScript, but to answer your question, I presume this would work:
async function fetchJson(endpoint) {
var res = await fetch(endpoint);
return res.json();
}
async function get() {
var endpoint1 = `/endpoint1`;
var endpoint2 = `/endpoint2`;
var data = {
json1: await fetchJson(endpoint1),
json2: await fetchJson(endpoint2)
};
console.log('data in fetch', data); // this logs the json
return data;
}
// module which uses get method of fetchData get
async function main() {
var fetchData = require('fetchData');
var data = await fetchData.get();
console.log('returned from fetchData', data);
}
return main();

Resources