How can I determine the "store name" (not sure what the proper terminology is) for a given ED Model? Say I have App.Payment, is there a store method that let's me look up its corresponding name, i.e. payment (for example to use in find queries)?
For Ember Data 1.0 (and later)
modelName is a dasherized string. It stored as a class property, so if you have an instance of a model:
var model = SuperUser.create();
console.log(model.constructor.modelName); // 'super-user'
For Ember Data Pre 1.0
typeKey is the string name of the model. It gets stored as a class property of the model, so if you have an instance of a model:
var model = App.Name.create({});
console.log(model.constructor.typeKey); // 'name'
You might be looking for Ember's string dasherize method:
var fullClassName = "App.SomeKindOfPayment";
var className = fullClassName.replace(/.*\./, ""); // => "SomeKindOfPayment"
var dasherizedName = Ember.String.dasherize(className); // "some-kind-of-payment"
There might be a built-in way to do this in Ember, but I haven't found it after spending some time looking.
EDIT: Ember Data might also let you get away with passing "App.SomeKindOfPayment" when a model name is needed - it usually checks the format of the model name and updates it to the required format by itself.
store.find, store.createRecord, and other persistence methods, use the store.modelFor('myModel'). After some setup it call container.lookupFactory('model:' + key); where key is the 'myModel'. So any valid factory lookup syntax is applicable. For example:
Given a model called OrderItems you can use: order.items, order_items, order-items, orderItems.
It turns out there was no need to do this after all, and here's why:
I was trying to the the string representation of the model ("payment" for App.Payment) in order to call store.findAll("payment"). However, looking at the ED source for store, the findQuery function calls modelFor to look up the factory (App.Payment) from the string (payment), unless a factory is already provided. And the factory is easily accessible from the controller by calling this.get('model').type. There's no need to convert it to a string (and back).
Here's the relevant code from the Ember Data source.
modelFor: function(key) {
var factory;
if (typeof key === 'string') {
factory = this.container.lookupFactory('model:' + key);
Ember.assert("No model was found for '" + key + "'", factory);
factory.typeKey = key;
} else {
// A factory already supplied.
factory = key;
}
factory.store = this;
return factory;
},
Related
I'm trying to prevent re-fetch of previously cached data. But the documentation provides a couple of ways of achieving this through cacheRedirects and dataIdFromObject. I'm trying to understand when one technique is used over the other.
He's an example flow using dataIdFromObject -- would this provide enough context for Apollo to fetch the detail view data from cache, or do I additionally need a cacheRedirect to link the uuid query?
List view query:
query ListView {
books {
uuid
title
abstract
}
}
Detail view query:
query DetailView {
book(uuid: $uuid) {
uuid
title
abstract
}
}
cache constructor args with dataIdFromObject:
new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
case 'book': return `book:${object.uuid}`;
default: return defaultDataIdFromObject(object); // default handling
}
}
});
I believe you are incorrect when you say
But the documentation provides a couple of ways of achieving this
through cacheRedirects and dataIdFromObject.
I believe only cacheRedirects achieve what you want.
dataIdFromObject allows you to customize how ApolloClient should uniquely identify your objects. By default, ApolloClient assumes your objects have either a id or _id property, and it combines the object __typename with the id property to create a unique identifier.
By providing a dataIdFromObject function, you can customize this unique identifier. For example, if all of you objects have an id which is a uuid, then you could supply a dataIdFromObject function which simply instructs ApolloClient to use the object's id property, without appending __typename.
Using Ember-Data 0.13-59 & Ember 1.0.0 RC 6 (from starter kit)
Problem: upon save() to a new record made from App.Userstat.createRecord({ ... }) the server gets the POST and successfully returns an id but the new record is not available in the Userstat model.
To better understand example: this is a quiz app(for multiple choice questions). Each question has several choices and when a user selects a choice, their choice to the corresponding question is stored in a Model, App.Userstat.
At each question, the app needs to know whether the user has already answered this question or if it's new.
I use a computed property as a setter and getter. The setter is called when a user selects a choice (the choice's value is passed to computed property). First it checks if a record exists for the user's current question. If it doesn't exist it will create a new record. If it does exist, it should only issue a PUT request with updated content.
Code Updated(July 8, 11AM)
App.UserstatsController = Ember.ArrayController.extend();
App.QuestionController = Ember.ObjectController.extend({
needs: "userstats",
chosen = function(key, value) {
// getter
if(value === undefined) {
// something goes here
// setter
} else {
// the question.id is used to compare for an existing record in Userstat mdoel
var questionId = this.get('id');
var questionModel = this.get('model');
// does any Userstat record belong to the current question??
var stats = this.get('controllers.Userstats');
var stat = stats.get('model').findProperty('question.id', questionId);
// if no record exists for stat, means user has not answered this question yet...
if(!stat) {
newStat = App.Userstat.createRecord({
"question" : questionModel,
"choice" : value // value passed to the computed property
)}
newStat.save(); // I've tried this
// newStat.get('store').commit(); // and this
return value;
// if there is a record(stat) then we will just update the user's choice
} else {
stat.set('choice', value);
stat.get('store').commit();
return value;
}
}.property('controllers.Userstats')
No matter how many times I set chosen it always sends a POST (as opposed to an update only sending a PUT request) because it never adds the record to the model the first time.
To demonstrate further, in the setter part of the computed property, when I put this code:
var stats = this.get('controllers.Userstats')
console.log stats
the Userstats controller shows all previously existing records, but not newly submitted records!
How come the new record isn't available after I save() or commit() it???
Thanks :)
EDIT
maybe it has something to do with me adding a record to the singular model App.Userstat and then when I look for it, I'm searching using the UserstatsController which is an Array controller???
I don't know if it's a typo, but the computed property is defined the wrong way and should be like this:
App.QuestionController = Ember.ObjectController.extend({
needs: 'userstats',
choice: 'controllers.userstats.choice',
chosen: function(key, value) {
...
}.property('choice')
...
});
Inside the property() you should also define properties that trigger the computed property if they change. This way if choice changes the chosen cp will be triggered.
Please let me know if it helps.
How can I bind an Ember Map value to a TextField value. Let's say I have this configuration:
App.AppsController = Em.Controller.extend({
selections: null
});
App.AppsRoute = Ember.Route.extend({
setupControllers: function(controller, model) {
controller.set('selections', Ember.Map.create());
}
});
And in my template:
{{view Ember.TextField valueBinding="bindingToMap"}}
I have tried to valueBinding="controller.selections.somekey" where somekey is a key from my map. However, the value is never bound. Note that initially the map is empty. Can this be the root of the problem?
EDIT:
I have also tried to use the binding with an integer value in the controller and it works. So the problem comes when I bind a more complex data structure such as a Map. I couldn't find anything in the docs explaining how to bind a map.
Caveat: I'm pretty new to Ember myself.
Looking at the implementation of Ember.Map, I don't think you can currently (in 1.0.0-pre2) do this. Ember.Map implements create, get, and set, but is not a normal Ember object. So among other things, "properties" in a map aren't really properties, and there's no observables support. The handlebars implementation relies heavily on observable support, so I think what you're doing won't work.
Someone correct me if I'm wrong, but this is what I'm looking at in the 1.0.0-pre2 code:
var Map = Ember.Map = function() {
this.keys = Ember.OrderedSet.create();
this.values = {};
};
/**
#method create
#static
*/
Map.create = function() {
return new Map();
};
Map.prototype = {
/**
Retrieve the value associated with a given key.
#method get
#param {anything} key
#return {anything} the value associated with the key, or undefined
*/
get: function(key) {
var values = this.values,
guid = guidFor(key);
return values[guid];
},
...
Point being, it implements its own get (and create) rather than extending Ember.Object...so no observable support. Though I might have missed it if it reopen's later or something.
EDIT:
Also, not really what you asked, but if you're building an interface that relies on the existence of some key, you should really probably be defining your own model class that has these keys as properties. You can still set them to null if they're "not set". If you also need the ability to set arbitrary keys, make one of your properties an Ember.Map and call it otherProperties or something, and put them in there. But if your view depends on a known key, it should be a defined property.
I use console.log() a lot, especially in combination with Ember.inspect(). But there's one thing I miss:
How can I find out the type of an object (Class)?
For example: Getting something like <Sandbox.ApplicationController:ember288> when inspecting Ember.get("controller")?
If you just want the model name (for example app/models/comment.js has the model name comment), you can use thing.constructor.modelName.
For example:
var aComment = this.get('store').createRecord('comment');
aComment.get('constructor.modelName') // => 'comment'
I understand you are looking for a string for debugging purposes, but I originally came to this question wanting to know specifically how to get the type of the object, not a string describing the object.
Using the built in Javascript property constructor will yield the class used to construct the instance. For example you could do:
person = App.Person.create();
person.constructor // returns App.Person
person.constructor.toString() // return "App.Person"
If you get Class, you can usually call toString() (or as a shortcut concat an empty string + '') to get something like <Sandbox.ApplicationController:ember288>
Another useful feature (in chrome) is the dir command.
dir(App.User)
This will give you the full object information, rather than just the name.
Be aware that some of these answers suggested here only work in development. Once your code is in production most of those methods / class names will get minified.
import Model from '#ember-data/model';
export default class Animal extends Model {
// ...
}
So in development:
const model = this.store.createRecord('animal');
model.constructor.name // returns Animal
in production:
const model = this.store.createRecord('animal');
model.constructor.name // returns 'i' (or any other single letter).
To avoid this, use constructor.toString()
const model = this.store.createRecord('animal');
model.constructor.toString() // returns 'model:animal'
My model needs to have multiple enums of the same type:
class Broker {
static constraints = {
brokerTypes(nullable:false)
}
List<BrokerType> brokerTypes
}
The model is being instantiated with the params from the request, which has in it a list of BrokerTypes:
def save(){
def brokerInstance = new Broker(newParams)
System.out.println(brokerInstance.getBrokerTypes().toString());
if (!brokerInstance.save(flush: true)) {
render(view: "create", model: [brokerInstance: brokerInstance])
return
}
redirect(action: "show", id: brokerInstance.id)
}
The println prints out the list of BrokerTypes as expected, so i know that it exists in the instance. Later, the model is retrieved as follows:
def brokerInstance = Broker.findByLatAndLon(lat,lon)
System.out.println(brokerInstance.getBrokerTypes().toString());
This time the println prints out 'null'
So i imagine the problem is that GORM doesn't know how to store this list of enums, and instead when the brokerInstance.save() is called, its saving the brokerTypes field as null.
Do i need to create a mapping somehow to get GORM to recognize the list? A hack alternative would be to instead of storing the list of enums, to store a list of strings or something and then map back to the enum when needed, but this doesnt seem clean
You will have to use a hasMany clause so that grails/gorm initializes a one to many relationship
You should add the following snippet to your domain class.
static hasMany = [brokerTypes : BrokerType]