What's the difference between ember.js Object methods extend and create?
TLDR: You will find the answer in Ember guides: classes and instances.
Sometimes I see that one in the examples and sometimes the other. In particular, what's the difference between Em.Application.extend({}) and Em.Application.create({})?
If I declare my app like this, what does it mean?
Ember.Application.create({
MyController : Ember.ArrayController.extend({
}),
});
How can I access the instance of MyController? Do I need to create it somehow? I need to push some data into it.
The dead simple answer is that extend defines a new JS Class which inherits from the class you're extending, but does not create an instance of that class. create creates an instance of the class.
Em.Application is a particular case, because you're creating a namespace, not an object instance. I don't know when you'd ever want to extend Em.Application.
App = Em.Application.create(); // I have a new Em.Application namespace
App.A = Em.Object.extend(); // I have defined a new class, App.A,
// which inherits from Em.Object
var a = App.A.create(); // a now contains an instance of App.A.
I'd suggest you read "Naming Conventions", too.
ETA: And "Understanding Ember Objects", as suggested in zaplitny's comment.
From what little I understand, in layman's terms, you extend when you want to define a new object idea with properties that will never change(except for reopen), and exist throughout all versions of this object.
You create when you want to work with a particular individual (instance of) object. In other words, something with properties that will be changed by actions or other particular instances.
Often you only need to create relationships between objects, you don't need to speak about particular individual objects, but rather the idea of the object. Therefore, you don't need to create every time you extend.
Hopefully I'm understanding it correctly.
Related
Creating in-repo-addon in Ember.JS gives a lot of possibilities. Today I've stumbled into a case when I wanted to register all classes in directory (for example my-dir) into Dependency Injector (same way it's done with services by Ember, but I wanted to use some other namespace).
For example I want to allow developer to create multiple classes inside proxy directory, and register all of them in DI under proxy: namespace. So later we can use:
Ember.Component.extend({
myProxy: Ember.inject('proxy:my'),
});
You'll need to do this using an initializer. More details on this here: https://guides.emberjs.com/v2.12.0/applications/dependency-injection/
The hard part may be getting all proxy items in s folder to automatically register ...
Edit
Looks like I didn't spend enough time thinking about this. You should be able to do at least part of this easily. There are two parts to this.
Part 1
Ember currently uses the ember-resolver to handle lookups for various items. If you check the tests for the resolver you'll notice that you should be able to map in anything you want: https://github.com/ember-cli/ember-resolver/blob/master/tests/unit/resolvers/classic/basic-test.js
So in your case, if you do a Ember.getOwner(this).lookup('proxy:main') from within an Ember instantiated class (a route, controller or component for instance) it would look in app/proxy/main.js which your addon could be populating.
Details on the Ember.getOwner lookup are available here: https://emberjs.com/api/classes/Ember.html#method_getOwner
Part 2
So at this point you can lookup proxies (which would be doable in an init method). But if we want to get truly elegant we'd want to allow Ember.inject.proxy('main') syntax.
Doing so would involve calling a private method inside of Ember.inject in an initializer. Because that naming scheme is changing in the new Javascript modules RFC, it may be unwise to try to add this syntactic sugar ...
So I'd advise avoiding touching that private API unless it's really important to your app design.
i am new to realm and did not found a solution which was satisfies me.
i have an application where i can record tours with gps data and so on. (there are multiple different objects which are stored in realm).
i created a realm singleton which should do all my realm suff (update, create, delete) for my objects.
now i ran into the following problem:
i start a tour and record it. first it is created, everything is fine. then i came to the point where i have to update my tour object and only a few properties (basically each new gps point updates it). an additional requirement is, that there can be properties, which are not persistent in realm and are only on the object instance.
so now i have the options to call realm.add(object, update:true) which overrides all properties.
i cannot say object.prop1 = asdf , object.pro2 = 345 because i have no write context at this level of my logic. so i can update within a realm.create(type, updatedict, update:true)
but the big downside of this approach is, that i have to refetch the object again to "know" the changes on my object instance.
so updating some properties of an object results in:
create dictionary with id(primary key) and properties to change
call update on my realm singleton and passing all necessary data.
call a fetch on my realm instance to get the new object again, which leads me to loose existing not persisted property values.
i doubt i'm the first with such a requirement but i could not find a solution:
Summary:
Realm Singleton class handling all Realm actions within a write context
Different Realm Object classes which can have not persistent objects
Need partially update for some properties
dont want to have realm code in my viewcontrollers logic, only in its manager.
It's hard to suggest something without any code examples but personally I think not having the ability to update the individual properties of your models is not a good idea.
I think you have 2 options:
Add a method to your RealmSingleton that allows you to get write context (to execute a block inside a write transaction), like:
func updateTour(updateBlock: (Tour) -> Void) {
realm.write {
updateBlock(currentTour)
}
}
...
RealmSingleton.shared.updateTour { tour in
tour.property = value
}
Add the convenience methods to update the individual properties of your Tour object:
RealmSingleton.shared.setTourProperty(value)
I'm using Ember CLI and by default it seems to set Ember.MODEL_FACTORY_INJECTIONS = true; in app.js.
I tried commenting this line out (and setting it to false) and then my app seemed to behave in some sort of strict mode. I got a bunch of failed assertions because some of my Model relations didn't explicitly specify the inverse.
This is the exact error:
You defined the 'account' relationship on (subclass of DS.Model), but multiple possible inverse relationships of type (subclass of DS.Model) were found on (subclass of DS.Model). Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses
Using the default Ember CLI generated app with Ember.MODEL_FACTORY_INJECTIONS = true;, I didn't get these errors. So I'm lead to believe that this flag changes core behaviour somehow.
Insight please!
DS.Model, your model base class, is just another class defined by Ember Data. In order for it to have special functionality like dependency injection, Ember needs to hook into that class so that when you instantiate it, the instance references the app container. When Ember.MODEL_FACTORY_INJECTIONS is on, Ember applies additional mixins to the class so it can.
You can use Ember.getOwner(instance) to get the app container (or owner) of an instance. This is how you can, for example, look up the store of your application or a controller instance. When you call MyModelClass.create() directly, the owner isn't set. You either need to use Ember.setOwner() or instantiate it with an existing owner. See ApplicationInstance#ownerInjection().
let owner = Ember.getOwner(this);
User.create(
owner.ownerInjection(),
{ username: 'rwjblue' }
)
However, many applications that use Ember Data don't instantiate model instances directly and don't need to do this.
You can currently use Ember.MODEL_FACTORY_INJECTIONS = true to opt in to the functionality, but I believe they're moving towards making that the default in the future.
So turning the feature off caused errors since certain mixins were not getting included in your model classes.
I'm using the CreateObject() method to create an instance of a CFC and then interacting with this newly created 'instance'. I'm doing this because that's how it seems to be done, but I don't understand why we do this.
Why can't we just interact with the CFC directly instead of creating an instance of it?
A CFC is just a file with some code in it, so it makes little sense to suggest "interacting" with it, just the same as you might suggest "interacting" with a CFM file without <cfinclude>-ing it or similar.
A CFC defines a component, and to use a component, one creates an instance of it. In some languages - eg Java - one can have static properties and methods, and one can access them via the class rather than necessarily object, but CFML does not have this concept. CFCs define components which are used as objects, just the same as in other languages a class defines what it is to be an object, and to use an object, one first needs to create an instance of it.
You can call the cfc directly using cfinvoke. You just have to realize that cfinvoke creates an object of the cfc first, then executes the method you invoked. Also, once the method is invoked, the object is no longer available.
If your .cfm page is only going to use one method of the component, cfinvoke is ok because there is less code for you to write. However, if you use two or more, it's less efficient because a new object has to be created each time.
In other word, while you don't have to create an instance of the cfc first, it's often a good idea to do so.
I hope you have read OOPs and its practices. CFC is your 'blueprint' (say a car design) and object is your own data model (say a car of blue color (method to set color), with nitrogen filled tires (method to set pressure in tires) and runs on LPG (method for fuel type)). CF allow you interact directly with CFC (CFINVOKE) and you do not have to create an instance each time but it just only make sense that you would not want to go to workshop/design lab each time you want to change a configuration for your car.
In the notes of this commit, the Ember team have made it very clear that App.__container__.lookup() is not the way to get at controllers. Instead we should use the needs property.
I understand the rationale behind this, and the idiomatic way to access singleton controllers.
However, in my app, I have some cases where I need instance controllers. In that case, I am using App.__container__.lookupFactory() to get at the prototype which I can then create() or extend()
Is there a better way to do this (without using __container__?
Edit:
Here is an example use case.
App.MyContainerView = Ember.ContainerView.extend
...
addChildView: ->
#get("content").pushObject(App.MyChildView.create(...))
The above example will push a new view onto the stack (allowing views to be dynamically created)
However, these views will (may?) not have the right container (and other properties?) set due to being created using App.MyChildView.create(). This is especially true in cases where we are doing a partial integration of Ember into an existing app.
The way to create these views would instead be:
App.__container__.lookupFactory("view:my_child").create()
In which case everything would be ok.
Additional use cases exist, for creating instance controllers outside the context of the router.. but the idea is the same.
I don't know if you're still looking for an answer. I am also struggling with how to do things "the Ember way".
This answer put me on the right track, and should be relevant to your question:
"Please ensure this controller was instantiated with a container"
As for me, I had the same problem as in the above question: when I manually instantiated my App.AnyOtherController with App.AnyOtherController.create(...), then inside this controller, I could not access dependency injections (such as a session object that I make available to all my controllers and routes).
Instantiating the same controller this way solves the problem by giving the controller a container:
this.container.lookupFactory('controller:any_other').create(...)
You should be able to access this.container from any view, and I guess, any controller, as long as they have been given a container.
You can Ember.String.decamelize('AnyOther') to convert the CamelCase controller name to a suitable string.
More on containers here: http://ember.zone/beginning-to-understand-the-ember-js-container/
If it doesn't help you, I still hope this helps someone out there, as this container stuff is a bit tricky at first...