clarification of Ember's this.get() method - ember.js

This is more of a general question than anything specific, but I'm new to ember and don't really understand when and how to use Ember's this.get('foo') (and similarly bar.get('foo')).
For example, in my route I have a user object on which there is a property called credits
user = this.store.find('user', userId)
console.log(user)
credits = user.get('credits')
console.log(credits)
my console.log shows me that user.content._data.credits has a value and also has a methods called get content and - more specifically - get credits. However, console.logging credits always returns undefined.
if i set the user as a model though, using this.get('user.credits') in my controller works fine.
I've read the docs about the advantages .get offers with computed properties, but could anyone concisely explain some ground rules of when to use this.get('foo') vs. bar.get('foo') and why it works in some places but not others.
Thanks!

You always need to use Em.get and Em.set for getting and setting properties of an Ember.Object. It's the basic rule. Without it you may find variety of bugs in observers/rendering and other places.
There is a misunderstanding of operations flow in your code: this.store.find always returns a promise object, not the actual data that you request. Detailed:
user = this.store.find('user', userId) // user - Em.RSVP.Promise object
console.log(user) // logs the Em.RSVP.Promise object
credits = user.get('credits') // gets property 'credits' of the Em.RSVP.Promise object (user)
console.log(credits) // always logs `undefined` because there is no property called 'credits' in Em.RSVP.Promise prototype
We must to rely on async nature of Promise and to rewrite this code like this:
this.store.find('user', userId).then(function(user) {
console.log(user) // logs the App.UserModel object with actual data
credits = user.get('credits') // gets property 'credits' of the App.UserModel instance (user)
console.log(credits) // logs real data from the model
});
There is another important part of getting properties from a model object, if you're using ember-data as data layer: you need to declare all fields of the model that you wish to get afterwards.

Related

#mswjs/data question: why does RTK-Query sandbox example need separately handcoded POST and PUT mocks?

This is a question about the default behaviour of #mswjs/data.toHandlers function using this example with #mswjs/data to create mocks for RTK-Query calls.
https://codesandbox.io/s/github/reduxjs/redux-toolkit/tree/master/examples/query/react/mutations?from-embed
the file src/mocks/db.ts creates a mock database using #mswjs/data and defines default http mock responses using ...db.post.toHandlers('rest') but fails to work if I remove the additional PUT and POST mocks.
My understanding is that #mswjs/data toHandlers() function provides PUT and POST mock API calls for a defined database (in this case Posts) by default according to the github documentation so I am seeking advice to understand better why toHandlers does not work for PUT and POST in this example. i.e. if i remove PUT and POST mock API calls they fail.
What do the manual PUT and POST API mocks do that the default toHandlers dont?
You are correct to state that .toHandlers() generates both POST /posts and PUT /posts/:id request handlers. The RTK-Query example adds those handlers explicitly for the following reasons:
To emulate flaky error behavior by returning an error response based on the Math.random() value in the handler.
To set the id primary key to nanoid().
Adding a post fails if you remove the explicit POST /posts handler because the model definition for post does not define the initial value for the id primary key. You cannot create an entity without providing a primary key to it, which the example does not:
// PostManager.tsx
// The "post" state only contains the name of the new post.
const [post, setPost] = useState<Pick<Post, "name">>(initialValue);
// Only the "post" state is passed to the code that dispatches the
// "POST /posts" request handled by MSW.
await addPost(post).unwrap();
If we omit the random error behavior, I think the example should've used nanoid as the initial value of the id property in the model description:
import { nanoid } from "#reduxjs/toolkit";
const db = factory({
post: {
- id: primaryKey(String),
+ id: primaryKey(nanoid),
name: String
}
});
This way you would be able to create new posts by supplying the name only. The value of the id primary key would be generated using the value getter—the nanoid function.
The post edit operation functions correctly even if you remove the explicit PUT /posts/:id request handler because, unlike the POST handler, the PUT one is only there to implement a flaky error behavior (the edited post id is provided in the path parameters: req.params.id).

loopback operation hook: add filter to count api

I need to intercept my loopback queries before they query my Mongodb to add additional filters, for example, to limit the object to what the user has access to.
I can successfully update the query on access operation hook to add filters to the GET /Applications , where Applications is my object. However This fails to work for GET /Applications/count
The command runs with a 200, however it returns zero results, even though I'm adding the exact same filters. There most be something different about count that I'm missing. The ctx object looks have a ton of functions/objects in it. I'm only touching the query property, but there must be something else I need to do.
Any ideas? Thank you, Dan
Could you please share your access hook observer's implementation. I tried it on a sample app, and following access hook works as expected for /api/Books/count:
module.exports = function(Book) {
Book.observe('access', function logQuery(ctx, next) {
ctx.query.where.id = 2; // changing filter value for where
console.log('Accessing %s matching %j', ctx.Model.modelName, ctx.query.where);
next();
});
};
Verify that you're modifying query property of Context (see access hook).
Hope that helps.

How to get notified on model changes in generic way with ember data

I am using Ember and Ember Data and I would like to listen to any changes relevant for a specific model.
For example in my app I could have an Order model with two fields - attr1 and attr2.
The two use cases/questions are:
How to get notified when new Order instances get added or deleted to/from store. Something like store.on(modelName, 'deleted/added', listener). modelName in this case would equal 'order'.
How to get notified on any change within all models of specific type in the store. Something like store.on(modelName, 'modelUpdated', listener). I don't want to specify in which attributes I am interested because I am interested in any change in any attribute.
Any ideas and pointers are really appreciated.
ember-data provides lifecycle callbacks for DS.Model objects. Have a look to the events panel of the doc.
You can extend the store to achieve the behavior in point 1. We will use an event name of "added:modelName".
this.store.reopen(Ember.Evented, {
createRecord(model) {
this.trigger('added' + ':' + model);
return this._super(...arguments);
}
}
Point 2 is harder.
To listen from somewhere:
listen: function() { this.store.on('added:order'), this.handleAddedOrder; }

Why are models from store already updated before model.save is called?

I was testing some behavior in ember.js.
The scenario:
I have a simple model called Team with property name.
I am using the fixures-adapter for the team.
I implemented inline-editing in a list-view. The Textbox-fields are bound to the models property "name".
The Object-Controller handles the SaveEvent. In the SaveEvent i do following:
App.TeamController = Ember.ObjectController.extend({
actions: {
save: function(){
var model = this.get('model');
// alerts changed text from textbox which is bound to the model
alert(this.get("model.name"));
//load unchanged Object from store (before calling save):
var fromStore = this.store.getById('team',2);
// Also alerts the changed content from the text box
alert(fromStore.get("name"));
}
}
});
Why is the alert of this.get("model.name") equals to alert(fromStore.get("name")) without calling model.save()? Between: When I use this.store.find('team',2) and resolve the thenable-result, the behavior is still the same.
How would the code behave, when I would use an own adapter against a web service?
The store only has one copy of a record. So when you fetch it from the store again it's returning the exact same instance to you as you already have.
save persists the record to your server, not persists to the store.
the properties aren't set in stone in the record until you've successfully persisted. You can still do record.rollback() to revert the changes that have occurred.

How can I force ember-data to refresh an object after committing it?

I'm submitting an object to our API via a POST and then transitioning to a route that displays that object. The API modifies one or more fields in the object in the POST and returns the updated info in the request response.
The data displayed is the original data from before the POST to our API. I can see from the console that ember-data is receiving back the updated information from our API. How can I force ember to "refresh" the object so that it displays the correct info?
Matt. Yehuda Katz posted a reply to a user which provides this functionality:
https://stackoverflow.com/a/14183507/506230
Basically you create a record, apply it, save it, then reload it.
saveMessage: function(text){
var acct = Social.Account.find(this.get("id")),
msg = Social.store.createRecord(
Social.Message,
{
text: text,
account: acct,
created: new Date()
}
);
acct.get("messages").addObject(msg);
Social.store.commit();
var timeoutID = window.setTimeout(function(){
__msg.reload();__
console.log('reloading');
}, 250);
}
It turns out ember was actually behaving properly and no additional work was necessary. The problem is that I was setting a variable on the controller with the same name as a computed property on my model. The value of the variable on the controller was being displayed rather than the computed property. Changing the name of the controller variable resolved the issue without any additional code.