dojo declare correct way - javascript

file: dojo/dir1/utils/XmlJsonUtils.js
// Author: Rajat Khandelwal
define([
"dojo/_base/declare" // declare
], function(declare){
return declare("dir1.utils.XmlJsonUtils",[],{
parseXml : function (xml) {
var self=this;
var dom = null;
if (window.DOMParser) {
try {
dom = (new DOMParser()).parseFromString(xml, "text/xml");
}
catch (e) { dom = null; }
}
else if (window.ActiveXObject) {
try {
dom = new ActiveXObject('Microsoft.XMLDOM');
dom.async = false;
if (!dom.loadXML(xml)) // parse error ..
window.alert(dom.parseError.reason + dom.parseError.srcText);
}
catch (e) { dom = null; }
}
else
alert("cannot parse xml string!");
return dom;
},
xml2json : function (xmldata)
{
var self=this;
if(xmldata.firstChild==null)
{
return {name:xmldata.nodeName+": (value null)", checked: true}
}
else if(xmldata.firstChild.nodeType==3)
{
return {name:xmldata.nodeName+": "+xmldata.firstChild.nodeValue, checked:true}
}
else
{
var mychildren=[];
var i=0;
var nochildren=xmldata.childElementCount
for(i=0;i<nochildren;i++)
{
var j=self.xml2json(xmldata.childNodes[i])
mychildren[i]=j
}
var ret= {name:xmldata.nodeName, children:mychildren, checked:true}
return ret
}
},
convert2arr : function (result,ctr,res_arr)
{
var self=this;
if(result[ctr].checked[0]==false)
return;
if(result[ctr].children==undefined)
{
var name=result[ctr]['name'][0];
var kv = name.split(': ');
if(kv[1]=="(value null)")
kv[1]="";
res_arr.push.apply(res_arr,["<",kv[0],">",kv[1],"</",kv[0],">"]);
return ctr+1;
}
else
{
var i=ctr;
var new_ctr=ctr;
var no_children=result[ctr].children.length;
res_arr.push.apply(res_arr,["<",result[ctr].name[0],">"])
for(i=0;i<no_children;i++)
{
new_ctr=self.convert2arr(result,result[ctr].children[i]._0,res_arr)
}
res_arr.push.apply(res_arr,["</",result[ctr].name[0],">"]);
return new_ctr;
}
},
convert2xml : function (result)
{
var arr=[]
self.convert2arr(result, 0, arr)
return arr.join('')
}
})
})
but when in the code I require the dir1.utils.XmlJsonUtils, it says Uncaught Error: declare XmlJsonUtils: base class is not a callable constructor. What is the correct way to declare some utility functions.
And those should be like static functions. I don't want to do x=new XmlJsonUtils(); x.parseXml(..). I want to do XmlJsonUtils.parseXml(..)

Your class should not have to have the constructor method defined, dojo.declare is supposed to handle this.. However, doing so doesnt hurt, simply define a blank constructor: function() { }. I suspect youre facing some sort of bug.
The define is as should be, 'define' is used for the require-scope, when running require(["my.module"]), its expected to have a define method, which returns the base class via declare.
file: dojo/dir1/utils/XmlJsonUtils.js:
define([
// requirements
"dojo/_base/declare",
"dir1/utils/Toolkit" // sample in-package dependency
"./Toolkit" // Same as Above
], function (declare) {
// no slash separator, use dot with declare,
// use a reference and return on last line
var Klass = declare(
/// declaredClass: string, moduleUrl with dot-separater + filename /.js//
"dir1.utils.XmlJsonUtils",
/// base class: Array(mixins)
[],
/// class scope
{
_methodMeantToBePrivate: function() { },
randomInstanceMethod: function() { }
}
); // end declare
// set any aliases, which you want to expose (statics)
Klass.StaticCallable = function() {
// careful with your scope access inhere
}
// return the declared class to 'define'
return Klass;
}); // end define
This way (you must have a reference, either pulled in with require or getObject), you could use the StaticCallable function without initializing / constructing an instance of the module. AMD compliant syntax is like so:
require(["dir1/utils/XmlJsonUtils"], function(xmlUtils) {
xmlUtils.StaticCallable();
});
or if previously required
var xmlUtils = dojo.getObject("dir1.utils.XmlJsonUtils")
xmlUtils.StaticCallable();
A specific example could be a versatile class like the following, where both instance and static access is possible. Base class defines 'tools', derived class defines the variables the 'tools' operate on - and if instantiated, the default topics can be subscribed - [ MessageBusBase | MessageBus ]

The issue: in your code.
return declare("dir1.utils.XmlJsonUtils",[],{
parseXml : function (xml) {
Instead of dir1.utils.XmlJsonUtils use dir1/utils/XmlJsonUtils, i.e., use slashes instead of dots in your function declaration.

Related

How to use a variable inside itself

I want to use the variable inside itself and I see other people do it but why does it not work for me?
This is my ES6 file
// Setup module
// ------------------------------
var FullCalendarAdmin = function () {
//
// Setup module components
//
var _componentRender = function () {
// Basic calendar
var _componentFullCalendarAdmin = function (events) {
// Define element
var calendarAgendaViewElement = document.querySelector('.fullcalendar-agenda-admin');
// Initialize
if (calendarAgendaViewElement) {
var calendarAgendaViewInit = new FullCalendar.Calendar(calendarAgendaViewElement, {
plugins: ['dayGrid', 'timeGrid', 'interaction'],
select: function (start, end) {
var title = prompt("Add event:");
var data;
if (title != '') {
data = {
title: title,
start: start,
end: end
};
calendarAgendaViewInit.addEvent(data);
}
}).render();
}
};
//
// Return objects assigned to module
//
return {
init: function () {
_componentRender();
}
}
}();
// Initialize module
// ------------------------------
document.addEventListener('DOMContentLoaded', function () {
FullCalendarAdmin.init();
});
How can I use the calendarAgendaViewInit to call the addEvent function without getting function as an undefined error?
Thanks in advance!
The problem is that you invoke .render immediately.
So your calendarAgendaViewInit is not an instance of FullCalendar.Calendar but the result of the render method.
What you can do is first define the calendarAgendaViewInit variable
var calendarAgendaViewInit = new FullCalendar.Calendar(calendarAgendaViewElement, {
plugins: ['dayGrid', 'timeGrid', 'interaction'],
select: function (start, end) {
var title = prompt("Add event:");
var data;
if (title != '') {
data = {
title: title,
start: start,
end: end
};
calendarAgendaViewInit.addEvent(data);
}
});
and then call calendarAgendaViewInit.render().
This is sort of an expanded explanation to the comment above. It looks like calendarAgendaViewElement is simply a DOM element that you've found and assigned to a variable. The problem here is that you can only call methods on class instantiations that are now objects with methods inside. If you had seen others call addEvent like that, then they were likely calling it on an instantiation of a class meaning that addEvent had been previously declared as part of that class and they are simply calling that method.
See the example below,
If I declare a class as follows:
class Sample {
sayHello(){
console.log('hello')
}
}
Then instantiate a new object of the 'Sample' class:
var sampleClass = new Sample()
Then I can call 'sayHello' by referring to the method inside the object
sampleClass.sayHello() // hello
Hope that helps

AngularJS 1.4x how to extend a factory object

SOLUTION
thanks to hege-hegedus answer below. Applied it to my actual code and works great.
// NOTE : called from another service, but removed function wrap around and angular module setup for brevity sake
// article is serverObject
var articleInstance = new Article(article);
console.log(articleInstance instanceof Article)
// true
console.log(articleInstance.isProduct(article))
// true (in my case)
/*
* Create the constructor from server article object using lodash
*/
ArticleConstructor.$inject = [];
function ArticleConstructor() {
return function(data) {
var keys = ['app_id', 'body', 'headline', 'object_type', 'url', 'status'
];
_.assign(this, _.pick(data, keys));
};
}
/*
* Extend the iief constuctor - ArticleConstruct
*/
Article.$inject = ['ArticleConstructor'];
function Article(ArticleConstructor) {
function ArticleExtended(data) {
ArticleConstructor.call(this, data);
}
// create the new Article object with the ArticleConstructor prototype object and properties
ArticleExtended.prototype = Object.create(ArticleConstructor.prototype);
// Article inherits a constructor property from its prototype i.e. ArticleConstructor
ArticleExtended.prototype.constructor = ArticleExtended;
ArticleExtended.prototype.isProduct = function () {
return this.object_type == 3;
};
ArticleExtended.prototype.hasImage = function () {
return _.has(this, 'image');
};
return ArticleExtended;
}
How do I extend the factory object below. I'm using lodash to auto hydrate the factory constructor, which works great, but now none of my original methods execute e.g. isIcon() returns an error msg - "isIcon is not a function". I've searched for an answer but most constructor examples use the traditional return service; at the end of object, which works fine but then forces me back to more manual approach to building the constructor. I feel like I'm missing something obvious.
Using AngularJS 1.4.8
FACTORY OBJECT TO EXTEND
// AJS factory - the problem child
ImageUnableToExtendFn.$inject = ['IMG_TYPE'];
function ImageUnableToExtendFn(IMG_TYPE) {
Image.prototype.isIcon = function (img) {
return img.type === IMG_TYPE.ICON;
};
return function(data) {
var keys = ['id', 'src', 'alt', 'type'];
_.assign(this, _.pick(data, keys));
};
});
I've tried extending the IIEF factory with angular.extend(), but that doesn't work either (example below):
angular.extend(imageOfUnableToExtendFn, {
isIcon: function(img) {
return img.type === IMG_TYPE.ICON;
}
})
MORE DETAILED OF THE ABOVE FOR REFERENCE PURPOSES
define([
'angular',
'lodash'
], function(angular, _) {
'use strict';
ImageService.$inject = ['ImageClassicFn', 'ImageUnableToExtendFn'];
function ImageService(ImageClassicFn, ImageUnableToExtendFn) {
var imageService = {
images: null,
createInstance: function(serverImageObject) {
var self = this,
imageOfClassicFn,
imageOfUnableToExtendFn,
isIcon,
if (angular.isDefined(serverImageObject)) {
imageOfClassicFn = new ImageClassicFn();
isIcon = imageOfClassicFn.isIcon(serverImageObject);
console.log('IS ICON', isIcon);
// > true of false
imageOfUnableToExtendFn = new ImageUnableToExtendFn(serverImageObject);
// result is a hydrated instance of ImageClassicFn with mapped keys using lodash
isIcon = imageOfClassicFn.isIcon(serverImageObject);
// ERROR - isIcon is not a function
// Attempting to extend manually fails silently
angular.extend(imageOfUnableToExtendFn, {
isIcon: function(img) {
return img.type === IMG_TYPE.ICON;
}
})
isIcon = imageOfClassicFn.isIcon(serverImageObject);
// SAME ERROR - isIcon is not a function
}
}
};
return imageService;
}
ImageClassicFn.$inject = ['IMG_TYPE'];
function Image(IMG_TYPE) {
function Image(id, src, alt, type) {
this.id = id;
this.src = src;
this.alt = alt;
this.type = type;
}
Image.prototype.isIcon = function (img) {
return img.type === IMG_TYPE.ICON;
};
return Image;
});
ImageUnableToExtendFn.$inject = ['IMG_TYPE'];
function Image(IMG_TYPE) {
Image.prototype.isIcon = function (img) {
return img.type === IMG_TYPE.ICON;
};
return function(data) {
var keys = ['id', 'src', 'alt', 'type'];
_.assign(this, _.pick(data, keys));
};
});
return angular.module('content.images', [
])
.constant("IMG_TYPE", {
"ICON": 1,
})
.factory('ImageClassicFn', ImageClassicFn)
.factory('ImageUnableToExtendFn', ImageUnableToExtendFn)
.service('ImageService', ImageService);
});
Subclassing in javascript is a bit tricky. Take a look at this SO post about javascript inheritance.
Basically, this is how you usually do this, wrapped in angular 1.x modules:
ImageClassicFactory.$inject = ['IMG_TYPE'];
function ImageClassicFactory(IMG_TYPE) {
function ImageClassic(id, src, alt, type) {
this.id = id;
this.src = src;
this.alt = alt;
this.type = type;
}
ImageClassic.prototype.isIcon = function (img) {
return img.type === IMG_TYPE.ICON;
};
return ImageClassic;
});
module.factory('ImageClassic', ImageClassicFactory);
ImageExtendedFactory.$inject = ['IMG_TYPE', 'ImageClassic'];
function ImageExtendedFactory(IMG_TYPE, ImageClassic) {
function ImageExtended(id, src, alt, type) {
ImageClassic.call(this, id, src, alt, type);
}
ImageExtended.prototype = Object.create(ImageClassic.prototype);
ImageExtended.prototype.constructor = ImageExtended;
ImageExtended.prototype.isIcon = function (img) {
return img.type === IMG_TYPE.ICON;
};
return ImageExtended;
});
module.factory('ImageExtended', ImageExtendedFactory);

How to call a function on string jQuery

I was reading through fluent api I got a doubt.
I want to take in a string upon which a jQuery function or example is called upon
Function
function compareThis(newString) {
function compare(newString) {
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
}
}
Where it is called as
("alerting").compareThis("alerted").compare(); //alert 'different string'
I want to pass the data/string not as parameter but as called upon.
JSFiddle
Note: I would like to call the function in similar cases like finding date interval etc
You can use prototype to add function to String class:
String.prototype.compare = function(newString){
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
};
I think you should adapt the code for your function, but it's the idea.
Maybe I missed interpreted however, it looks as it you required a form of method chaining to compare string. To do this you can create a variable and create functions inside it.
var compare = (function(){
var thisString;
var stringToCompare;
var create = function(sVal) {
thisString = sVal;
return this;
};
// Public
var compareThis = function(sVal) {
stringToCompare = sVal;
return this;
};
var compare = function(anotherString) {
return thisString == stringToCompare;
};
return {
create: create,
compareThis: compareThis,
compare: compare
};
}());
var b = compare.create('test').compareThis('test').compare();
alert(b);
Example fiddle

How to create a javascript library using a closure

I have written some javascript that I would to encapsulate in a closure so I can use it elsewhere. I would like do do this similar to the way jQuery has done it. I would like to be able to pass in an id to my closure and invoke some functions on it, while setting some options. Similar to this:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
I have read a lot of different posts on how to use a closure to do this but am still struggling with the concept. Here is where I left off:
_snr = {};
(function (_snr) {
function merge(root){
for ( var i = 1; i < arguments.length; i++ )
for ( var key in arguments[i] )
root[key] = arguments[i][key];
return root;
}
_snr.draw = function (options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
}
var options = merge(defaults, options)
return this.each(function() {
//More functions here
});
};
_snr.erase = function () {};
})(_snr);
When ever I try to call the draw function like the first code section above, I get the following error, '_snr is not a function'. Where am I going wrong here?
EDIT
Here is what I ended up doing:
function _snr(id) {
// About object is returned if there is no 'id' parameter
var about = {
Version: 0.2,
Author: "ferics2",
Created: "Summer 2011",
Updated: "3 September 2012"
};
if (id) {
if (window === this) {
return new _snr(id);
}
this.e = document.getElementById(id);
return this;
} else {
// No 'id' parameter was given, return the 'about' object
return about;
}
};
_snr.prototype = (function(){
var merge = function(root) {
for ( var i = 1; i < arguments.length; i++) {
for ( var key in arguments[i] ) {
root[key] = arguments[i][key];
}
}
return root;
};
return {
draw: function(options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
};
options = merge(defaults, options);
return this;
},
erase: function() {
return this;
}
};
})();
I can now call:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
Because you declared _snr as an object and not a function. Functions can have properties and methods, so there's various ways to achieve what you want, for example one of them would be say...
_snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
You can also pass the outer context into a closure to hide your variables from accidentally polluting the global namespace, so like...
(function(global) {
var _snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
//export the function to the window context:
global._snr = _snr;
})(window);
window._snr('#tag').foo('wat');
Happy coding.
Because your _snr is an object, not a function. You have to call it like this:
_snr.draw({
canvasId: '#canvas',
imageSrc: 'someImage.png'
});
When you do _snr('#canvas') that is a function call which is why you're getting that error. _snr is an object with some methods attached to it such as draw() and erase(). The reason jQuery is able to pass arguments into the $ is because they return the $ as a function object which is why we're able to pass it various selectors as arguments.
You are going wrong at the first line _snr = {}
It needs to be
_snr = function(){
selector = arguments[0]||false;
//snr init on dom object code
return _snrChild;
}
Im on a mobile phone but when im on a pc I will maybe fix the whole code c:
Here you have a snr object and that has erase and draw methods. What you intend to do is to write a _snr function which will get an id and return a wrapper object. That returned object should have erase and draw methods. so you can do
var returnedObject = _snr("my_id");
returnedObject.draw("image.png");

Resolve function pointer in $(document).ready(function(){}); by json string name

I have a json object retrieved from server in my $(document).ready(...); that has an string that I would like to resolve to a function also defined within $(document).ready(...); so, for example:
$(document).ready(function{
$.getJSON(/*blah*/,function(data){/*more blah*/});
function doAdd(left,right) {
return left+right;
}
function doSub(left,right) {
return left-right;
}
});
with json string:
{"doAdd":{"left":10,"right":20}}
One way I thought about was creating an associative array of the function before loading the json:
var assocArray=...;
assocArray['doAdd'] = doAdd;
assocArray['doSub'] = doSub;
Using eval or window[](); are no good as the function may not be called for some time, basically I want to link/resolve but not execute yet.
Change your JSON to
{method: "doAdd", parameters : {"left":10,"right":20}}
Then do
var method = eval(json.method);
// This doesn't call it. Just gets the pointer
Or (haven't tried this)
var method = this[json.method]
How about something like this?
$(function(){
// Function to be called at later date
var ressolvedFunc = null;
// Ajax call
$.getJSON(/*blah*/,function(data){
// Generate one function from another
ressolvedFunc = (function(data) {
var innerFunc;
var left = data.left;
var right = data.right;
// Detect action
for (action in data) {
if (action == "doAdd")
innerFunc = function() {
return left + right;
};
else
innerFunc = function() {
return left - right;
};
}
return innerFunc;
})(data);
});
});
The anonymous function returns fresh function, with the new values stored within the enclosure. This should allow you to call the function at later date with the data previously retrieved from the GET request.
Rich
try this:
var doX = (function() {
var
data = [],
getDo = function(action) {
for(var d in data) {
if (data[d][action]) {
return data[d];
}
}
return null;
};
return {
set: function(sdata) {
data.push(sdata);
},
doAdd: function() {
var add = getDo("doAdd");
if (!add)
return 0;
return add.doAdd.left + add.doAdd.right;
},
doSub: function() {
var sub = getDo("doSub");
if (!sub)
return 0;
return sub.doAdd.left + sub.doAdd.right;
}
};
})();
$(document).ready(function{
$.getJSON(/*blah*/,function(data){ doX.set(data); });
});

Categories