Object Not Being Passed as Reference - javascript

I have an ObjectManager, which holds a reference to all objects that are created. The problem is that the ObjectManager is not referencing the object that was created, but instead it seems to be creating a new instance of it. Any help would be appreciated. Thanks!
var Fieldset = function (options) {
var fieldset = ($.extend(true, {
id: '',//Let's assume this has been overridden with 'MyFieldset' via the options param.
title: '',
accordion: '',
fields: [],
hidden: false,
Show: function () { $('#' + this.id).show() },
Hide: function () { $('#' + this.id).hide() }
}, options));
if (fieldset.id != null && fieldset.id != '')
ObjectManager.fieldsets[fieldset.id] = fieldset;//Save a reference to this object in the ObjectManager, so I can call ObjectManager.GetFieldset('MyFieldset'). A reference is only saved if an id is provided.
log(ObjectManager.GetFieldset(fieldset.id) == fieldset);//true
return fieldset;
}
Edit:
Sorry, I thought it was clear what I wanted this to do. There is nothing special about ObjectManger. It just has a property and Get method for each of my objects. For simplicity I only included the fieldsets property and Getter. I hope this clears up my intentions.
var ObjectManager =
{
fieldsets: {},
GetFieldset: function (id) { return this.fieldsets[id]; }
};
Edit2:
After some testing, I found something odd... Hopefully someone can explain to me why this is happening.
var myFieldset = new Fieldset({ id: 'MyFieldset' });
log(myFieldset == ObjectManager.GetFieldset('MyFieldset'));//I just found that it is true in this case...
//... However, this is not the normal way I create Fieldsets... This is:
var editForm = new Form({
dataStore: function () { return ClientsDS; },
id: 'ClientEditForm',
fieldSets: [
new Fieldset({
id: 'ClientDetailsFieldSet',
title: 'Details',
fields: [
new Field({ id: 'ClientID', name: 'ID', property: 'ID', fieldType: 'hidden', value: '0' })
]
})
]
});
log(editForm.fieldSets[0] == ObjectManager.GetFieldset('ClientDetailsFieldSet'));//false

On EDIT2:
Your objects are not equal, because they are not the same. The equality operator does not say these two objects have the same key/value pairs, they are equal when they are the same object.
For instance,
var a = b = {a: 1, b:2};
//This is b = {a: 1, b: 2}; a = b; In case you were wondering
a === b //true
var a = {a: 1, b: 2},
b = {a: 1, b: 2};
a === b //false

Hmm, your Fieldset constructor is returning an object. Perhaps try calling it as Fieldset({...}) instead of new Fieldset({...})?

I am assuming that your Form class looks something like your Fieldset class, i.e. that it $.extends (makes a deep copy) the options you give it with its internal "prototype". The object returned is the extended prototype not the options extended with the prototype object. Try changing the order of your $.extend arguments (put options second and the internal "prototype" third) and see if that changes anything. Alternatively, post your Form class :-)

Related

Extract value of property from object in a Set?

For the example, SomeItem is the model for an object (would be modeled as an interface in Typescript or you can just imagine that there is an item with the form of SomeItem if we are in untyped land.
Say I have a Set: mySet = new Set([{item: SomeItem, selected: true}, ...]).
And I want to check if itemA: SomeItem is selected or not.
What is the cleanest way to do this?
This did not work:
const isSelected = mySet.has({item: itemA, selected: true});
Nor did this:
const isSelected = Array.from(mySet).includes({item: itemA, selected: true});
I'm assuming the above two did not work because it is trying to compare the objects by reference, rather than value.
This does work:
let isSelected: boolean;
mySet.forEach(state => {
if (state.item === itemA) {
isSelected = state.selected;
}
});
But my gut tells me there is a correct way to do this.
So,
How do I extract the value of a property of an object in a Set?
Comparing two objects with the same properties returns true only if they have the same reference, I would suggest to compare their properties as the properties are primitive values..
The array some method can be used to filter if the set contains an specific object
let mySet = new Set([{item: 'SomeItem', selected: true}]);
let itemA = "SomeItem";
let isSelected = Array.from(mySet).some(element => element.item === itemA);
console.log(isSelected);
Let's take a look at it this way, Sets mainly return an iterable. Sure, they're hashed in order, as is a Map, but from the looks of your data structure, a Map would benefit you more here.
const x = new Set([
{ "foo": 1, selected: true },
{ "bar": 1, selected: false },
{ "baz": 1, selected: false },
{ "barf": 1, selected: false },
]);
Now, to get what you're looking for, you'll need as you did, convert to an array using Array.from (or [...x] spread it) and iterate, finding they key.
Now, as a Map:
const y = new Map();
y.set("foo", { selected: true });
y.set("bar", { selected: false });
y.set("baz", { selected: false });
y.set("barf", { selected: false });
With this, you simply change the structure slightly to give item1 or whatever you use the Map key, and set whatever elements you want.
y.has("foo"); // true
y.get("foo").selected; //true
So if you wanted here, it's much easier to grab the iterable key name and get which Map index has the property you want

Run code when getter/setting changed

Can I use getter/setters to do a similar task to an observer?
So for example if I assign an instance of a getter/setter to multiple objects, these will all be references, so if either object causes the getter to change the same code would run right?
I have tried the following code:
var obj = {
value: null,
get val() {
return this.value;
},
set val(x) {
console.log('set');
if (this.value !== x) {
console.log('value has been changed, do stuff!');
}
this.value = x;
}
}
var one = {
name: 'object 1',
value: obj /* Reference object */
}
var two = {
name: 'object 2',
value: obj /* Reference object */
}
var three = {
name: 'object 3',
value: obj /* Reference object */
}
Then run one.value.value = 2 which should fire the console log. However I just get 2 output in the console and no console.log.
Edit
Just saw where I was going wrong, I should be doing one.value.val = 2, this is starting to work, hang on.
Edit 2
Not too sure if this will function the way i'm expecting. I'm going to try and breakdown what i'm trying to do.
I have got an array of objects as follows:
var images = [
{ index: 0, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 1, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 2, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 3, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 4, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 5, object: HTMLElement, src: String, active: Object, alt: String },
{ index: 6, object: HTMLElement, src: String, active: Object, alt: String }
];
This object will get duplicated a couple of times throughout my script however the active state needs to remain the same throughout all instances.
How i'm duplicating the object:
var newList = [];
for (var i = 0; i < _.images.length; i++) {
// Create new instances of the images
var img = document.createElement('img');
img.src = images[i].object.src;
img.classList.add('responsive-img');
img.alt = images[i].alt;
var span = document.createElement('span');
span.appendChild(img);
if (i === current) img.parentNode.classList.add('active');
var newImage = {
object: img,
index: i,
src: images[i].src,
active: images[i].active, // Use reference to old list
alt: images[i].alt
};
newList.push(newImage);
// Add each image to the gallery DOM
_.gallery.main.appendChild(span);
}
Basically now what I need to happen, is that if the active value is changed in either reference, then code should execute and add/remove a class from the object within that instance.
Does this make sense?
So if the following is run
images[0].active.val = 1
Then newList[0].object.classList.Add('active'); and images[0].object.classList.Add('active'); executes.
There is more code that should execute however lets take it a step at a time. I was using a Polyfill for Observer before however it's too heavy weight for what i want to do and over the top, also having issues with it on Internet Explorer.
I think the best way to do this is maybe some kind of callback from the setter so I could run unique code for each instance that this object is within?
It feels a bit hackish but adding a getter to active to set the object you are referring should work :
var active = {
_value : null,
object : null,
set value(v) {
_value = v;
this.object.style.display = v;
}
}
var images = [
{img : document.querySelector('.div1'), _active : active, get active () {this._active.object = this.img; return this._active}
},
{img : document.querySelector('.div2'), _active : active, get active () {this._active.object = this.img; return this._active}
}
]
images[0].active.value = 'none';
images[1].active.value = 'block';
<div class = 'div1'>div 1</div>
<div class = 'div2' style = 'display:none'>div 2</div>

Why Object.create doesn't create a new list for me? [duplicate]

This question already has answers here:
Javascript object members that are prototyped as arrays become shared by all class instances
(3 answers)
Closed 7 years ago.
I'm trying to create a builder pattern in JavaScript. But I'm wondering why if I call Object.create twice I got the same list as before.
Here's my code.
var filterBuilder = {
filters: [],
addFilter: function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value
}
this.filters.push(filter);
return this;
},
build: function() {
return this.filters;
}
};
If I run Object.create(filterBuilder).build() I get [] which is good. But when I start adding filter
Object.create(filterBuilder).addFilter({dimension: '1', value: 'v'}).build();
I get one filter which is good
But then if I do
Object.create(filterBuilder).addFilter({dimension: '1', value: 'v'}).addFilter({dimension: '1', value: 'v'}).build();
I will get three filters, the first one is from the previous call. Isn't Object.create supposed to create a new object for me?
Prototype properties are shared, so the filters array is the same for both objects you created. If you want each object to have its own filters you have to add it as an own property (a property that is owned by the object, not the prototype), ie:
var filterBuilder = {
addFilter: function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value
}
this.filters.push(filter);
return this;
},
build: function() {
return this.filters;
}
};
var a = Object.create(filterBuilder)
a.filters = [];
a.addFilter({dimension: '1', value: 'v'}).build();
var b = Object.create(filterBuilder)
b.filters = []
b.addFilter({dimension: '1', value: 'v'}).addFilter({dimension: '1', value: 'v'}).build();
console.log(a.filters.length, b.filters.length); // 1 2
You can do this with new as well:
function FilterContainer() {
this.filters = [];
}
FilterContainer.prototype.addFilter = function() {
...
};
FilterContainer.prototype.build = function() {
...
};
// Has its own `filters` array
new FilterContainer().addFilter(...).build();
When you call Object.create(filterBuilder) several times, you get two objects which hold references to the same filters array.
var b1 = Object.create(filterBuilder);
var b2 = Object.create(filterBuilder);
// now b1.filters is the _same_ object as b2.filters,
// so calling
b1.filters.push(...);
// will inevitably modify b2.filters as well.
Your best choice here is using classical functions and prototypes
function FilterBuilder() {
this.filters = [];
}
FilterBuilder.prototype.addFilter = function() { };
var builder = new FilterBuilder();
builder.addFilter();
Object.create() takes an optional second argument which can define properties that are not inherited from the prototype, so you can (re-)define the filters property of newly created object like this:
Object.create(filterBuilder, {
filters : { writable : true, enumerable: true, value : [] }
})
https://jsfiddle.net/1m7xx4ge/2/
// full code
var filterBuilder = {
filters: [],
addFilter: function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value
}
this.filters.push(filter);
return this;
},
build: function() {
return this.filters;
}
};
Object.create(filterBuilder, {
filters : { writable : true, enumerable : true, value : [] }
})
.addFilter({dimension: '1', value: 'v'}).build();
Object.create(filterBuilder, {
filters : { writable : true, enumerable : true, value : [] }
})
.addFilter({dimension: '1', value: 'v'})
.addFilter({dimension: '1', value: 'v'}).build();

How to push an object in an Array?

Iam trying to push in array an object, but I get always error.
fCElements = [],
obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj,
myid:2,
name:'klaus'
})
how I can push into array functions like "myFunction"?
Thanks
In the Object literal, you can only give key-value pairs. Your obj doesn't have any value.
Instead, you can do like this
var fCElements = [];
fCElements.push({
obj: {
fun: myFunction,
id: 2
},
myid: 2,
name: 'klaus'
});
Now, you are creating a new object, obj, on the fly, while pushing to the array. Now, your fCElements look like this
[ { obj: { fun: [Function], id: 2 }, myid: 2, name: 'klaus' } ]
You need to give your obj property a name (or a value).
var obj = {};
obj.fun = myFunction;
obj.id = 2;
fCElements.push ({
obj:obj,
myid:2,
name:'klaus'
});
The object you are pushing to the array seems off. It will try to push this object:
{
{fun: myfunction, id: 2},
myid: 2,
name: 'klaus'
}
Which is an invalid object since the first value has no key. You should do it like this instead:
fCElements.push ({
myObj:obj,
myid:2,
name:'klaus'
});

Accessing jQuery plugin properties

I'm creating a jQuery plugin and I'm calling it like the following
$('#element').helpify({
msg : 'This is my message',
closeButton: {
text: "Close this",
colour: "red"
}
});
Then in the plugin I set up some defaults and use extend to create an object with the parameters like this:
var settings = $.extend({
title: 'Default Title',
msg : 'Default message',
closeButton: {
text: "Close",
colour: "red",
btnClass: "pull-right"
}
}, options);
I know I can then access the title by writing settings.title, what I'm unsure of is how to access the properties within closeButton.
Any help much appreciated, thanks!
EDIT
I can access the properties when I pass some in as in the first code block above, however in the second code block which is setting up some defaults and then using the passed in 'options' object should there be some properties supplied - if I don't pass any properties in and rely on the defaults, the ones within closeButton do not work, but the others do, i.e. msg
EDIT 2
Here is a JS fiddle showing what I mean - http://jsfiddle.net/U5W5G/1/
Simply use settings.closeButton.text
JavaScript properties can be accessed using one of two methods:
Dot notation
The most common and basic way of accessing properties - however illegal variable names (except for reserved words - they are allowed as property names under ES5) will not work.
foo.bar; // OK
foo.class; // only in ES5 and up
foo.&^&%^&#&(#&&#; // SyntaxError: yeah, it doesn't work
Square bracket notation
When using the square bracket notation, it can take anything - however it will be converted to a string (all object properties in JavaScript are strings):
// both are the same
foo['bar'];
foo["bar"];
// this is fine
foo['&^&%^&#&(#&&#'];
// this is equivalent to foo["[object Object]"]
foo[{}];
Pick your fancy - but unless you need to, it's most likely easier to use dot notation to access JavaScript object properties.
EDIT: about your jsFiddle, here's why it doesn't work:
var options = {
// Passing these options in
msg: 'This is my message',
closeButton: {
text: "Close this",
colour: "red"
}
},
// These are the defaults if none are passed in
settings = $.extend({
title: 'Default Title',
msg: 'Default message',
closeButton: {
text: "Close",
colour: "red",
btnClass: "pull-right"
}
}, options);
console.log(settings.closeButton.text);
console.log(settings.closeButton.colour);
console.log(settings.closeButton.btnClass);
/*
settings.closeButton.text
settings.closeButton.colour
settings.closeButton.btnClass
*/
When you're calling $.extend(), any properties in the later arguments will replace those in the earlier ones. In this case, your closeButton property in your $.extend() call is being replaced by the one in options, since the arguments was given later.
Here's an example of this in action:
var a = { foo: 'bar' };
var b = { foo: 'baz' };
var c = $.extend(a, b);
var d = $.extend(b, a);
console.log(c.foo); // baz (b was last argument)
console.log(d.foo); // bar (a was given last)
To solve this issue, either swap the arguments, or (in this case acceptable) perform a deep copy, by prepending the arguments with true:
$.extend({ a: { b: 1, c: 2 } }, { a: { b: 3 } }).a; // { b: 3 }
$.extend(true, { a: { b: 1, c: 2 } }, { a: { b: 3 } }).a; // { b: 3, c: 2 }

Categories