I'm working with sails.js waterline orm. Now this is not particularly a sails question, but i have to place some context, so when you create a record you get back an object with the data created. If the record has other records (collections) associated, it has keys related to those in the returned object, but those keys are getters/setters, even if no data is present for those related objects.
I've simplified a few things just to expose the main point.
This is my user model:
var User = {
attributes:
status: {type:'string'}
images: {
collection: 'Image'
}
}
Lets assumme, i performed a create query on a User model, that has a images collection associated. The userRecord is what the query returned.
if i console.log this out it shows the properties related to the model itself
but not the associated records, even though the key is actually there, you can
access it but is not visible to console.log or utils.inspec even when setting show hidden to true.
console.log(userRecord)
This is what gets returned
{ name: 'zaggen'}
This is what should get returned
{ name: 'zaggen',
images: [{
path: 'some-path/img.png'
}]
}
And i can access the hidden property like this:
console.log(userRecord.images[0].path)
// outputs some-path/img.png
How is this even possible?, as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the __proto__ object, but in this case they are not.
After searching for this i haven't found anything and its pretty weird so i thought it could be a good question for SO. It'll help on my work process if i could console.log this info and get all the data, right now i can use lodash and call clone or defaults and i get the object as it should.
as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the proto object
That's no longer true in ES5. It was true in ES3.
Notice that even in the original javascript, objects and functions have hidden properties like .__proto__ or .constructor or .prototype? It was like some native javascript objects have these magic features (like how setting innerHTML can call the HTML compiler). ES5 exposes all that magic by way of Object.defineproperty.
The specific feature that hides a property from console.log() is enumerable. Setting it to false on a property makes it hidden from for..in (no need for .hasOwnProperty() anymore):
var foo = {a:1}
Object.defineProperty(foo,'b',{
enumerable: false, // hide it from for..in
value: 2
})
console.log(foo); // prints out {a:1}
console.log(foo.b); // prints out 2
There are other useful features such as getters and setters (allowing you to emulate properties like .innerHTML that calls a function when you write to it) and writable (allowing you to make a property read-only). See the full documentation for details: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Sails uses waterline which is where the model is defined. If you take a look at the source code for it, you see this:
https://github.com/balderdashy/waterline/blob/77fe3a9b9a9b12110a6ba079a84e5cd43a4369db/lib/waterline/model/lib/model.js#L57-L75
/**
* Log output
* #return {String} output when this model is util.inspect()ed
* (usually with console.log())
*/
Object.defineProperty(this, 'inspect', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
var output;
try {
output = self.toObject();
} catch (e) {}
return output ? util.inspect(output) : self;
}
});
So they override the console.log output to self.toObject(). This is one of their internal methods that does all kinds of stuff that could be responsible for the output your seeing. For example:
// Don't run toJSON on records that were not populated
if (!self.proto._properties || !self.proto._properties.joins) return;
Or:
if (!this.proto._properties.showJoins) return;
I noticed in their integration tests, they pass { showJoins: true } as the second argument when creating the model. I couldn't find anything about it in the docs, but maybe you could try that?
https://github.com/balderdashy/waterline/blob/48dc007b69a133169651aeb422fa3a61c3c6802c/test/integration/model/save.js#L150
Related
While it's possible to prevent deletion of Object properties by using freeze/seal/preventExtensions, I'd love to know whether it's possible to prevent deleting the object itself.
For example, in case I create an object in the global scope, and want to avoid nested functions or other modules from being able to delete that object, can I treat this global-scoped object simply as a property of the globalThis variable? Would it be possible to prevent deleting it in all hosts - Node, browsers, Deno?
My final goal is that, basically, I want to prevent overriding the object, rename it's properties/fields/symbols, or modifying it's values, or manipulating it's prototype chain as well.
So, please tell me how to keep this secret unchanged, undeleted (and maybe un-readable) along with all of it's fields and sub-fields:
const secretTree = {
name: 'barack obama',
password: 'ilovetrump',
subTrusts: [
{ name: 'biden', password: 'zZzZ' }
]
};
(function putin() {
delete secretTree;
globalThis.secretTree = { name: 'vlad', password: '123456' };
})();
// and also this:
// delete secretTree; const secretTree = ...
// and all other variations of deletion
Please, treat the case of null-prototyped object as well (in which __proto__: null).
Maybe, the correct way to approach this question and to describe it, is that we need to protect the "variable" itself from changing, as opposed to protect the "object" value inside it. To say, I want to "reserve" the variable name from any modifications, so forever I know that "globalThis.secretTree" will refer to something I trust on.
I tried to avoid some untrusted library functions from touching important objects.
Option 1: Object.defineProperty() with Object.freeze()
You can use Object.defineProperty() (or Object.defineProperties() or Reflect.defineProperty()):
By default, properties added using Object.defineProperty() are not writable, not enumerable, and not configurable.
You can also use Object.freeze() to freeze nested objects to make immutable structures.
Example:
Object.defineProperty(globalThis, "secretTree", {
value: Object.freeze({
name: "barack obama",
password: "ilovetrump",
subTrusts: Object.freeze([
Object.freeze({ name: "biden", password: "zZzZ" }),
]),
}),
});
Attempts to modify the data will fail silently or throw errors.
Option 2: Modules
Variables defined inside an ES module are scoped to that module. Code outside the module cannot get nor delete its data unless the module exports a reference to it.
example.mjs
export {}; // a module must have at least one `export` or `import`
const secretTree = { /* your data */ };
Other code attempting to access, modify, or delete it will fail to do so:
import * as data from "./example.mjs";
delete data.secretTree; // doesn't reference the actual `secretTree`
I'm using OLN for some basic namespacing in a few javascript files. The "logical hierarchy" of those files is something like this:
-main.js
-somefile.js
-otherfile.js
-....
main.js manages stuff that all pages need, but then depending on the specific page loaded somefile.js or otherfile.js might be loaded (or neither).
In main.js, this is the syntax:
main.js
var mainjs = {
someobjects: [baz],
// more stuff
}
Depending on what page is loaded, other javascript files are loaded. They might add objects to that list, like so:
somefile.js
let foo;
let bar;
$(function() {
console.log( " somefile.js ready!" );
mainjs.someobjects = mainjs.someobjects.concat([foo, bar])
}
If I look in the console after page load, I can see that within mainjs.someobjects, I have those 3 objects. "baz" is defined and the 2 others I added are "undefined". That's fine.
However, when I later initialized those 2 objects from somefile.js (user clicks something), then in the console I can see that foo & bar are both defined. However, in mainjs.someobjects, the 2 objects are still undefined.
I thought those would be passed as reference, but apparently I was wrong. Basically the issue I'm trying to address is that I have a few different somefile.js which may create different objects (dataTables actually) but they fetch the data (ajax calls) only when the tab that contains them is shown. Then I need to adjust the columns widths based on the data. A known issue with datatables, when the html is created before the datatable is populated. A call to table.columns.adjust() is needed for that to happen.
So:
Why are my variables in mainjs still undefined and who I can change my structure so that when those variables are later assigned to objects, they actually reference the object?
Alternatively another way to ensure my datatables are ajusted. However I'd rather NOT have to build a completely different structure to make that happen. I guess I could delegate the column adjusting to the lower-level files where they are defined...
I thought those would be passed as reference, but apparently I was wrong.
Yes, JavaScript is a purely pass-by-value language, other than something JavaScript native modules ("ESM") do.
It sounds to me like you want to use ESM. In ESM, when you export a module-level binding (loosely, "variable") and another module imports it, the other module has a read only live binding that links it to the original exported one. That means that when the module that imported it uses it, it sees the then-current value of the source binding (not the value it had when it was exported).
So if you were using ESM and did:
export let foo;
export let bar;
// ...code here that asynchronously or periodically updates `foo` and/or `bar`...
Then a module importing them:
import {foo, bar} from "./some-module.js";
...will see the current values of foo and bar via those live bindings.
If you don't want to use ESM for some reason, you can export an object with properties for foo and bar, and have the other code using that exported object. Since the object is stable (we don't replace it with a different object), code using it can reliably access its foo and bar properties and see their current values.
Basically, in somefile.js:
const meaningfulName = {
foo: undefined, // Or some more meaningful initial value
bar: undefined, // Or some more meaningful initial value
};
$(function() {
console.log( " somefile.js ready!" );
// Just FWIW, I probably woudn't use an array for this, I'd use an object
mainjs.someobjects = mainjs.someobjects.concat(meaningfulName);
}
Then code using that object would use its foo and bar properties, and always see the current value of them.
I have a form field that has the following generic autocomplete:
<generic-autocomplete
v-model='c.heuristicId'
v-on:change='heuristicSelected(i, $event)'
label='Fuente de datos'
:apiFunction='heuristicsFetchFunction'
:disabled='disabled'/>
When saving the form and sending it with this field in blank heuristicIdgets sent as nullwhich is what is desired.
However, when selecting a value from the generic autocomplete and then deleting it, heuristicIdgets sent with the id of the previously selected value.
Inside the function that calls axios I'm logging console.log("heuristicId to be sent: " + campaign.heuristicId);and undefinedgets logged in both scenarios.
EDIT: After inquiring a little bit more I believe that the error might be in one of <generic-autocomplete/>'s computed properties:
isValid: function () {
// this checks that the name in display equals the object selected in
// memory so no invalid labels are displayed while a valid object is 'selected'
let vc = this
let result = false
if (vc.object) {
result = vc.objectName === vc.object.name
console.log('generic-autocomplete#isValid', result)
}
return result
}
An empty vc.objectwill return a falseresult and I haven't been able to satisfactorily handle that.
After logging a really complex control flow I finally got to the point where I knew which properties I had to mutate. However, it turns out that in Vue 2 mutating inline properties is an anti-pattern. The capability of mutating inline props was deprecated from the previous Vue version in order to be consistent with the library's fully reactive paradigm. So what happens is that if you try to mutate an inline property, when the component re-renders, such property will be set back to its default (or previous) value.
This is solved by adding a computed property that's a copy from the property that you want to mutate.
//Somewhere else in the code the property is defined
property: null
//Add the computed property
data: function () {
return {
mutableProperty: this.property,
}
}
I have a post, saved to $scope.mydata in the controller. I give this data via a parameter to a service that returns a function.
The service:
module.factory('myservice', function () {
return function servicefunction(mydata) {
var test = _.keys(mydata).length;
console.log("mydata", mydata);
console.log("test", test);
Firebug in Firefox shows me in the dom that mydata contains:
Object { $$state={...}, angularCollection=false, $object={...}, mehr...}
and the test returns 5.
When I type
module.factory('myservice', function () {
return function servicefunction(mydata) {
var test = _.keys(mydata.$$state).length;
console.log("mydata", mydata.$$state);
console.log("test", test);
Firebug gives me the Object { status=0} and in the DOM there is status and value []. I need to get access to that value. The test returns 1 for the status.
Why test via _.keys finding the status but not the value?
It looks like you are trying to access a property (value) that is inherited from your mydata.$$state's prototype. In that case, using _.keys will never list value.
It may very well be that it is inherited for now because it is empty, and it will become an own property once it is set.
Why not simply accessing it like so: mydata.$$state.value? It should work for both cases (inherited and owned).
I may lack a major part of your context, so if it still does not work, it would be worth providing more information about your data.
So, I have the following code that access my rest api:
Employees.Employee = Backbone.Model.extend({
urlRoot: 'api/employee'
})
var daryl = new Employees.Employee({id:17})
daryl.fetch()
console.log(daryl.attributes)
Now, when I console.log the attributes, the daryl object is set up like this roughly:
daryl = {
attributes:
[0]: {
id: 17,
first: 'Daryl',
last: 'xxxx',
email: 'xxx'
},
id: 17,
watchers...
protos...
}
So trying to daryl.get('first') results in undefined. Everything else is stored in the object in the array at index 0. Why is this? I'm a newbie here but this is definitely not how most of the tutorials seem to show how backbone works.
So if I do daryl.get('first'), I get undefined. daryl.get('id') works as expected. daryl.get('0') actually returns a plain old javascript object of the actual model, i.e. what I would probably expect to be my backbone model to ACTUALLY be. why is this?
Your server appears to be returning an array in its response, hence why calling model.get('0') is returning the attributes you really wanted. You either need to modify the server's response to only return the object (instead of an object inside an array) or you need to add a parse method to your model to return the first item in the response array.
Not sure if this is the issue in question (but doing console.log after calling fetch is problematic), but it is important to keep in mind that daryl.fetch()is happening asynchronously.
That is to say, you should try:
daryl.fetch().done(function(){
console.log(daryl.attributes);
model.get("first");
});
or
daryl.fetch({success : function(model){
console.log(model);
model.get("first");
}});
This ensures that the AJAX request was complete prior to trying to act on the model and very well maybe why get returns undefined.