Cannot add item to observableArray - javascript

This should be a pretty trivial question, but it's getting me crazy!
I't like step 1 of the tutorial, but I can't it to work.... so this is my code:
/* Model definition */
var PrivateSalesMenuModel = function () {
var self = this;
this.privateSales = ko.observableArray();
this.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
var PrivateSaleMenuItem = function (ps) {
this.title = ps.Description;
this.hasActual = ps.HasActual;
this.hasSale = ps.HasSale;
this.listaId = ps.ListaId;
this.isSelected = false;
};
/* end model definition*/
var privateSalesMenuModel = new PrivateSalesMenuModel();
ko.applyBindings(privateSalesMenuModel);
Pretty simple... I have an object that represent my model, that is a collection of others objects, called PrivateSaleMenuItem.
Problem is that addPrivateSale didn't work as expected. Somewhere in the code I do
privateSalesMenuModel.addPrivateSale(ps);
where ps is an object created by other JavaScript functions... anyway is exactaly the object I need in the constructor of PrivateSaleMenuItem, so it's consistency is not the problem.
The problem seems o be that self.privateSales.push(newPs); doesn't work... after that inocation, the number of privateSalesMenuModel.privateSales is still 0.
Why is that?
Edited
I put toghether an example in jsFiddle with this same exact code, and it works fine, so I suspect something in my page make the push method of observableArray stop working... how can I find out what it is?
ops... the link of jsfiddle http://jsfiddle.net/YBHf5/

It looks like you are mixing this and self in your code here:
var PrivateSalesMenuModel = function () {
var self = this;
this.privateSales = ko.observableArray();
this.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
Changing them to all use self fixes it like this:
var PrivateSalesMenuModel = function () {
var self = this;
self.privateSales = ko.observableArray();
self.addPrivateSale = function (privateSale) {
var newPs = new PrivateSaleMenuItem(privateSale);
self.privateSales.push(newPs);
};
};
Please see working fiddle here:
http://jsfiddle.net/YBHf5/1/

Problem solved.... it was a trivial problem indeeed, with a trivial solution: I just needed to push the external script declaration at the end of the page.
I had the script (in which the code provided on the question is present) at the top of the page, inside the body, but above anything else.
Just putting the script reference at the end of the body and everything works as expected again.
Sorry for wasting your time... but this little issue is very hard to find out, 'cause the reason is not clear at all while debugging

Related

Swapping hardcoded arguments for variables

I've got a functional script on my site that allows me to open a link in a new window when a specific class is added to the link. I need a lot of those on my site though so I figured I'd make the script a bit easier to edit by working with variables.
In the process of changing out hardcoded strings for variables my script stopped working though. The only one that works is the var where I set the url.
I'm learning that ${} doesn't work everywhere. Hope that someone can point out where my thinking is wrong. Also hope that I got the terminology right, trying to learn though! :-)
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
function ${function1Name}() {
window.open(function1Url, "_blank", "height=200");
}
jQuery("${function1Class}").click(function(){
${function1Name}()
});
None of your uses of ${} are valid JavaScript syntax.
Your function declaration van be replaced with:
window[function1Name] = function () {
window.open(function1Url, "_blank", "height=200");
}
Please note that the function will no longer be hoisted when declared this way, so order of operation matters.
The click handler can be written as follows:
jQuery(function1Class).click(function() { // Note that I just used the variable there.
window[function1Name]();
});
There is a ${} concept in JavaScript, but that is only in template literals:
const myVariable = "Foo";
const message = `myVariable contains: "${myVariable}"!`;
console.log(message);
There's several syntax issues here.
Firstly, function ${function1Name}() is not valid syntax. Function names must be defined before runtime. If you want to dynamically access a function, place it in an object and set the key with the variable reference.
Secondly, ${function1Name}() is again not valid syntax. You cannot invoke a function like that dynamically. Referring to the suggestion above, you can access an object dynamically so the first point fixes this problem.
Thirdly, string interpolation only works within template literals, so you need to delimit the string with backticks: ``. However it's completely redundant here as you can just use $(function1Class)
With those issues in mind, here's an updated example:
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
var funcObj = {
[function1Name]: function() {
console.log(`function called, window would open here containing ${function1Url}...`);
// window.open(function1Url, "_blank", "height=200");
}
}
$(function1Class).click(function() {
funcObj[function1Name]()
});
/*
alternative using a template literal, although note that it's redundant here
$(`${function1Class}`).click(function() {
funcObj[function1Name]()
});
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Click me
One last thing to note is that no version of IE supports template literals, so be sure of your browser support requirements before using them.
So cool, I got it to work!
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
I can now add a bunch of url's and corresponding classes as was my intention. Super happy about that.
A follow up question though, as I'll have to experiment with what the ideal window parameters will be I'm trying to make those arguments variables as well. I've tried the examples of how to insert a variables output from the functional code but those methods don't work there. Here's that code:
var windowWidth = 250
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=[windowWidth],height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
How would I insert the variables value (2nd line of Function1) there ?

Javascript Module Pattern accessing updated parameter value

I've tried to search for an answer to my question, but I'm starting to think that, given the lack of results, I'm obviously not expressing the question properly. With that in mind, apologies if the title of this post is misleading, I am still very much learning.
I have a simplified version of my code below
var testData = ['a', 'b']
var addReceiver = (function () {
dataReceiver = function (dataInput) {
t = this
t.data = dataInput;
console.log(t.data);
t.interval = setInterval(function () {
t.update();
}, 1000);
t.stopUpdate = function () { clearInterval(t.interval); };
t.update = function () {
//t.data = dataInput;
console.log(t.data);
};
};
})()
var testLogger = new dataReceiver(testData);
</script>
The behaviour that I wish to emulate is when I type into the console
testData = ['c','d','e']
for testLogger to log the array ['c','d','e'] every second rather than ['a','b'].
I could change
t.data = dataInput to t.data = testData
to achieve this, but that would obviously be pointless, or else every new instance I created of dataReceiver would have a reference to the same data input.
So, my question is, how would I need to change the code to provide the testLogger vairable (or any instance of dataReceiver) access to a variable outside of its local scope?
Use the object that you have created instead of accessing the global variable,
var testLogger = new dataReceiver(testData);
testLogger.data = [1,2,3,4];
Now you will be able to print the newly injected data. I mean the setInterval will print the updated value.

Struggling with a bug that knockout.js me

I fear this is something as embarrassing as a typo, but since I´m stuck on this and quite desperate I´m willing to pay with pride. ;)
This is my case:
Task = function (data) {
var self = this;
self.TaskId = data.TaskId;
self.TaskName = ko.observable(data.TaskName);
}
ViewModel = function () {
var self = this;
self.Tasks = ko.observableArray();
self.SelectedTask = ko.observable();
}
$.getJSON("/myService/GetAllTasks",
function (tData) {
var mappedTasks = $.map(tData, function (item) {
return new Task(item)
});
self.Tasks(mappedTasks); // Populate Tasks-array...
});
self.newTaskItem = function () {
var newitem = new Task({
TaskId: -1,
TaskName: "enter taskname here"
});
self.Tasks.push(newitem); // THIS ONE CRASH
self.Tasks().push(newitem); // BUT SUBSTITUTED WITH THIS ONE IT RUNS ON...
self.editTaskItem(newitem);
};
self.editTaskItem = function (item) {
self.SelectedTask(item); // UNTIL TIL LINE WHERE IT CRASHES FOR GOOD...
self.showEditor(true); // makes Task-edior visible in HTML
};
I also hava an "self.SelectedTask.subscription" in my file, but leaving it out of the code makes no difference.
I also should mention that my database table is empty, so the getJSON returns no data to the mappedTasks, leaving self.Tasks() = [ ] (according to Firebug)
I have fixed the incorrectly closed tags in my code.
Part 2:
Decided after a while to redo my code from the starting point. It got me one step further.
The code now stops on the second of these lines (in "self.newTaskItem"):
self.Tasks.push(newitem);
self.SelectedTask(newitem); // Here it fails.
These two observables are connected in my HTML like this:
<select data-bind="options: Tasks, optionsText: '$root.TaskName', value: SelectedTask"</select>
It looks like your ViewModel() function never gets closed. Add a closing } to wherever you want that function declaration to end. It looks to me (based on your formatting) that you want this:
ViewModel = function () {
var self = this;
self.Tasks = ko.observableArray();
self.SelectedTask = ko.observable();
}
Additionally, you need to close your$.getJson call with a );:
$.getJSON("/myService/GetAllTasks",
function (tData) {
var mappedTasks = $.map(tData, function (item) {
return new Task(item)
});
self.Tasks(mappedTasks); // Populate Tasks-array...
});
I am not 100% sure what your problem is or what error you are getting but this is what I would do - change your Task = function to function Task -
function Task(data) {
var self = this;
self.TaskId = data.TaskId;
}
By saying Task = function without using a var in front of it you are registering Task in the global namespace, not a good idea. Same thing with your view model... Fix it if you can still...
self.newTaskItem = function () {
var newitem = new Task({
// Your Task is looking for a TaskId, not a TextBatchId
TaskId: 1
});
self.Tasks.push(newitem);
self.editTaskItem(newitem);
};
Also, you are creating a TextBatchId where I think your Task object is looking for a TaskId. Fix that, or if you are doing it on purpose for some reason please show your view code and give a better explanation of what is going wrong and what errors you see.
(assuming the unclosed stuff isn't present in your real code)
In Task, TaskId isn't an observable, so when you set SelectedTask to a particular task your editor fields won't properly update (it's a fairly common mistake to assume that the elements of an observableArray are themselves observable, but they aren't unless you explicitly make them so).

strange javascript IE bug, variable in class clears

I've got a problem, so:
var pager = new Array();
var Imtech = {};
Imtech.Pager = function() {
this.paragraphs = '';
this.showPage = function() {
console.log(this.paragraphs.html());
}
}
What i do:
pager[0] = new Imtech.pager();
pager[0].paragraphs = $('some obj name here');
pager[0].showPage();
pager[0].showPage();
So, i ve got arr of paginating classes;
When i call method pager[0].showPage(); - its all ok
But when i try to make it again pager[0].showPage() - i got empty result...
Even got no ideas where is mistake; and var clears not only after console.log(), but after any operation...
How I decided problem:
So I made a very very bad version, but for ie it works fine;
The problem was, that after first usage of stored DOM element, all innerHTML cleared, but objects still existed; I decided to store innerHTML like a text string; smth like:
pager[0].innerHTML = $('some obj name here').html();
And when i need this inneHTML like obj I can make obj, so it's not brilliant variant, but in ie (& other browsers) works fine.

Functional object basics. How to go beyond simple containers?

On the upside I'm kinda bright, on the downside I'm wracked with ADD. If I have a simple example, that fits with what I already understand, I get it. I hope someone here can help me get it.
I've got a page that, on an interval, polls a server, processes the data, stores it in an object, and displays it in a div. It is using global variables, and outputing to a div defined in my html. I have to get it into an object so I can create multiple instances, pointed at different servers, and managing their data seperately.
My code is basically structured like this...
HTML...
<div id="server_output" class="data_div"></div>
JavaScript...
// globals
var server_url = "http://some.net/address?client=Some+Client";
var data = new Object();
var since_record_id;
var interval_id;
// window onload
window.onload(){
getRecent();
interval_id = setInterval(function(){
pollForNew();
}, 300000);
}
function getRecent(){
var url = server_url + '&recent=20';
// do stuff that relies on globals
// and literal reference to "server_output" div.
}
function pollForNew(){
var url = server_url + '&since_record_id=' + since_record_id;
// again dealing with globals and "server_output".
}
How would I go about formatting that into an object with the globals defined as attributes, and member functions(?) Preferably one that builds its own output div on creation, and returns a reference to it. So I could do something like...
dataOne = new MyDataDiv('http://address/?client');
dataOne.style.left = "30px";
dataTwo = new MyDataDiv('http://different/?client');
dataTwo.style.left = "500px";
My code is actually much more convoluted than this, but I think if I could understand this, I could apply it to what I've already got. If there is anything I've asked for that just isn't possible please tell me. I intend to figure this out, and will. Just typing out the question has helped my ADD addled mind get a better handle on what I'm actually trying to do.
As always... Any help is help.
Thanks
Skip
UPDATE:
I've already got this...
$("body").prepend("<div>text</div>");
this.test = document.body.firstChild;
this.test.style.backgroundColor = "blue";
That's a div created in code, and a reference that can be returned. Stick it in a function, it works.
UPDATE AGAIN:
I've got draggable popups created and manipulated as objects with one prototype function. Here's the fiddle. That's my first fiddle! The popups are key to my project, and from what I've learned the data functionality will come easy.
This is pretty close:
// globals
var pairs = {
{ div : 'div1', url : 'http://some.net/address?client=Some+Client' } ,
{ div : 'div2', url : 'http://some.net/otheraddress?client=Some+Client' } ,
};
var since_record_id; //?? not sure what this is
var intervals = [];
// window onload
window.onload(){ // I don't think this is gonna work
for(var i; i<pairs.length; i++) {
getRecent(pairs[i]);
intervals.push(setInterval(function(){
pollForNew(map[i]);
}, 300000));
}
}
function getRecent(map){
var url = map.url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
function pollForNew(map){
var url = map.url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(map.div);
elt.innerHTML = content;
}
and the html obviously needs two divs:
<div id='div1' class='data_div'></div>
<div id='div2' class='data_div'></div>
Your 'window.onload` - I don't think that's gonna work, but maybe you have it set up correctly and didn't want to bother putting in all the code.
About my suggested code - it defines an array in the global scope, an array of objects. Each object is a map, a dictionary if you like. These are the params for each div. It supplies the div id, and the url stub. If you have other params that vary according to div, put them in the map.
Then, call getRecent() once for each map object. Inside the function you can unwrap the map object and get at its parameters.
You also want to set up that interval within the loop, using the same parameterization. I myself would prefer to use setTimeout(), but that's just me.
You need to supply the loadResource() function that accepts a URL (string) and returns the HTML available at that URL.
This solves the problem of modularity, but it is not "an object" or class-based approach to the problem. I'm not sure why you'd want one with such a simple task. Here's a crack an an object that does what you want:
(function() {
var getRecent = function(url, div){
url = url + '&recent=20';
// do stuff here to retrieve the resource
var content = loadResoucrce(url); // must define this
var elt = document.getElementById(div);
elt.innerHTML = content;
}
var pollForNew = function(url, div){
url = url + '&since_record_id=' + since_record_id;
var content = loadResoucrce(url); // returns an html fragment
var elt = document.getElementById(div);
elt.innerHTML = content;
}
UpdatingDataDiv = function(map) {
if (! (this instanceof arguments.callee) ) {
var error = new Error("you must use new to instantiate this class");
error.source = "UpdatingDataDiv";
throw error;
}
this.url = map.url;
this.div = map.div;
this.interval = map.interval || 30000; // default 30s
var self = this;
getRecent(this.url, this.div);
this.intervalId = setInterval(function(){
pollForNew(self.url, self.div);
}, this.interval);
};
UpdatingDataDiv.prototype.cancel = function() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
})();
var d1= new UpdatingDataDiv('div1','http://some.net/address?client=Some+Client');
var d2= new UpdatingDataDiv('div2','http://some.net/otheraddress?client=Some+Client');
...
d1.cancel();
But there's not a lot you can do with d1 and d2. You can invoke cancel() to stop the updating. I guess you could add more functions to extend its capability.
OK, figured out what I needed. It's pretty straight forward.
First off disregard window.onload, the object is defined as a function and when you instantiate a new object it runs the function. Do your setup in the function.
Second, for global variables that you wish to make local to your object, simply define them as this.variable_name; within the object. Those variables are visible throughout the object, and its member functions.
Third, define your member functions as object.prototype.function = function(){};
Fourth, for my case, the object function should return this; This allows regular program flow to examine the variables of the object using dot notation.
This is the answer I was looking for. It takes my non-functional example code, and repackages it as an object...
function ServerObject(url){
// global to the object
this.server_url = url;
this.data = new Object();
this.since_record_id;
this.interval_id;
// do the onload functions
this.getRecent();
this.interval_id = setInterval(function(){
this.pollForNew();
}, 300000);
// do other stuff to setup the object
return this;
}
// define the getRecent function
ServerObject.prototype.getRecent = function(){
// do getRecent(); stuff
// reference object variables as this.variable;
}
// same for pollForNew();
ServerObject.prototype.pollForNew = function(){
// do pollForNew(); stuff here.
// reference object variables as this.variable;
}
Then in your program flow you do something like...
var server = new ServerObject("http://some.net/address");
server.variable = newValue; // access object variables
I mentioned the ADD in the first post. I'm smart enough to know how complex objects can be, and when I look for examples and explanations they expose certain layers of those complexities that cause my mind to just swim. It is difficult to drill down to the simple rules that get you started on the ground floor. What's the scope of 'this'? Sure I'll figure that out someday, but the simple truth is, you gotta reference 'this'.
Thanks
I wish I had more to offer.
Skip

Categories

Resources