How to access the Ember Data Store from the console? - ember.js

In Ember 2+, does anyone know how to get a reference to the Ember Store in order to troubleshoot Model mapping in the javascript console?
It was possible through App.__container__.lookup in Ember 1, but this doesn't exist anymore, and it's bloody hard to find documentation on this.
Thanks

If you look in your package.json, you should see a ember-export-application-global package that's installed by default (if not, install it). This will export your application not to the global App object, but to a global object that's named after your app. So you might have window.TodoList or window.ShoppingCart instead of window.App. From there you can use this line (similar to Ember 1.x.x):
AppName.__container__.lookup('service:store')
You can also do what I do and create an instance initializer for it:
export default {
name: 'store-on-app',
after: 'ember-data',
initialize(instance) {
const application = instance.container.lookup('application:main');
const store = instance.container.lookup('service:store');
application.set('store', store);
}
}
Then you can just use AppName.store.

If you don't want to install a separate package to access your app in the console, you can do it through window.Ember.Namespace.NAMESPACES. For example, something you can run in the console to find your app instance is:
var app = Ember.A(Ember.Namespace.NAMESPACES).filter(n => {return n.name === 'your-app-name'})[0];
From here, you can access the store on the app's container as explained by #GJK
var store = app.__container__.lookup('service:store');
I used this for debugging an Ember app in production which didn't have its container registered on the window. I found it out by looking through the ember-inspector source code, since it always has access to the container.
https://github.com/emberjs/ember-inspector/blob/2237dc1b4818e31a856f3348f35305b10f42f60a/ember_debug/vendor/startup-wrapper.js#L201

Related

ReferenceError: ga is not defined [Ionic 2.2 Unit Testing With Karma]

I'm adding unit tests to an Ionic 2.2.0 app I manage, but my Components crash at test-time when they encounter Google Analytics code. I'm using Ionic's official unit testing example as a basis, and my current progress can be seen on our public repo.
My project uses Google Analytics, which is added to the HTML and downloaded at runtime (because we have different keys for development vs production).
The code that initializes Analytics is in my main.ts, and it sets a global variable ga, which is subsequently available throughout the application.
I'm beginning the tests for the app's first page, which uses Analytics. When I run the tests, I'm met with the following error
Component should be created FAILED
ReferenceError: ga is not defined
at new MyBusesComponent (webpack:///src/pages/my-buses/my-buses.component.ts:33:6 <- karma-test-shim.js:138419:9)
at new Wrapper_MyBusesComponent (/DynamicTestModule/MyBusesComponent/wrapper.ngfactory.js:7:18)
at CompiledTemplate.proxyViewClass.View_MyBusesComponent_Host0.createInternal (/DynamicTestModule/MyBusesComponent/host.ngfactory.js:15:32)
........
This is because main.ts doesn't seem to be loaded or executed, and I assume TestBed is doing that purposefully. It's certainly better that I don't have the actual Google Analytics object, but the Component does need a function called ga.
My question, therefore, is as follows: how can I create Google Analytics' ga variable in my test configuration such that it's passed through to my components at test-time?
I've tried exporting a function from my mocks file and adding it to either the imports or providers arrays in my spec file, but to no avail.
I appreciate any advice! Feel free to check my code at our repo I linked to above and ask any followups you need. Thanks!
You declare the var ga but that is just to make TypeScript happy. At runtime, the ga is made global from some external script. But this script is not included in the test.
What you could do is just add the (mock) function to the window for the tests. You could probably do this in your karma-test-shim.js.
window.ga = function() {}
Or if you wanted to test that the component is calling the function with the correct arguments, you could just add the function separately in each test that uses the function. For example
beforeEach(() => {
(<any>window).ga = jasmine.createSpy('ga');
});
afterEach(() => {
(<any>window).ga = undefined;
})
Then in your test
it('..', () => {
const fixture = TestBed.creatComponent(MyBusesComponent);
expect(window.ga.calls.allArgs()).toEqual([
['set', 'page', '/my-buses.html'],
['send', 'pageview']
]);
})
Since you're making multiple calls to ga in the constructor, the Spy.calls will get the argument of all each call and put them in separate arrays.

Ember Inject Service into Ember Utility

I know Ember has a logger, but I wanted to create my own for learning purposes. I have a service called logger, and I want to be able to use this service everywhere. I have no problem injecting this service into components, controllers, and etc... I cannot figure out how to inject this service into a Utility I created without passing it through the create function. I don't want to have to pass my logger everywhere I create the utility. When I try to inject it into the object it complains about not being in a container. What's the best way to do this?
Okay, its important to understand what Ember.inject.service actually does! Its like a shorter version for this:
myService: Ember.computed({
get() {
return Ember.getOwner(this).lookup('service:myService);
}
}),
So what is this getOwner? It gives you the owner of an Object. Most of your objects like models, controllers, components, views and so on are created by the Dependency Injection (DI) container. For a class to be available on the DI container it needs to be registered.
Your default classes like controllers, routes, views are automatically registered by the Resolver. After registration you can inject them into other classes automatically when they are created by the container. Also into all instances created by the container the owner is injected.
Because the container itself is private, these public APIs are on the Application. getOwner also returns the application.
If you want to manually lookup an instance on the container you can use lookup.
For your utility class you probably use a normal .create() to get the object. This of course will not automatically couple it to your application, so the owner is not available. Also automatic injection will not work.
You can manually inject the owner with the ownerInjection:
myClass.create(Ember.getOwner(this).ownerInjection(), {...});
Then Ember.inject.service will work because getOwner will return the injected owner.
The other thing you could do is to register your utility objects on the container and then look them up. Then the owner is automatically injected.
Not sure which Ember version initiated this pattern but the Ember documentation contains the answer to that question starting from v4.3:
import { inject as service } from '#ember/service';
import { getOwner, setOwner } from '#ember/application';
class Item {
#service('shopping-cart') cart;
constructor(context) {
setOwner(this, getOwner(context));
}
function addToCart() {
this.cart.add(this);
}
}
// On any framework object...
let item = new Item(this);
item.addToCart();
I hit a similar problem a little while back.
The utilities are of type Ember.Object.
So, all you have to do is inject the service into the Ember.Object class as a property.
Like this:
Ember.Object.reopen({
testService:Ember.inject.service('testService')
});
Et Voila!Now you can literally use your service anywhere

Using Ember CLI Addon Models in Ember Application?

I want to allow something like the following to work in my application:
store.find('my-addon.my-addon-model', 1)
store.find('my-addon/my-addon-model', 1)
store.find('my-addon:my-addon-model', 1) (unlikely)
The thing is I want it to search for a model that is 100% defined in an addon.
import MyAddonModel from 'my-addon/models/my-addon-model' works from within my app - but container resolution doesn't...
How would I do/allow this?
This question is the same as:
Registering models from another namespace with the Ember Data store
However the naming there is a bit confusing. When trying this out I also seem to have hit a bug in ember-data#1.0.0-beta.15.
What you need to do in your module initializer is register the model(s) to your application.
import Ember from 'ember';
import MyModel from my-module/models/my-model';
export default {
name: 'my-module-models',
initialize: function(container, application) {
//one of these calls for each model you need to register
application.register('model:myModule.myModel',MyModel);
}
};
Now according to my experience with Ember and Ember-Data, this is supposed to work just like that. But in my setup with ember-data#1.0.0-beta.15 there seems to be an issue determining the models "key" after creation. The model instance is created fine, but you will hit an error trying to save the model.
I've found a quick workaround, but it's a hack and I would need to investigate further whether I'm missing a step or it's a bug. The workaround involves setting the "typeKey" of the class, resulting in:
import MyModel from my-module/models/my-model';
export default {
name: 'my-module-models',
initialize: function(container, application) {
//one of these calls for each model you need to register
application.register('model:myModule.myModel',MyModelreopenClass({typeKey:'myModule.myModel'}));
}
};
Finally, there is another way around. When creating modules in ember-cli, you have the app folder which will be merged with the app using your module. You could place default extends of your model(s) in the app folder.

ember-cli App.get / App.set throw undefined

I'm converting a globals based real-time Ember app to an es6 based app that utilizes ember-cli. In my app I need to know the current route fairly often. In the globals version, I was doing this.
Globals Pattern
var MyApp = Ember.Application.create({
currentRoute : ''
});
MyApp.Route = Ember.Route.extend({
actions : {
didTransition : function () {
MyApp.set('currentRoute', this);
}
}
});
I could then do MyApp.get('currentRoute') from within my session or offline controllers when determining how / where to transition when certain events occurred.
When using ember-cli, I import the app to be able to reference it from the necessary controllers.
import MyApp from "../app";
But it turns out that MyApp.currentRoute, MyApp.get, and MyApp.set are all undefined.
Part of me thinks this is a bug in ember-cli that the application instance no longer has bound getters and setters. Part of me realizes it's not a great practice to store things on the application instance either.
I could get around this issue by converting all instances of MyApp.get and MyApp.set to Ember.get(MyApp, ...) and Ember.set(MyApp, ...) respectively, but I thought I'd ask here first as this seems to either be an issue with Ember-Cli or else something where there's a better recommended way to achieve what I need.
If you look at app.js (what you are importing), it is not your application instance, it is exporting a subclass of Ember.Application. That's why get et al are not available on it.
var App = Ember.Application.extend({ <----- NOT create
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver: Resolver
});
export default App;
To get the actual application instance from within your route use:
Ember.getOwner(this).lookup('application:main')

Why does this Ember.js app work locally but not on jsFiddle.net?

Here's the fiddle. Here's the gist with the contents of my local file.
As you can see, the HTML and JavaScript are identical, and I'm loading identical versions of the jQuery, Handlebars.js, and Ember.js libraries. It works as expected locally, but does not render the application template on jsFiddle.net.
I see the following error in the Web Console:
[19:44:18.202] Error: assertion failed: You must pass at least an object and event name to Ember.addListener # https://github.com/downloads/emberjs/ember.js/ember-latest.js:51
BTW-To test the gist as a local HTML file, make sure to run it behind a web server or your browser won't download the JavaScript libs. If you have thin installed (ruby webserver), go to the directory it's in and run thin -A file start, then navigate to localhost:3000/jsfiddle-problem.html in your browser.
If you set the "Code Wrap" configuration on your fiddle to one of the options other than "onLoad" your application will work. Here is an example.
The reason for this is Ember initializes an application when the jQuery ready event fires (assuming you have not set Ember.Application.autoinit to false). With the jsFiddle "Code Wrap" configuration set to "onLoad" your application is introduced to the document after the jQuery ready event has fired, and consequently after Ember auto-initializes.
See the snippet below from ember-latest, taken on the day of this writing, which documents Ember auto-initialization being performed in a handler function passed to $().ready.
if (this.autoinit) {
var self = this;
this.$().ready(function() {
if (self.isDestroyed || self.isInitialized) return;
self.initialize();
});
}
This was strange - I couldn't get your fiddle working, specifically your {{controller.foo}} call until I disabled autoinit. I am guessing when using jsfiddle the application initialize kicks off before seeing your router. I also noticed with your fiddle the router was not logging any output even when you had enableLogging set to true.
I updated your fiddle to not use autoinit, http://jsfiddle.net/zS5uu/4/. I know a new version of ember-latest was released today, I wonder if anything about initialization changed.