D365 Error when calling function from another JS - javascript

I am having an issue calling JS function from another JS file.
Main JS where the function is defined.
var Common = Common || {};
Common.BaseAction = Common.BaseAction || {};
Common.BaseAction.SetNotification = function (message, level, uniqueId)
{
Xrm.Page.ui.setFormNotification(message, level, uniqueId);
}
Common.BaseAction.clearNotification = function (uniqueId) {
Xrm.Page.ui.clearFormNotification(uniqueId);
}
JS from where I am calling the function
var apItem = apItem || {};
apItem.BaseForm = apItem.BaseForm || {};
apItem.BaseForm.SetName = function ()
{
var bookName = Xrm.Page.getAttribute("ap_bookid").getValue()[0].name;
var condition = Xrm.Page.getAttribute("ap_condition").getText();
if (bookName !== null && condition !== null) {
Xrm.Page.getAttribute("ap_name").setValue(bookName + " - " + condition);
}
}
apItem.BaseForm.CountOverDueBy = function() {
var rentedTill = Xrm.Page.getAttribute("ap_rented_till").getValue();
var nowD = Date.now();
if (rentedTill !== null) {
var overdueBy = parseInt((Date.now() - rentedTill) / 86400000);
if (overdueBy > 0) {
Xrm.Page.getAttribute("ap_overdue_by").setValue(overdueBy);
Common.BaseAction.SetNotification("Book is Overdue by " + overdueBy
+ " Days.", "WARNING", "OverDueWarning");
}
else {
Xrm.Page.getAttribute("ap_overdue_by").setValue(null);
Common.BaseAction.clearNotification("OverDueWarning");
}
}
}
In the entity's form, I have added both above files with common.js being at the top and from the event handler I am calling function apItem.BaseForm.CountOverDueBy
Save + Published and Ctrl + F5 gives following error
ReferenceError: Common is not defined
at Object.apItem.BaseForm.CountOverDueBy (https://<domain>/%7B636651014350000438%7D/WebResources/ap_ItemFormBase.js?ver=2091450722:24:13)
at eval (eval at RunHandlerInternal (https://<domain>/form/ClientApiWrapper.aspx?ver=2091450722:153:1), <anonymous>:1:17)
at RunHandlerInternal (https://<domain>/form/ClientApiWrapper.aspx?ver=2091450722:159:1)
at RunHandlers (https://<domain>/form/ClientApiWrapper.aspx?ver=2091450722:118:1)
at OnScriptTagLoaded (https://<domain>/form/ClientApiWrapper.aspx?ver=2091450722:233:1)
at https://<domain>/form/ClientApiWrapper.aspx?ver=2091450722:202:1
I have tried everything but nothing seems to be working.

The way you register the JS files in form, starting from common.js on top & then ap_ItemFormBase.js should work. But product team made few performance improvements around script files like lazy script loading/parallel script loading. This is little tricky & modern scripting is messy between different clients like UUI & web.
Like explained in blog post, if you set the pre-requisite js as dependency, it will load before you consume it in dependent files.
Open the ap_ItemFormBase.js web resource from solution (not from Form Properties), go to Dependencies tab & add the common.js. This will make sure file is ready before reference is used.

Related

parsley.js extending error message text

By design I'm using one div above form to show errors. For this, case I also need some prefix, before each error message, typically it's text from corresponding label tag.
I'd implemented this as extension, just as the new function, which should be executed manually... but in this case I've lost default ui behaviuor.
So, the question is it possible to implement such behaviour as part of parsley (by extending parsley)?
You can customize where parsley puts the error with the errorsContainer. Could be a function that creates the container within your summary area.
That errors container could also have some sort of other information that you populate.
There is a need for a better error message system, see among others this issue.
I'd implemented desired behavior by extending two internal functions. Not sure it was good idea, but it does exactly what I needed in some particular cases:
(function ($) {
function getFieldLabel(parsleyField, suffix) {
var result = '',
labels = $('label[for=' + parsleyField.$element.prop('id') + ']');
suffix = suffix || '';
if (labels.length) {
result = labels[0].innerText + suffix;
}
return result;
}
window.ParsleyExtend = window.ParsleyExtend || {};
window.ParsleyExtend = $.extend(window.ParsleyExtend, {
// NB! this method replaces parsley's default `_addError`
_addError: function _addError(name, _ref5) {
var message = _ref5.message;
var assert = _ref5.assert;
this._insertErrorWrapper();
this._ui.$errorsWrapper.addClass('filled').append($(this.options.errorTemplate).addClass('parsley-' + name).html(
getFieldLabel(this, ' - ') + (message || this._getErrorMessage(assert))
));
},
// NB! this method replaces parsley's default `_updateError`
_updateError: function _updateError(name, _ref6) {
var message = _ref6.message;
var assert = _ref6.assert;
this._ui.$errorsWrapper.addClass('filled').find('.parsley-' + name).html(
getFieldLabel(this, ' - ') + (message || this._getErrorMessage(assert))
);
}
});
})(jQuery);

Implementation of a client-side 'require' function compatible with node.js

Having decided to implement all of my Javascript libraries as modules compatible with node.js, I've now put together a simple 'require' function to be used on the client side of things. I do realize that there are already plenty of very good implementations out there of this, but most seem fairly heavy-handed so I'm just opting to go with the "roll-your-own" approach.
Here's my 'require.js' file:
/*
A client-side script loading class designed to be compatible with node.js
*/
function require(filename)
{
try
{
var
script = null,
ajax = new XMLHttpRequest(),
index = filename.toLowerCase().indexOf('.js');
if(index <= 0 || index != filename.length - 3)
filename += '.js';
ajax.onload = function()
{
script = this.responseText;
};
ajax.open('GET', filename, false);
ajax.send();
return _isolate_script_(script);
}
catch(error)
{
return null;
}
}
function _isolate_script_(_script_)
{
return (new Function
(
'var exports = {}, module = {exports : null}; '
+ _script_ +
'; return module.exports || exports;'
)).call();
}
Example module ('example.js'):
/*
Example of a multiple-class export file
*/
exports.metallic = function(value)
{
return (Math.sqrt(4 + value * value) + value) / 2;
}
exports.inverse_metallic = function(value)
{
return exports.metallic(-value);
}
Example of a module consumer ('main.js'):
function main()
{
var
example = require('example');
if(example != null)
{
var
value = Math.floor(Math.random() * 100) + 1;
alert
(
'example.metallic(' + value + ') = ' + example.metallic(value)
+ '\n' +
'example.inverse_metallic(' + value + ') = ' + example.inverse_metallic(value)
);
}
else
alert('Error: cannot load "example"');
}
Finally, a basic HTML file that invokes our page logic:
<!DOCTYPE html>
<html>
<head>
<script src = 'require.js'></script>
<script src = 'main.js'></script>
</head>
<body onload = "main()"></body>
</html>
So my specific question is simply whether or not I've implemented everything correctly, and moreover if the framework and use-cases seem kosher enough?
It seems that you are trying to reimplement Browserify:
Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.
If you want to implement a similar thing yourself then take a look at the Browserify source code.
See: https://github.com/substack/node-browserify
I'm just going to answer this myself.
The main issue is that older browsers sometimes freeze-up with synchronous ajax requests. Simply launching the entire 'main' function in it's own thread seems to be a good solution for that. That is, if some unforeseen issue arises with loading scripts then the page itself may not be responsive, but the browser should at least be unaffected. In other words, something along the lines of this:
<!DOCTYPE html>
<html>
<head>
<script src = 'require.js'></script>
<script src = 'main.js'></script>
</head>
<body onload = "setInterval(main)"></body>
</html>
As to concerns about throughput, I've now tested this with huge amounts of data (~100 MB) and it appears to make little difference on performance as to whether the code was loaded with a script tag or via the ajax call. All major browsers on two different operating systems and the results were pretty much the same. So unless I see convincing evidence to the contrary I'm just going to assume this to be the rule rather than the exception.
All that said, I'm certainly still open to any comments or criticisms that may be.
If you want module caching as well try this:
let parent = "";
let cache = {};
/**
* Load a JavaScript text synchronously.
*
* #param url The url.
*/
function cludge(url) {
/* resolve and check */
if (parent !== "")
url = new URL(url, parent).href;
let map = cache[url];
if (map !== undefined)
return map;
map = {};
cache[url] = map;
/* load and execute */
let back = parent;
try {
parent = url;
let request = new XMLHttpRequest();
request.open('GET', url, false);
request.send();
let fun = new Function("exports", "require", request.responseText);
fun(map, cludge);
} finally {
parent = back;
}
return map;
}
But the name resolution is not as elaborate as in nodejs.

Including objects from external .js files

I have been searching for many hours over several days for this answer and though there are many topics on how to include files in a project (also here at Stack Overflow), I have not yet found THE solution to my problem.
I'm working on a project where I want to include one single object at a time, from many different files (I do not want to include the files themselves, only their content). All the object in all the files have the same name, only the content is different.
It is important that I do not get a SCRIPT tag in the head section of the page as all the content from the files will have the same names. None of the files will have functions anyways, only one single object, that will need to be loaded one at the time and then discarded when the next element is loaded.
The objects will hold the data that will be shown on the page and they will be called from the menu by an 'onclick' event.
function setMenu() // The menu is being build.
{
var html = '';
html += '<table border="0">';
for (var i = 0; i<menu.pages.length; i++)
{
html += '<tr class="menuPunkt"><td width="5"></td><td onclick="pageName(this)">'+ menu.pages[i] +'</td><td width="5"></td></tr>';
}
// menu is a global object containing elements such as an array with
// all the pages that needs to be shown and styling for the menu.
html += '</table>';
document.getElementById("menu").innerHTML = html;
style.setMenu(); // The menu is being positioned and styled.
}
Now, when I click on a menu item the pageName function is triggered and I'm sending the HTML element to the function as well, it is here that I want the content from my external file to be loaded into a local variable and used to display content on the page.
The answer I want is "How to load the external obj into the function where I need it?" (It may be an external file, but only in the term of not being included in the head section of the project). I'm still loading the the file from my own local library.
function pageName(elm) // The element that I clicked is elm.
{
var page = info.innerHTML; // I need only the innerHTML from the element.
var file = 'sites/' + page + '.js'; // The file to be loaded is created.
var obj = ?? // Here I somehow want the object from the external file to be loaded.
// Before doing stuff the the obj.
style.content();
}
The content from the external file could look like this:
// The src for the external page: 'sites/page.js'
var obj = new Object()
{
obj.innerHTML = 'Text to be shown';
obj.style = 'Not important for problem at hand';
obj.otherStuff = ' --||-- ';
}
Any help will be appreciated,
Molle
Using the following function, you can download the external js file in an ajax way and execute the contents of the file. Please note, however, that the external file will be evaluated in the global scope, and the use of the eval is NOT recommended. The function was adopted from this question.
function strapJS(jsUrl) {
var jsReq = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
if (jsReq === null) {
console.log("Error: XMLHttpRequest could not be initiated.");
}
jsReq.onload = function () {
try {
eval(jsReq.responseText);
} catch (e) {
console.log("Error: The script file contains errors." + e);
}
};
try {
jsReq.open("GET", jsUrl, true);
jsReq.send(null);
} catch (e) {
console.log("Error: Cannot retrieving data." + e);
}
}
JSFiddle here
Edit: 1
After some refactoring, I came up with this:
function StrapJs(scriptStr, jsObjName) {
var self = this;
self.ScriptStr = scriptStr;
self.ReturnedVal = null;
function _init() {
eval(self.ScriptStr);
self.ReturnedVal = eval(jsObjName);
}
_init();
}
You can then get the script string any way you want and just instantiate a new StrapJs object with the script string and name of the object to return inside the script string. The ReturnedVal property of the StrapJs object will then contain the object you are after.
Example usage:
var extJS = "var obj = " +
"{ " +
" innerHTML : 'Text to be shown', " +
" style : 'Not important for problem at hand', " +
" otherStuff : ' --||-- ' " +
"}; ";
var extJS2 = "var obj = " +
"{ " +
" innerHTML : 'Text to be shown 2', " +
" style : 'Not important for problem at hand 2', " +
" otherStuff : ' --||-- 2' " +
"}; ";
var strapJS = new StrapJs(extJS, 'obj');
var strapJS2 = new StrapJs(extJS2, 'obj');
console.log(strapJS.ReturnedVal.innerHTML);
console.log(strapJS2.ReturnedVal.innerHTML);
See it in action on this fiddle

Appending to External Browser Window

I have a Windows app that contains a browser control that loads pages from my website. However, due to the Windows app, I cannot debug Javascript in the usual ways (Firebug, console, alerts, etc).
I was hoping to write a jQuery plug-in to log to an external browser window such that I can simply do something like:
$.log('test');
So far, with the following, I am able to create the window and display the templateContent, but cannot write messages to it:
var consoleWindow;
function getConsoleWindow() {
if (typeof (consoleWindow) === 'undefined') {
consoleWindow = createConsoleWindow();
}
return consoleWindow;
}
function createConsoleWindow() {
var newConsoleWindow = window.open('consoleLog', '', 'status,height=200,width=300');
var templateContent = '<html><head><title>Console</title></head>' +
'<body><h1>Console</h1><div id="console">' +
'<span id="consoleText"></span></div></body></html>';
newConsoleWindow.document.write(templateContent);
newConsoleWindow.document.close();
return newConsoleWindow;
}
function writeToConsole(message) {
var console = getConsoleWindow();
var consoleDoc = console.document.open();
var consoleMessage = document.createElement('span');
consoleMessage.innerHTML = message;
consoleDoc.getElementById('consoleText').appendChild(consoleMessage);
consoleDoc.close();
}
jQuery.log = function (message) {
if (window.console) {
console.log(message);
} else {
writeToConsole(message);
}
};
Currently, getElementById('consoleText') is failing. Is what I'm after possible, and if so, what am I missing?
Try adding
consoleDoc.getElementById('consoleText');
right before
consoleDoc.getElementById('consoleText').appendChild(consoleMessage);
If the line you added is the one that fails, then that means consoleDoc is not right, if the next line is the only one that fails then ..ById('consoleText') is not matching up
If I don't close() the document, it appears to work as I hoped.

Get the url of currently executing js file when dynamically loaded

So I'm trying to load a script dynamically and figure out the URL path at which that script was loaded. So some guy gave me a pretty awesome solution to this problem if the scripts are statically loaded ( How to get the file-path of the currently executing javascript code ). But I need a dynamically loaded solution. For example:
$(function()
{ $.getScript("brilliant.js", function(data, textStatus)
{ // do nothing
});
});
where "brilliant.js" has:
var scripts = document.getElementsByTagName("script");
var src = scripts[scripts.length-1].src;
alert("THIS IS: "+src);
Ideally this should either print out "brilliant.js" or "〈hostname+basepath〉/brilliant.js"
Currently brilliant.js works for statically included scripts, but not for scripts included dynamically (like with $.getScript). Anyone have any ideas? Is there somewhere in the dom that stores all the scripts that have been loaded?
EDIT: Andras gave a pretty good solution, though it probably only works for jQuery. Since that's probably the most popular library, and definitely what I'm going to be using. It can probably be extended for other libraries as well. Here's my simplified version:
var scriptUri;
curScriptUrl(function(x)
{ scriptUri = x;
alert(scriptUri);
});
function curScriptUrl(callback)
{ var scripts = document.getElementsByTagName("script");
var scriptURI = scripts[scripts.length-1].src;
if(scriptURI != "") // static include
{ callback(scriptURI);
}else if($ != undefined) // jQuery ajax
{ $(document).ajaxSuccess(function(e, xhr, s)
{ callback(s.url);
});
}
}
When your script gets loaded with jQuery (and I guess other frameworks as well), your script will become indistinguishable from a script that was originally in the HTML document. jQuery makes a request reaching out for your script and puts back the reply as the text child of a <script> node. Your browser has no way of knowing where it originated from, whether it was modified before inserted, etc. It is just a script node as far as she is concerned.
There can be workarounds, however. In the case of jQuery, you can hook up to the ajax events and exploit the fact that they are called right after your script executes. Basically, this would yield "brilliant.js" in your example:
var handler = function (e, xhr, s) {
alert(s.url);
}
$(document).ajaxSuccess(handler);
A more elaborate one:
(function ($, undefined) {
/* Let's try to figure out if we are inlined.*/
var scripts = document.getElementsByTagName("script");
if (scripts[scripts.length - 1].src.length === 0) {
// Yes, we are inlined.
// See if we have jQuery loading us with AJAX here.
if ($ !== undefined) {
var initialized = false;
var ajaxHandler = function (e, xhr, s) {
if (!initialized) {
initialized = true;
alert("Inlined:" + s.url);
initmywholejsframework();
}
}
//If it is, our handler will be called right after this file gets loaded.
$(document).ajaxSuccess(ajaxHandler);
//Make sure to remove our handler if we ever yield back.
window.setTimeout(function () {
jQuery(document).unbind("ajaxSuccess", ajaxHandler);
if (!initialized) {
handleInlinedNonjQuery();
}
}, 0);
}
} else {
//We are included.
alert("Included:" + scripts[scripts.length - 1].src);
initmywholejsframework();
}
//Handle other JS frameworks etc. here, if you will.
function handleInlinedNonjQuery() {
alert("nonJQuery");
initmywholejsframework();
}
//Initialize your lib here
function initmywholejsframework() {
alert("loaded");
}
})(jQuery);
B T, sorry if this doesn't help, but I'm curious why you would need to do this? The reason I'm asking is I don't see why you can't just use the relative file paths to load these files? Finding out where you're located could be done with window.location, but why would you? And as for loading them, can't you make an ajax call to the file and then eval them?
This will work in every browser except IE and doesn't depend on assuming what the name of a file is:
var getErrorLocation = function (error) {
var loc, replacer = function (stack, matchedLoc) {
loc = matchedLoc;
};
if ("fileName" in error) {
loc = error.fileName;
} else if ("stacktrace" in error) { // Opera
error.stacktrace.replace(/Line \d+ of .+ script (.*)/gm, replacer);
} else if ("stack" in error) { // WebKit
error.stack.replace(/at (.*)/gm, replacer);
loc = loc.replace(/:\d+:\d+$/, ""); // remove line number
}
return loc;
};
try {
0();
} catch (e) {
var scriptURI = getErrorLocation(e);
}
alert("THIS IS: " + scriptURI);

Categories