I am using WebStorm to write SuiteScript code and the netsuite N/record module (I defined the module as record). I have a function I wrote that will return a record (below is a simplified form of it)
/**
*
* #returns {record.Record} record.Record
*/
function getRecord() {
return rrecord.load({type: 'customrecordtc_login', id: recordId})
}
I am trying to use the JS doc so that it knows that this return is a Record object, i.e. if I called something like
r = getRecord()
I was hoping intelisence would know that I can use something such as
r.getText({...})
etc. But currently I can't get the JSDoc to do that.
Is that something possible and if so how?
The JSDoc that I put doesn't seem to direct it to Record object.
Is there an additional addon (I added in the suitecloud SDK plugin from NetSuite)?
Below is a screen shot of how my IDE looks in a given place and I wondered if there was a way to JSDoc to do something kind of similar with my own utils scripts (or their returns). (to add or change something so the IDE knows to treat a return from a utility function as a specific thing and let it auto complete. For example if I called x=utils.getSomeRecord(). and the IDE would be able to give tips for x. ** display .getField etc. like above screen shot)
Related
When trying to create a new script record within NetSuite, I get the error "Fail to evaluate script: All SuiteScript API Modules are unavailable while executing your define callback". I can't find any real information on what may cause this, and I can't see anything in my custom module that looks suspicious. I can't post the code here as the module is nearly 2,000 lines in length and has some proprietary code in it. As it was with another custom module I built that had issues at the "Upload Script File" stage, if I remove reference to the module in the script the process continues, and then I can go back to the script and return the module reference, wherein everything seems to work correctly afterward.
The only information I found that seemed useful was that the error could be caused by referencing a module outside of the define callback, but that isn't the case. The module has two large objects constructed within, and they're returned from the callback. The only other thing I can think of is that this module calls the other custom module, but I haven't seen anything that says I can't do that.
So, overall, what should I look for to resolve this error? I really cn't seem to find anything useful or that applies to this situation.
EDIT
Ok, so I believe that I discovered the cause is due to the calling of a search function outside of an object/function being returned for the callback. Here's a simplified version of what's happening, since a lot of fields and values are managed:
/**
* custom.module.js
* #NApiVersion 2.x
* #NModuleScope Public
*/
define(['N/search'],
/**
* #param {search} search
*/
function(search) {
var fields = new Array("a","b","c","d","e");
var lValues = search.lookupFields({
type : "customrecord_ng_cs_settings"
, id : "1"
, columns : fields
});
var _values = {
a : lValues.a
, b : lValues.b
, c : lValues.c
, d : lValues.d
, e : lValues.e
};
var _funcs = {
func_a : function() {
// do stuff
}
, func_b : function() {
// do stuff
}
, func_c : function() {
// do stuff
}
};
return {
value : _values
, func : _funcs
};
});
I need to maintain this kind of structure as not everything that gets returned in _values is actually a search/lookup result. Am I going to be forced to encapsulate the construction of this object within a function? And would that cause the lookup to occur every time a value is needed? This is a conversion from a 1.0 script, and this gets loaded and set only once at the beginning so the data is all there the entire time without having to be repeatedly fetched.
So, I see the following options:
Convert the callback output to a function and doing something like
the following at the start of every script:
var _values = customModule.values();
Find some way to rework the code so that any script using the module
can still access values in the following way:
var _a = customModule.values.a;
I'd very much prefer option #2. Is it possible?
You cannot run any SuiteScript module code outside of an entry point. You will need to encapsulate your data retrieval in a function, then invoke that function at the beginning of your entry point.
If you want to avoid multiple fetches, you can leverage memoization in your function, or perhaps N/cache or N/session to store the data.
I wanted to take some time to "clean" a personal app, to remove most of the warnings, etc.
As stated in the title I use the PhpStorm IDE and I have some warnings "Unresolved variable slug" when I use series.slug. The variable series comes from either a JSON from a PHP Class or after an Ajax call.
Is there a way to indicate an object's properties or to link a js variable to a PHP class (like in Twig)?
P.S. In my "Settings > Languages > JS > Code Quality Tools", I have nothing enabled, I just have the "basic" PhpStorm inspection.
If you use some object with keys only known in runtime (generated, received through the ajax call, etc.) in your code, there is no way for the IDE to resolve them using static code analysis.
But you can let the IDE know what your runtime data looks like. Possible solution using JSDoc annotations:
/**
* #typedef {Object} series
* #property {string} slug
* ... other series props here....
*/
...
/**
* function that uses series data
* #param {series} data
*/
function foo (data){...}
See also https://youtrack.jetbrains.com/issue/WEB-17419#comment=27-1058451, https://intellij-support.jetbrains.com/hc/en-us/community/posts/206349469-disable-unresolved-variable-on-json-object-received-by-ajax-call for other possible workarounds
I open a project with file => new => static web project. Name it MyProject and web content folder name is WebContent.
Right click the WebContent directory and add a js directory. Right click the js directory and choose new => other => JavaScript source file and call it DomStuff.js with the following content:
var MyApp={};
MyApp.DomStuff={};
MyApp.DomStuff.someFunction=function(){
return true;
}
MyApp.do<== here it auto completes to DomStuff
Right click the js directory again and choose new => other => JavaScript source file and call it WorkFlow.js
When I type MyApp. then press control + space I get a box saying No Default Proposals. It's like auto complete only works when all the code is in one file.
Right click JavaScript Resources under the Source tab MyProject/WebContent is listed as included all and excluded none.
This is in Eclipse Version: 4.2.1 with Eclipse Web Developer Tools 3.4.1 and JavaScript Development Tools 1.4.1.
For as long as I remember I never got the auto complete to work on code outside the file I am currently working on. Is this normal or am I missing some setting here?
Standard JSDT can seem non deterministic when it come to completions across files. That is because even if the project is configured correctly then the most expensive type of it's source code analysis only occurs on files that are open. Do you get the same result if both files are open?
By the way, there is a fork of JSDT that is a candidate for merging to official JSDT called JSDT+NJSDoc that handles such cross file analysis extremely efficiently (and adds other features): https://bitbucket.org/nexj/webtools.jsdt.core
[update]
Basically Eclipse with JSDT can't do the job. Tried to define all my namespaces with constructor functions but it's all too much trouble.
Trying netbeans now and it'll assist most of the closure library and all of my code. Some things like goog.array won't complete because goog.array is never defined. When adding goog.array=new Object() to the array.js it'll complete goog.array and goog.array.ArrayLike. These modded js file go in your js/libs directory of your project.
If you want the troublesome way to get it to work in Eclipse then keep reading.
It seems that Eclipse has problems with objects declared as object literals. Declaring my complex types with a constructor function seems to do the trick. An instance of the root object has to be created on Window (capital W).
// this would be the way I would define
// a complex property that doesn't need
// more than one instance and is not complex
// enough to put in a separate file
myapp.workflow.objectLiteral={
thisDoesNotCodeAssist:function(){},
neitherDoesThis:22
};
/** if you define the complex property as
* a constructor it will auto complete
* in other files and closure compiler will
* recognize it's type
*/
/** #constructor organizes flow
* used for Eclipse code assist */
var WorkFlow=function(){};
ProwpWithSubs=function(){};
PropWithSubs.prototype.subProp=22;
PropWithSubs.prototype.subFunction=function(){
this.subsub=44;
};
WorkFlow.prototype.propwithsubs=new PropWithSubs();
if(ALLWAYSFALSE){
/**needed for Eclipse code assist
* #constructor
*/
var MyApp=function(){};
MyApp.prototype.workflow=new WorkFlow();
MyApp.prototype.dom=new DOM();
Window.prototype.myapp=new MyApp();
}else{
myapp.workflow=new WorkFlow();
}
[update]
Here are 3 files I use to split up the definition and the implementation a little so it's easier with google closure.
Setting up the main types in types.js
// source: js/mmyapp/types.js
goog.provide("myapp.types");
/** #constructor */
var gooblediegoog=function(){};
/** #constructor */
gooblediegoog.prototype.WorkFlow=function(){};
/** #constructor */
gooblediegoog.prototype.Dom=function(){};
myapp.types=new gooblediegoog();
A file that isn't used at all in my code but tells Eclipse how to auto complete:
// source: js/myapp/forCodeAssist.js
/** #const {boolean} */
var ALLWAYSFALSE=false;
if(ALLWAYSFALSE){
/**needed for Eclipse autocomplete
* #constructor
*/
var MyApp=function(){};
MyApp.prototype.types=new gooblediegoog();
Window.prototype.myapp=new MyApp();
MyApp.prototype.workflow=new myapp.types.WorkFlow();
MyApp.prototype.dom=new myapp.types.Dom();
}
An implementation of workflow:
// source: js/myapp/workflow.js
goog.provide("myapp.workflow");
goog.require("myapp.types");
goog.require("myapp.dom");
/** #returns number|undefined */
myapp.types.WorkFlow.prototype.createOrder=function(){
return myapp.dom.getArticleAmout();
};
myapp.workflow=new myapp.types.WorkFlow();
window['console'].log(myapp.workflow.createOrder());
This can be converted to a myapp.workflow.createOrder=... syntax by replacing myapp.types.WorkFlow.prototype with myapp.workflow, removing myapp.workflow=new myapp.types.WorkFlow() and removing the goog.require("myapp.types"). Maybe this can be automated in the build/compile process if needed.
I am not sure if creating a single object with the help from a constructor function is much more expensive than just having goog.require create myapp.workflow and adding properties to it as I used to do (and as it's done in closure library).
I've created a Dojo module which depends on dojox/data/JsonRestStore like this:
define("my/MyRestStore",
["dojo/_base/declare", "dojox/data/JsonRestStore"],
function(declare, JsonRestStore) {
var x = new JsonRestStore({
target: '/items',
identifier: 'id'
});
...
which is fine. But now I want to have the the uncompressed version of the JsonRestStore code loaded so that I can debug it. I can't find any documentation on how to do this, but since there is a file called 'JsonRestStore.js.uncompressed.js' I changed my code to:
define("my/MyRestStore",
["dojo/_base/declare", "dojox/data/JsonRestStore.js.uncompressed"],
function(declare, JsonRestStore) {
...
thinking that might work.
I can see the JsonRestStore.js.uncompressed.js file being loaded in FireBug, but I get an error when trying to do new JsonRestStore:
JsonRestStore is not a constructor
Should this work?
Is there a way of configuring Dojo to use uncompressed versions of all modules? That's what I really want, but will settle for doing it on a per dependency basis if that's the only way.
Update
I've found a way to achieve what I want to do: rename the JsonRestStore.js.uncompressed.js file to JsonRestStore.js.
However, this seems a bit like a hacky workaround so I'd still be keen to know if there is a better way (e.g. via configuration).
You have two options
1) Create a custom build. The custom build will output a single uncompressed file that you can use for debugging. Think the dojo.js.uncompressed.js but it includes all the extra modules that you use.
OR
2) For a development environment, use the dojo source code. This means downloading the Dojo Toolkit SDK and referencing dojo.js from that in the development environment.
For the projects I work on, I do both. I set up the Dojo configuration so that it can be dynamic and I can change which configuration that I want using a query string parameter.
When I am debugging a problem, I will use the first option just to let me step through code and see what is going on. I use the second option when I am writing some significant js and don't want the overhead of the custom build to see my changes.
I describe this a bit more at
http://swingingcode.blogspot.com/2012/03/dojo-configurations.html
I think the reason for this is due to the fact that the loader declares its class-loads (modules), by the file conventions used. The 1.7 loader is not too robust just yet, ive had similar problems until realizing how to separate the '.' and '/' chars.
Its only a qualified guess; but i believe it has to do with the interpretation of '.' character in the class-name which signifies as a sub-namespace and not module name.
The 'define(/ * BLANK * / [ / * DEPENDENCIES * / ], ...)' - where no first string parameter is given - gets loaded by the filename (basename). The returned declare also has a saying though. So, for your example with jsonrest, its split/parsed as such:
toplevel = dojox
mid = data
modulename = JsonRestStore.js.uncompressed
(Fail.. Module renders as dojox.data.JsonRestStore.js.uncompressed, not dojox.data.JsonRestStore as should).
So, three options;
Load uncomressed classes through <script src="{{dataUrl}}/dojox/data/JsonRestStore.js.uncompressed.js"></script> and work them on dojo.ready
I think modifying the define([], function(){}) in uncompressed.js to define("JsonRestStore", [], function() {}) would do the trick (uncomfirmed)
Use the dojo/text loader, see below
Text filler needed :)
define("my/MyRestStore",
["dojo/_base/declare", "dojo/text!dojox/data/JsonRestStore.js.uncompressed.js"],
function(declare, JsonRestStore) {
...
JsonRestStore = eval(JsonRestStore);
// not 100% sure 'define' returns reference to actual class,
// if above renders invalid, try access through global reference, such as
// dojox.dat...
I've been at this one for a while now:
I'm using both jQuery and Prototype within Redmine, a RoR webapp. They play along well thanks to jQuery's noConflict.
I've got jqGrid working fine too.
Now here's my problem: I'm trying to use the Table Filter plugin by PicNet
but I get the following js error:
this.each is not a function
# line 862 of prototype.js
function collect(iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
}
It's obviously calling a prototype function while it should not, but the plugin code is minimized, and actually compiled with python, so there's no un-minimized version...
I'm not very good with js to start with, and I'm stumped as to why it calls the wrong function...
P.S: I'm using
jQuery 1.4.4
Prototype 1.7
Firebug 1.8.4 for debugging
Update: found the answer myself, see below!
I found the solution in the 'closure' lib, a dependency for Table Filter:
/**
* #define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
* rely on Array.prototype functions, if available.
*
* The Array.prototype functions can be defined by external libraries like
* Prototype and setting this flag to false forces closure to use its own
* goog.array implementation.
*
* If your javascript can be loaded by a third party site and you are wary about
* relying on the prototype functions, specify
* "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
*/
So I downloaded the necessary things (python, closure compiler, ... ) and built the Table Filter code myself with this parameter set to false, and it works.
Now I've got another problem, but I should be ok. If not I'll come back here and ask!
Try wrap the plugin in:
;(function($) {
// This way you secure that $ in this local scope is referring to jQuery and not prototype.
// Original plugin goes here....
})(jQuery);