<html>
<body>
var el = document.getElementById("tab");
var tab = Table(el, data);
tab.showData();
tab.takeData();
var PieChart=drawPieChart(canvas);
</body>
</html>
<script>
function Table(el, data) {
...
...
return{
showData: function(){
...
...
}
takeData: function(){
var myData=new Array();
for (var i = 0; i < val2; i++) {
myData[i] = document.getElementById('polja' + i).value;
}
}
...
...
};
}
function drawPieChart(canvas){
...
...
return{
getmyData(
);
...
...
};
}
</script>
how can i get myData in function "drawPieChart" except making myData global variable? thx
i was thinking in html make somthing like this PieChart.getMyData(Table.takeData); or something like that
Based on your edit, you can do this:
function Table(el, data) {
var myData = new Array(); // Not global but accessible to every function that gets returned here
return{
// removed unnecessary code
takeData: function(){
for (var i = 0; i < val2; i++) {
myData[i] = document.getElementById('polja' + i).value;
}
},
function drawPieChart(canvas){
// Can access myData here
}
}
}
First, the problems with your code:
You have a block of JavaScript sitting inside <body>. This is not standard, not allowed, and will fail. Specifically, it will be displayed as text on the page, rather than being executed as JavaScript. All JavaScript must be located in one of the following places: an inline <script> tag, an external file that you load with a <script> tag, an event attribute such as onload="...", or an attribute that can run JavaScript, such as href="javascript:..." on <a> elements.
You have a syntax error in the object you're returning from Table(); there is no comma separator between the hash key/value pairs (showData() and takeData()). The comma is required.
You have some uninitialized variables, including val2, data, and canvas. You probably just excerpted your code to omit the relevant initialization, but you should try to present complete self-contained code samples when asking questions on Stack Overflow.
With regard to your question, the object tab you are returning from Table() looks an awful lot like an instance object of a class in any OO language. JavaScript supports the OO paradigm via the prototype pattern, so a sensible approach would be to make Table a full class by defining its prototype. If you do this, you can make tab a full instance of Table by creating it with the new operator, and then you can store the myData array as an attribute on tab. Here's a random demonstration based on your sample code:
http://jsfiddle.net/awytnngu/
HTML:
<div id="tab">
<input id="polja0" value="def1"/>
<input id="polja1" value="def2"/>
<input id="polja2" value="def3"/>
</div>
JS:
function Table(el,data) {
this.el = el;
return this;
}
Table.prototype.showData = function() {
// ...
};
Table.prototype.takeData = function() {
this.myData = new Array();
var val2 = this.el.children.length;
for (var i = 0; i < val2; ++i)
this.myData[i] = document.getElementById('polja'+i).value;
};
Table.prototype.drawPieChart = function(canvas) {
alert(this.myData);
};
var el = document.getElementById('tab');
data = 'whatever';
var tab = new Table(el,data);
tab.showData();
tab.takeData();
canvas = 'whatever';
var PieChart = tab.drawPieChart(canvas);
Just to throw in another possible approach, much simpler than the prototype solution, you can take an OUT parameter on takeData() and then pass it as an argument to drawPieChart():
http://jsfiddle.net/uv8bh6nj/
HTML:
<div id="tab">
<input id="polja0" value="def1"/>
<input id="polja1" value="def2"/>
<input id="polja2" value="def3"/>
</div>
JS:
function Table(el,data) {
return {
showData:function() {
// ...
},
takeData:function(OUT) {
OUT.myData = new Array();
var val2 = el.children.length; // closure
for (var i = 0; i < val2; ++i)
OUT.myData[i] = document.getElementById('polja'+i).value;
}
};
}
function drawPieChart(canvas,myData) {
alert(myData);
}
var el = document.getElementById('tab');
data = 'whatever';
var tab = new Table(el,data);
tab.showData();
var takeDataOUT = {};
tab.takeData(takeDataOUT);
canvas = 'whatever';
var PieChart = drawPieChart(canvas,takeDataOUT.myData);
Related
I'm new to knockout.js (and this is also my first stackoverflow post) and I'm now facing the following problem.
I'm not able to bind the data from web api to a ko.observablearray. Why is the length of this Announcements ko.observablearray always 0? The code works fine with client side data (by adding new announcements)..
Here's the JS-code:
var AnnouncementModel = function () {
var self = this;
self.AnnouncementText = ko.observable();
self.AllDepartmentsBool = ko.observable();
self.Editable = ko.observable(false);
self.Add = function () {
viewModel.Announcements.push(self);
viewModel.AnnouncementToEdit(new AnnouncementModel());
};
self.Delete = function () {
ajaxHelper(announcementsUri + self.ID, 'DELETE').done(
viewModel.Announcements.remove(self));
};
self.Edit = function () {
self.Editable(!self.Editable());
};
}
//The ViewModel
function AnnouncementsViewModel() {
var self = this;
self.InitialData = ko.observableArray();
self.Announcements = ko.observableArray();
self.AnnouncementToEdit = ko.observable(new AnnouncementModel());
self.error = ko.observable();
function getAllAnnouncements() {
ajaxHelper(announcementsUri, 'GET').done(function(data) {
self.InitialData(data);
});
};
getAllAnnouncements();
};
var viewModel = new AnnouncementsViewModel();
ko.applyBindings(viewModel, document.getElementById("announcements-container"));
function createAnnouncement(announcementDto) {
var announcement = new AnnouncementModel();
announcement.AnnouncementText = ko.observable(announcementDto.AnnouncementText);
announcement.AllDepartmentsBool = ko.observable(announcementDto.AllDepartmentsBool);
announcement.Editable = ko.observable(false);
return announcement;
}
var length = viewModel.InitialData.length;
for (i = 0; i < length; i++) {
var newAnnouncement = createAnnouncement(InitialData[i]);
viewModel.Announcements.push(newAnnouncement);
}
The HTML:
<div id="announcements-container" style="display: inline-block; float: right">
<ul id="announcements-list" class="newsticker" data-bind="foreach: Announcements">
<li>
<span data-bind="html: AnnouncementText"></span>
</li>
</ul>
#Html.Partial("_AnnouncementsModal")
</div>
The InitialData gets populated from the api as it should:
GOT IT WORKING! :
Thanks for the quick answers. I got the code working by iterating the data with .forEach(). Another problem was that the initialData didn't get populated in it's current scope so I edited the getAnnouncements function to work like this :
function getAllAnnouncements() {
ajaxHelper(announcementsUri, 'GET').done(function(data) {
data.forEach(function (entry) {
var newAnnouncement = createAnnouncement(entry);
self.Announcements.push(newAnnouncement);
});
});
};
This line is the likely culprit:
var length = viewModel.InitialData.length;
Remember that InitialData is a function. Functions have a length (it's their "arity", the number of formal arguments they have), but the observable function for an observable array's length isn't the array's length..
You probably wanted the length of the array inside it:
var length = viewModel.InitialData().length;
// -------------------------------^^
Your various calls to push on observable arrays work even though length doesn't because Knockout provides push (and several other things) on the observable array function, as James points out.
Similarly, this line:
var newAnnouncement = createAnnouncement(InitialData[i]);
probably wants to be using the array as well (and is missing viewModel. in front of InitialData).
So that whole section probably wants to be refactored a bit:
viewModel.InitialData().forEach(function(entry) {
var newAnnouncement = createAnnouncement(entry);
viewModel.Announcements.push(newAnnouncement);
});
or without forEach (but really, it's nearly 2016, and it's shimmable on obsolete browsers);
var data = viewModel.InitialData();
for (var i = 0; i < data.length; ++i) {
var newAnnouncement = createAnnouncement(data[i]);
viewModel.Announcements.push(newAnnouncement);
}
Side note: Your code (at least as it is in the question) was also falling prey to The Horror of Implicit Globals by not declaring the i that you use in that for loop. I've added a var above, but this is another reason for using forEach to loop through arrays.
You can also use EcmaScript 6 style enumeration as follows:
viewModel.InitialData().forEach(item => {
let newAnnouncement = createAnnouncement(item);
viewModel.Announcements.push(newAnnouncement);
});
I have a page that generates this code MULTIPLE times (content of fields array is different):
<script language="javascript">
try {
var fields = Array();
fields = {id:1,type:text,value:banana};
fields = {id:2,type:text,value:apple};
fields = {id:3,type:number,value:10};
}
</script>
Then I have a function that accesses the fields array:
function functionName(fieldId){
var allFields = eval('fields');
if (allFields) {
for ( var i = 0; i < allFields.length; i++) {
var field = allFields[i];
if(field.id == fieldId){
return field;
}
}
}
return null;
}
The problem is, that I want to use this function in multiple places, accessing the fields array of DIFFERENT script tags. Right now, that function always just gets the first script tag, and I need to be able to scope it to a specific script tag.
Since I cannot change the actual function that generates the array, I CAN change the ID of the script tag like so:
<script language="javascript" id="uniqueID">
try {
var fields = Array();
.....
}
</script>
Now I have changed the function to this:
var script = document.getElementById(uniqueID);
function functionName(fieldId){
var allFields = eval('fields'); // now here...how do I scope this by using the script var that I've created above?
....
}
So, how can I scope my allFields var to a specific script tag?
It's pretty gross, but you can use a regular expression to parse the script block, assuming it always generates it in the same format.
function functionName(scriptID, fieldID) {
var lines = $('#' + scriptID).text().split('\n')
var result;
$.each(lines, function(i, e) {
var match = e.match(/field = (\{.*\})/);
if (match) {
var obj = eval(match[1]);
if (obj.id == fieldID) {
result = obj;
return false; // Tell `$.each` to stop
}
}
}
return result;
}
I have an object with some added methods to its prototype:
var MyClass = function() {
this.name = '';
this.innerData = [];
this._callbacks = {};
};
MyClass.prototype.add = function(data) {
this.innerData.push(data);
};
MyClass.prototype.remove= function(data) {
//This part is working
for (var i=0; i < this.innerData.length; i++) {
if (this.innerData[i].desc === desc) {
this.innerData.splice(i, 1);
}
}
};
MyClass.prototype.on = function(evname, callback) {
if (!this._callbacks[evname]) {
this._callbacks[evname] = $.Callbacks();
}
this._callbacks[evname].add(callback);
};
What I want to do, is a way for triggering ANY methods loaded in the _callbacks object, without the need to do an ugly if (methodIsBound(method)) { .... within all methods of my class.
E.g.:
var c = MyClass();
c.on('add', function() { console.log("Adding data"); });
c.add(someData);
c.add(someData2);
So far I tried doing this adding a check in any method, asking if that method is registered in the callbacks, but that is cumbersome, as for any new method I'd need to add the if (which does not belong to that method functionality), and if any other developer wants to add functionality, they will have to know that additional code.
I checked custom events both in pure JS and jQuery, but I definitely not understanding how to do it.
Does anyone have an idea?
Something in the like (TESTED ON FIREFOX)?
The idea is to take any method in MyClass.prototype and wrap it inside a function that calls a callback and then the original method. So every time you call the method, the saved callback is called, is that what you need?
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>Untitled</title>
<script>
var MyClass = function() {
this.name = '';
this.innerData = [];
};
MyClass.prototype.add = function(data) {
this.innerData.push(data);
};
MyClass.prototype.remove= function(data) {
//This part is working
for (var i=0; i < this.innerData.length; i++) {
if (this.innerData[i].desc === desc) {
this.innerData.splice(i, 1);
}
}
};
MyClass.prototype.on= function(fname,callback) {
var originalFunction=MyClass.prototype[fname];
var givenCallback=callback;
MyClass.prototype[fname]=function() {
givenCallback.call();
var args = Array.prototype.slice.call(arguments);
originalFunction.apply(this,args);
}
};
var c = new MyClass();
c.on('add', function() { console.log("Adding data"); });
c.add("aaa");
c.add("bbb");
</script>
</head>
<body>
</body>
</html>
UPDATE
Whith "fixed" parameters and "this" passed to the callback:
MyClass.prototype.on= function() {
var onArgs = Array.prototype.slice.call(arguments);
var originalFunction=MyClass.prototype[onArgs[0]];
var givenCallback=onArgs[1];
var callbackArgs=onArgs.slice(2);
MyClass.prototype[onArgs[0]]=function() {
var args = Array.prototype.slice.call(arguments);
givenCallback.apply(this,callbackArgs);
originalFunction.apply(this,args);
}
};
var c = new MyClass();
c.on('add', function(a,b) { console.log("Adding data '"+a+"',"+b+" obj:"+this); },"called add",1);
c.add("aaa");
c.add("bbb");
FIDDLE: http://jsfiddle.net/mgcr0jxj/2/
Here is the code:
http://jsfiddle.net/GKBfL/
I am trying to get collection.prototype.add to return a reference such that the final alert will display testing, testing, 123, testing. Is there a way to accomplish what I'm trying to do here?
HTML:
<span id="spantest">testing, testing, 123, testing</span>
JavaScript:
var collection = function () {
this.items = {};
}
collection.prototype.add = function(sElmtId) {
this.items[sElmtId] = {};
return this.items[sElmtId];
}
collection.prototype.bind = function() {
for (var sElmtId in this.items) {
this.items[sElmtId] = document.getElementById(sElmtId);
}
}
var col = new collection();
var obj = {};
obj = col.add('spantest');
col.bind();
alert(obj.innerHTML);
You problem is this line:
this.items[sElmtId] = document.getElementById(sElmtId);
This overwrites the object currently assigned to this.items[sElmtId] with the DOM node. Instead, you should assign the node to a property of that object:
this.items[sElmtId].node = document.getElementById(sElmtId);
That way, obj.node will always refer to the current node:
alert(obj.node.innerHTML);
DEMO
Side note: The problem with your fiddle is also that you execute the code when the DOM is not built yet (no wrap (head)), so it cannot find #spantest. You have to run the code once the DOM is ready, either no wrap (body), onDomRead or onLoad.
Creating a reference like you need is impossible in JavaScript. The closest thing you can get is either a nested or closed object, or just copying it over, like so:
var collection = function() {
this.items = {};
};
collection.prototype.add = function(sElmtId) {
return this.items[sElmtId] = {};
};
collection.prototype.bind = function() {
for(var sElmtId in this.items) {
var element = document.getElementById(sElmtId);
for(var x in element) {
this.items[sElmtId][x] = element[x];
}
}
};
var col = new collection();
var obj = {};
obj = col.add('spantest');
col.bind();
alert(obj.innerHTML);
But it won't be truly "bound". You'll have to use nested objects if you need that kind of functionality, and it will probably defeat the point of your syntactic sugar.
http://jsfiddle.net/GKBfL/7/
I'm trying to translate a PHP class into JavaScript. The only thing I'm having trouble with is getting an item out of an array variable. I've created a simple jsfiddle here. I cannot figure out why it won't work.
(EDIT: I updated this code to better reflect what I'm doing. Sorry for the previous mistake.)
function tattooEightBall() {
this.subjects = ['a bear', 'a tiger', 'a sailor'];
this.prediction = make_prediction();
var that = this;
function array_random_pick(somearray) {
//return array[array_rand(array)];
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*somearray.length)];
return random;
}
function make_prediction() {
var prediction = array_random_pick(this.subjects);
return prediction;
}
}
var test = tattooEightBall();
document.write(test.prediction);
Works fine here, you are simple not calling
classname();
After you define the function.
Update
When you make a call to *make_prediction* , this will not be in scope. You are right on the money creating a that variable, use it on *make_prediction* :
var that = this;
this.prediction = make_prediction();
function make_prediction() {
var prediction = ''; //initialize it
prediction = prediction + array_random_pick(that.subjects);
return prediction;
}
You can see a working version here: http://jsfiddle.net/zKcpC/
This is actually pretty complex and I believe someone with more experience in Javascript may be able to clarify the situation.
Edit2: Douglas Crockfords explains it with these words:
By convention, we make a private that variable. This is used to make
the object available to the private methods. This is a workaround for
an error in the ECMAScript Language Specification which causes this to
be set incorrectly for inner functions.
To see the complete article head to: http://javascript.crockford.com/private.html
You never call classname. Seems to be working fine.
Works for me:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
}());
Were you actually calling the classname function? Note I wrapped your code block in:
([your_code]());
I'm not sure what you're trying to accomplish exactly with the class structure you were using so I made some guesses, but this code works by creating a classname object that has instance data and a pickone method:
function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
this.pickone = function() {
var length = this.list.length;
var random = this.list[Math.floor(Math.random()*length)];
return random;
}
}
var cls = new classname();
var random = cls.pickone();
You can play with it interactively here: http://jsfiddle.net/jfriend00/ReL2h/.
It's working fine for me: http://jsfiddle.net/YznSE/6/ You just didn't call classname(). If you don't call it, nothing will happen ;)
Make it into a self-executing function like this:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length; //<---WHY ISN'T THIS DEFINED??
var random = somearray[Math.floor(Math.random() * length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
})();
var test = tattooEightBall();
document.write(test.prediction);
Should be:
var test = new tattooEightBall(); //forgot new keyword to create object
document.write(test.prediction()); // forgot parens to fire method
and:
this.prediction = make_prediction();
Should be:
this.prediction = make_prediction;