Sequelize getter not called when I use find - javascript

I have getters defined within the property definition of my model, they look like this:
timeReported: {
type: DataTypes.DATE,
defaultValue: Sequelize.NOW,
get() {
const input = this.getDataValue('timeReported')
const output = moment(input).valueOf()
return output
},
set(input) {
var output = moment(input).toDate()
this.setDataValue('timeReported', output)
},
validate: {
isBefore: moment().format('YYYY-MM-DD')
}
}
I have tried adding the getter to getterMethods in the options instead of within the property:
getterMethods: {
timeReported: function () {
debugger
const input = this.getDataValue('timeReported')
const output = moment(input).valueOf()
return output
}
}
I find that the setters are executed correctly when I save because I use timestamps as the request payload. I also find that validations work correctly.
Interestingly the afterFind hook is called correctly and it does show that customGetters have been registered as existing on my model.
However, when I call the find method on my model like this:
const {dataValues} = yield models.Reporting.findOne({
where: {
reportNo: reportNo
}
})
My getter methods are not invoked to transform my data for presentation. How do I invoke these methods on the data fetched from the db before I can access it.
I have tried adding the raw:false property to the queryOptions along with where. That did not work.
NOTE: I fetch my data using co, I have to retrieve the dataValues property from the model. None of the examples seem to be using the property.

When using Sequelize,one should not use dataValues property directly as I did. This will give me access to the underlying data stored in the table without invoking the getters.
To invoke all the getter functions on the entire instance:
const data = instance.get()
This will also populate any virtual fields that one has defined in the model.
To invoke the getter for an individual property:
const propValue = instance.get('timeReported')

Related

Destructuring an object from a dynamic string name

I got this config.json file :
"signs_table": {
"id_field": "dummy_id_field_name",
"prop" : "dummy_prop_name"
...
}
This file contains tons of configuration for huge amount of tables stored in a database. Every table has different field name but the configuration for my code is the same for each table (an Id field name, different property fields, but of course fields name changes from table to table).
So, in my code, I am getting a data object and I want to be able to destructs it to dynamically named properties (from the configuration) like so :
const { dummy_id_field_name, dummy_prop_name} = this.props.data
but this is hard coded way. I would like to load the named properties based on the configuration file.
something like :
const IdField = config.get("signs_table").id_field // this will retrieve the actual field name from config.json I want to be able to pass it through the destructuring operation
const PropField = config.get("signs_table").prop
const { IdField , PropField } = data
Here the config.get("signs_table") line is a from a class method that manage my config.json file...it basically retrieves the property.
So far I found this usefull approach :
ES6 — How to destructure from an object with a string key?
But this does not help me since I need to first retrieve the field name from the configuration file...
Any Idea ?
You cannot avoid fetching the field names from the config files first:
const { id_field: IdField, pro: PropField } = config.get("signs_table"); // this will retrieve the actual field names from config.json
Then, afterwards you can use those as computed property names when destructuring your actual data:
const { [IdField]: idValue , [PropField]: propValue } = this.props.data;
console.log(idValue, propValue);
You can de-structure arbitrary property names as I'll show below, but the question is why are you forcing yourself into a syntax you are unfamiliar with. You should stick with the readable and straightforward approach instead of some fancy convoluted method.
const idField = config.get("signs_table").id_field; // this will retrieve the actual field name from config.json I want to be able to pass it through the destructuring operation
const propField = config.get("signs_table").prop;
const { [idField]: idValue, [propField]: propValue } = data;
It would be more straightforward to simply avoid the de-structuring altogether and access the fields directly.
const idField = config.get("signs_table").id_field; // this will retrieve the actual field name from config.json I want to be able to pass it through the destructuring operation
const propField = config.get("signs_table").prop;
const idValue = data[idField];
const propValue = data[propField];

Angular Http subscribe - cannot assign to variable in component

I want to retrieve data from api and assign it to some value inside the angular component. In subscribe I'm trying to assign the data to loggedUser and then call function inside this subscribe to navigate to another component with this received object. Unfortunately I got the error : The requested path contains undefined segment at index 1. I want to have this object set outside the subscribe too. How can I achieve this?
logIn() {
this.portfolioAppService.logIn(this.loggingUser).subscribe((data) => {
this.loggedUser = data;
console.log(this.loggedUser);
console.log(data);
this.navigateToProfile(this.loggedUser.Id);
});
}
navigateToProfile(id: number) {
this.router.navigate(['/profile', id]);
}
console output
You are using an incorrectly named property when calling navigateToProfile.
From your console output, I can see that the data object in the subscribe looks like this:
{
id: 35,
// ..
}
But you are calling the function like this:
this.navigateToProfile(this.loggedUser.Id);
Instead, use the property id (lower case)
this.navigateToProfile(this.loggedUser.id);
To narrow this problem down in the future, try being more specific in your testing. Humans are good at seeing what they want to see and will assume the problem is more complicated than it is. If you had tried console.log(this.loggedUser.Id), you would have seen the result undefined, and worked out the problem yourself.

Backbone collection, get specific attribute

I've set an attribute currentPage on my Backbone collection, however I seem to not be able to log it specifically.
onRender: function () {
App.log('this.collection.currentPage onRender')
App.log(this.collection)
App.log(this.collection.accountID)
App.log(this.collection.currentPage)
App.log(this.collection.get('currentPage'))
}
I do the fetch then show, and within onRender, the following console log is produced
where this.collection.currentPage and this.collection.get('currentPage') are undefined even though I can see currentPage as a defined ("2") attribute in the log output of this.collection. this.collection.accountID is working how I expected currentPage to work.
What am I doing wrong?
Edit: I am setting the attribute on the success of .fetch because the data is returned in the response headers. Something like this,
getAccountSegments: function (accountID) {
var accountSegmentsCollection = new Entities.Collections.AccountSegmentCollection({
accountID: accountID
});
accountSegmentsCollection.fetch({
success: function (collection, response, options) {
collection.currentPage = options.xhr.getResponseHeader('X-Current-Page');
}
});
return accountSegmentsCollection;
},
Someone stated below that I am not allowed to set attributes on the collection (except by way of the constructor, but that's before the fetch). So how else could I get this response header data into my view?
Collections in backbone js do not allow you to set attributes.
The best way to store meta information in a backbone collection is through plain javascript object.
For example:
var myCollection = Backbone.Collection.extend({
model: myModel,
metaInfo: {
currentPage: "2"
}
});
Then you can get the current page in your view using this.collection.metaInfo.currentPage
get in this context is a little confusing. In Backbone Models have attributes, but collections do not, this means that get on a collection is different from get on a model.
get on a model retrieves an attribute, while on a collection it will return a model (by id). You could extend your collection to have attribute getters and setters:
var pageCollection = Backbone.Collection.extend({
getAttribute: function(attr) {
return this.attributes?this.attributes[attr]:null;
},
setAttribute: function(attr,val){
this.attributes = this.attributes || {};
this.attributes[attr] = val;
}
});
You could then set your currentPage attribute:
pageCollection.setAttribute('currentPage', 'myPage');
And retrieve it:
App.log(pageCollection.getAttribute('currentPage');
Which should give you the functionality you are after.

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)
The content I have bound has the following custom find method defined
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
return this.records;
},
find: function() {
var self = this;
$.getJSON('/api/foo/', function(response) {
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
});
return this.records;
}
});
My other model uses this directly
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property()
});
Now inside my template
{{#each foo in related.listings}}
{{foo.name}}<br />
{{/each}}
The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)
add: function(record) {
this.records.addObject(App.Foo.createRecord(record));
},
and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.
Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?
self.records.splice(i, 1);
Even when I query the client manually after the splice or empty work it returns 0
console.log(App.Foo.all().get('length'));
Initially I see records, but then I see they are gone (yet the html doesn't change)
I understood your question this way, that the following remark is the point your are struggling with:
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property("App.Foo.records.#each")
});
Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.
One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:
var a = []; // is the same as -> var a = Ember.A();
Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.
You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.
In your case, there is an additional problem, which is that in find(), you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.
Your model should look something like this:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
// can't use 'this.get(...)' within a class method
return Ember.get(this, 'records');
},
findAll: function() {
var records = Ember.get(this, 'records');
$.getJSON('/api/foo/', function(response) {
records.clear();
// in this case my json has a 'foos' root
response.foos.forEach(function(json) {
this.add(json);
}, this);
}, this);
// this gets updated asynchronously
return records;
},
add: function(json) {
// in order to access the store within a
// class method, I cached it at App.store
var store = App.get('store');
store.load(App.Foo, json);
var records = Ember.get(this, 'records');
records.addObject(App.Foo.find(json.id));
}
});
Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.
Here's a working jsfiddle.

Backbone.js fetch not actually setting attributes

I have a basic backbone model, its urlRoot attribute is set and the corresponding target on the server side returns a correct JSON output (both JSON string and application/json header).
I call a fetch like this:
var athlete = new Athlete({ id: 1 });
athlete.fetch();
at this point if I add a
console.log(athlete);
I can see the model, and inspecting it in firebug I can open the attributes object and see all the values returned from the server.
BUT if I do a:
console.log(athlete.get('name'));
I get undefined (the name appears under the attributes in the DOM inspection I mentioned above)
also doing a:
console.log(athlete.attributes);
returns an object containing only {id: 1} which is the argument I passed while creating the model.
If I create the model like this:
var athlete = new Athlete(<JSON string copypasted from the server response>);
then everything works fine, the .get() method returns whatever I ask, and athlete.attributes shows all the values.
What am I doing wrong?
fetch is asynchronous, which means that the data won't be available if you immediatly call console.log(athlete.get('name')) after the fetch.
Use events to be notified when the data is available, for example
var athlete = new Athlete({id: 1});
athlete.on("change", function (model) {
console.log(model.get('name'));
});
athlete.fetch();
or add a callback to your fetch
var athlete = new Athlete({ id: 1 });
athlete.fetch({
success: function (model) {
console.log(model.get('name'));
}
});
or take advantage of the promise returned by fetch:
athlete.fetch().then(function () {
console.log(athlete.get('name'));
});
Just as a quick remark when using events in this example. It did not work with change in my case because this events fire on every change. So sync does
the trick.
var athlete = new Athlete({id: 1});
athlete.on("sync", function (model) {
console.log(model.get('name'));
});
athlete.fetch();

Categories