Create One Array of Objects - javascript

I have the following code which is triggered using JQuery.UI.Sortable and it is behaving unexpectedly.
I need to create one array of objects in this function and return it every time it is called.
function saveOrder(el) {
var slideOrder = new Array();
$(el).each(function() {
var slideId = $(this).attr("data-llp-slide-id"),
slideNo = $(this).attr("data-llp-slide-number"),
slideOb = {slideId,slideNo};
slideOrder.push(slideOb);
});
return slideOrder;
}
What I am getting is multiple objects/arrays returned
Code that is triggering the function above as requested by #Rhumborl
function init() {
$('.slide-list').sortable({
connectWith: ".connected-sortable",
stack: '.connected-sortable ul',
stop: function(e){
$(".slide-item").each(function(i, el){
var hash = $(el).index()+1;
$(el).attr('data-llp-slide-number',hash);
$(el).find('[data-llp-slide-number-display]').text(hash);
var newOrder = saveOrder(el);
console.debug(newOrder);
});
}
}).disableSelection();
}

In the context you mentioned it is invalid object in the javascript. And you couldn't push like this
slideOb = {slideId,slideNo};
slideOrder.push(slideOb);
First define slideOb is an valid object then push like below
slideOb = {'id': slideId, 'no':slideNo};
slideOrder.push(slideOb);
Now slideOrder is an local array it is always hold one array object. make it as global to push more arrays into it.

Related

Javascript: call object method using string only

This code doesn't work.
var Modal = {
init: function() {
console.log("test");
}
}
var objMethod = "Modal.init";
window[objMethod]();
I saw some answers that it can be called using this but I want to know how it can be called without using the object.
Modal["init"]();
Thank you!
To call a namespaced function, you need to use a multidimensional array. In this case it would be window['Modal']['init'](), which can also be expressed by splitting the objMethod string and using array indices:
var arr = objMethod.split(".");
window[arr[0]][arr[1]]();
var Modal = {
init: function() {
console.log("test");
}
}
var objMethod = "Modal.init";
var arr = objMethod.split(".");
window[arr[0]][arr[1]]();

Cannot reduce array inside of an object

I am trying to reduce an array inside of an object. I am getting back
push is not a function
I have started my array as empty and created a add function to pass in as the first argument.
function add(a,b) {
return a +b;
}
var navBarArray = [];
var listArray = [];
var mapping = {
".navbar": navBarArray,
".list-group": listArray
};
I tried this approach on the mapping object but it creates errors
var mapping = {
".navbar": Math.round(navBarArray.reduce(add,0) ),
".list-group": listArray
};
However, I get push is not a function back in my console.
Below is my function that passes values to the array. I can create a variable inside the function and reduce it there. However, that limits access to my variable and will bloat my function as I continue.
Object.keys(mapping).forEach(function(selector) {
$(selector).hover(function(evt) {
console.log('mapping',mapping);
console.log('selector',selector);
enteredTime = new Date();
}, function() {
var ctime = new Date();
var time = (ctime.getTime() - enteredTime.getTime())/1000;
mapping[selector].push(time);
// *********** this works but not where I need it to*******
var reduce = Math.round(navBarArray.reduce(add,0) );
console.log(reduce);
});
})
Change your mapping object so it has separate places for the array and total:
var mapping = {
".navbar": {
total: 0,
times: []
},
".list-group": {
total: 0,
times: []
}
}
Then you do mapping[selector].times.push(time), and put the total with:
mapping[selector].total = mapping[selector].times.reduce(add, 0);

Can't set scoped variable in $.post callback

I have the following type of structure:
(function(){
var objects = [];
$('button.one').on('click', function(){
fetchObjects = function(objects) {
$.post("/fetchObjects")
.done(function(data){
objects = data;
console.log(objects.length);
});
}
fetchObjects(objects)
});
$('button.two').on('click', function(){
console.log(objects.length);
});
})();
You can see I have a variable objects that is local to this function. Initially its empty. When I click button.one I wish to populate objects with the returned value from some ajax request. It appears to work, however when clicking button.two, the objects variable is still an empty array.
Why isn't objects available in the jQuery callback?
I've also tried this approach but with the same results:
function callback(data) {
facilities = data
}
$.post("/fetchObjects")
.done(function(data){
callback(data);
});
What am I missing here? Please don't tell me to make "objects" global.
I don't know why you're passing objects as parameter. The following should work fine I think. Please let me know if you're trying to achieve something else.
(function(){
var objects = [];
$('button.one').on('click', function(){
fetchObjects = function() {
$.post("/fetchObjects")
.done(function(data){
objects = data;
console.log(objects.length);
});
}
fetchObjects()
});
$('button.two').on('click', function(){
console.log(objects.length);
});
})();
Let's simplify the code a bit. You have essentially this:
var objects = [];
fetchObjects = function(innerObjects) {
var data = ['a','b'];
innerObjects = data;
console.log(innerObjects.length);
};
fetchObjects(objects);
console.log(objects);
(I've changed the other objects variable's name just for clarity; the issue is the same even if it had the same name.)
When you call the function, innerObjects contains a reference to objects so modifying it would change the original array as well. But when you do
innerObjects = data;
now instead of modifying the array you're replacing the reference with something else. innerObjects "points" to data instead of objects and the original variable remains unchanged.
To make it work you'd need to loop through the data array (assuming it'll always be an array) and assign the contents to the objects reference one by one. This way you'll keep the original reference and modify the original array.
var objects = [];
fetchObjects = function(innerObjects) {
var data = ['a','b'];
for( var i = 0; i < data.length; i++ ) {
innerObjects[i] = data[i];
}
console.log(innerObjects.length);
};
fetchObjects(objects);
console.log(objects);
Or, in your actual code:
(function(){
var objects = [];
$('button.one').on('click', function(){
fetchObjects = function(objects) {
$.post("/fetchObjects")
.done(function(data){
for( var i = 0; i < data.length; i++ ) {
objects[i] = data[i];
}
console.log(objects.length);
});
}
fetchObjects(objects)
});
$('button.two').on('click', function(){
console.log(objects.length);
});
})();

How can I reuse an object literal

I'm trying to reuse an object I created to dynamically create more than one slider on a page.
My idea was to create an array and push my slider object there as often as needed, so I could access it by id. Unfortunatelly it doesn't work. Hope someone can point me in the right direction ...
So what I have is this;
var slider = {
"init":function(slide_it){
this.parent = $(slide_it);
/Count Elements and create a navigation depending on the count etc./
},
"otherstuff":{...}
}
In my (document).ready function I create an array and fill it up with different slider objects, add Ids to an accordion and call the init function:
var slide_array = [];
var accordion_sections = $('#accordion > div').length;
for(var i = 0; i < accordion_sections; i++){
slide_array.push(slider);
$('#accordion').children('div').eq(i).attr('id', 'slide_it_'+ i);
slide_array[i].init($('#slide_it_' + i).find('.slider'));
}
Then I have a button with class="next" and I call a function within the slider
$('.next').click(function(){
slide_array[0].otherstuff();
});
My plan is to get the parent of .next and its id so that I can use slide_array[parentID].otherstuff();
But ... it's not working propperly when I call the init function inside the for loop more then once.
More weird, some functions calls seem to work, other have no effect.
What am I doing wrong?
You can use Object.create.
var s1 = Object.create(slider),
s2 = Object.create(slider);
s1.init(...);
s2.init(...);
If you return this from init your will be able to chain like:
var s1 = Object.create(slider).init(...);
However at this point I would just ditch the object literal and use constructors, since this is what you need.
function Slider(slide_it) {
this.parent = $(slide_it);
}
Slider.prototype = {
constructor: Slider,
otherStuff: function () {}
};
var s1 = new Slider(...),
s2 = new Slider(...);
Write a function to return the object:
function slider() {
return {
"init":function(slide_it){
this.parent = $(slide_it);
/Count Elements and create a navigation depending on the count etc./
},
"otherstuff":{...}
};
}
Then:
slide_array.push( slider() );
That'll give you a separate object every time. In you're version, you're filling the array with references to the same single object.
Why not just turn that into a jQuery plugin ?
jQuery.fn.slider = function(options) {
return this.each(function() {
var sliderElem = $(this),
settings = $.extend({
speed : 3000,
something : 'other thing'
}, options);
otherStuff(sliderElem);
});
function otherStuff(elem) {
}
}
$('#accordion > div').slider();
No iteration or jumping through hoops, just call it on the collection and it creates a new slider for each element ?

Attach Event Listener to Array for Push() Event

Is there a way to know when a user has pushed (via push()) an item onto an array?
Basically I have an asynchronous script that allows the user to push commands onto an array. Once my script loads, it execute the commands. The problems is, the user may push additional commands onto the array after my script has already run and I need to be notified when this happens. Keep in mind this is just a regular array that the user creates themselves. Google Analytics does something similar to this.
I also found this which is where I think Google does it, but I don't quite understand the code:
Aa = function (k) {
return Object.prototype[ha].call(Object(k)) == "[object Array]"
I also found a great example which seems to cover the bases, but I can't get my added push method to work correctly:
http://jsbin.com/ixovi4/4/edit
You could use an 'eventify' function that overrides push in the passed array.
var eventify = function(arr, callback) {
arr.push = function(e) {
Array.prototype.push.call(arr, e);
callback(arr);
};
};
In the following example, 3 alerts should be raised as that is what the event handler (callback) does after eventify has been called.
var testArr = [1, 2];
testArr.push(3);
eventify(testArr, function(updatedArr) {
alert(updatedArr.length);
});
testArr.push(4);
testArr.push(5);
testArr.push(6);
The only sensible way to do this is to write a class that wraps around an array:
function EventedArray(handler) {
this.stack = [];
this.mutationHandler = handler || function() {};
this.setHandler = function(f) {
this.mutationHandler = f;
};
this.callHandler = function() {
if(typeof this.mutationHandler === 'function') {
this.mutationHandler();
}
};
this.push = function(obj) {
this.stack.push(obj);
this.callHandler();
};
this.pop = function() {
this.callHandler();
return this.stack.pop();
};
this.getArray = function() {
return this.stack;
}
}
var handler = function() {
console.log('something changed');
};
var arr = new EventedArray(handler);
//or
var arr = new EventedArray();
arr.setHandler(handler);
arr.push('something interesting'); //logs 'something changed'
try this:
var MyArray = function() { };
MyArray.prototype = Array.prototype;
MyArray.prototype.push = function() {
console.log('push now!');
for(var i = 0; i < arguments.length; i++ ) {
Array.prototype.push.call(this, arguments[i]);
}
};
var arr = new MyArray();
arr.push(2,3,'test',1);
you can add functions at after pushing or before pushing
Why not just do something like this?
Array.prototype.eventPush = function(item, callback) {
this.push(item);
callback(this);
}
Then define a handler.
handler = function(array) {
console.log(array.length)
}
Then use the eventPush in the place that you want a specific event to happen passing in the handler like so:
a = []
a.eventPush(1, handler);
a.eventPush(2, handler);
I'd wrap the original array around a simple observer interface like so.
function EventedList(list){
this.listbase = list;
this.events = [];
}
EventedList.prototype.on = function(name, callback){
this.events.push({
name:name,
callback:callback
});
}
//push to listbase and emit added event
EventedList.prototype.push = function(item){
this.listbase.push(item);
this._emit("added", item)
}
EventedList.prototype._emit = function(evtName, data){
this.events.forEach(function(event){
if(evtName === event.name){
event.callback.call(null, data, this.listbase);
}
}.bind(this));
}
Then i'd instantiate it with a base array
//returns an object interface that lets you observe the array
var evtList = new EventedList([]);
//attach a listener to the added event
evtList.on('added', function(item, list){
console.log("added event called: item = "+ item +", baseArray = "+ list);
})
evtList.push(1) //added event called: item = 1, baseArray = 1
evtList.push(2) //added event called: item = 2, baseArray = 1,2
evtList.push(3) //added event called: item = 3, baseArray = 1,2,3
you can also extend the observer to observe other things like prePush or postPush or whatever events you'd like to emit as you interact with the internal base array.
This will add a function called onPush to all arrays, by default it shouldn't do anything so it doesn't interfere with normal functioning arrays.
just override onPush on an individual array.
Array.prototype.oldPush = Array.prototype.push;
Array.prototype.push = function(obj){
this.onPush(obj);
this.oldPush(obj);
};
//Override this method, be default this shouldnt do anything. As an example it will.
Array.prototype.onPush = function(obj){
alert(obj + 'got pushed');
};
//Example
var someArray = [];
//Overriding
someArray.onPush = function(obj){
alert('somearray now has a ' + obj + ' in it');
};
//Testing
someArray.push('swag');
This alerts 'somearray now has a swag in it'
If you want to do it on a single array :
var a = [];
a.push = function(item) {
Array.prototype.push.call(this, item);
this.onPush(item);
};
a.onPush = function(obj) {
// Do your stuff here (ex: alert(this.length);)
};
Sometimes you need to queue things up before a callback is available. This solves that issue. Push any item(s) to an array. Once you want to start consuming these items, pass the array and a callback to QueuedCallback(). QueuedCallback will overload array.push as your callback and then cycle through any queued up items. Continue to push items to that array and they will be forwarded directly to your callback. The array will remain empty.
Compatible with all browsers and IE 5.5+.
var QueuedCallback = function(arr, callback) {
arr.push = callback;
while (arr.length) callback(arr.shift());
};
Sample usage here.
Untested, but I am assuming something like this could work:
Array.prototype.push = function(e) {
this.push(e);
callbackFunction(e);
}
A lot better way is to use the fact that those methods modify array length.
The way to take advantage of that is quite simple (CoffeeScript):
class app.ArrayModelWrapper extends Array
constructor: (arr,chName,path)->
vl = arr.length
#.push.apply(#,arr)
Object.defineProperty(#,"length",{
get: ->vl
set: (newValue)=>
console.log("Hello there ;)")
vl = newValue
vl
enumerable: false
})
for debugging purpose you can try. And track the calling function from the call stack.
yourArray.push = function(){debugger;}
We can prototype Array to add a MyPush function that does push the rec to the array and then dispatches the event.
Array.prototype.MyPush = (rec) =>
{
var onArrayPush = new Event("onArrayPush",{bubbles:true,cancelable:true});
this.push(rec);
window.dispatchEvent(onArrayPush);
};
and then we need an eventhandler to capture the event, here I am capturing the event to log the event and then indexing the record for example:
addEventListener("onArrayPush",(e)=> {
this.#Log(e);
this.#IndexRecords();
});
But in 2022 you may also go with callback as:
Array.prototype.MyPush = function(rec,cb){
this.push(rec);
cb(rec);
};
here cb is the callback that is invoked after rec is pushed to the Array. This works at least in the console.

Categories