I have a Rails app that is namespaced into three sections (almost 3 apps that share models). I would like for each namespaced section to have it's own Ember app. These apps are never loaded in the same layout so don't have to know anything about each other. In fact I would like to keep the code as separate as possible for when the app can eventually be really split up.
I am trying to do this using the ember-rails gem.
Basically it is like this question:
How can I specify an alternative directory for my HandlebarsJS templates with the ember-rails gem?
And the answer there works, but I'm pretty sure using templates_root limits me to just one namespace. So I couldn't also have an admin.js and admin/templates namespace as well as a customer.js and customer/templates namespace.
So does anyone know if ember-rails will support multiple namespaced Ember apps and render multiple template roots as a result?
Thanks!
As posted here you can have namespace templates by adding a custom resolver to each app.
App1 = Ember.Application.create({
Resolver: Ember.DefaultResolver.extend({
resolveTemplate: function(parsedName) {
parsedName.fullNameWithoutType = "app1/" + parsedName.fullNameWithoutType;
return this._super(parsedName);
}
})
});
App2 = Ember.Application.create({
Resolver: Ember.DefaultResolver.extend({
resolveTemplate: function(parsedName) {
parsedName.fullNameWithoutType = "app2/" + parsedName.fullNameWithoutType;
return this._super(parsedName);
}
})
});
App3 = Ember.Application.create({
Resolver: Ember.DefaultResolver.extend({
resolveTemplate: function(parsedName) {
parsedName.fullNameWithoutType = "app3/" + parsedName.fullNameWithoutType;
return this._super(parsedName);
}
})
});
Related
I've seen this url Ember-cli Pods & Loading Templates but adding a /app/bugs/loading.hbs or /app/bugs/detail/loading.hbs or even /app/bugs/detail/loading/template.hbs doesn't work.
What is the specific name/path of the template?
Its my understanding loading/error/index routes are generated automatically when you add an additional functions in this.route('bugs', function(){})
I'm missing something probably easy - any thoughts?
Since the docs don't specifically mention pods and I've seen a few people pose this question on the net using what seems like an older folder structure, I'll answer here.
For my /bugs route I have the following at
./app/bugs/index/route.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('bug');
},
actions: {
loading(transition, originRoute) {
console.log('loading transition');
console.log(originRoute);
//let controller = this.controllerFor('bugs');
//controller.set('currentlyLoading', true);
transition.promise.finally(function () {
console.log('done loading');
//controller.set('currentlyLoading', false);
});
}
}
});
The loading template it looks for is located at
./app/bugs/index-loading/template.hbs
I have not been able to test React Router with context successfully. I am using
react 0.13.3
react-router 0.13.3
jest 0.3.0
node 0.10.33
and have tried these approaches:
https://labs.chie.do/jest-testing-with-react-router/
https://gist.github.com/alanrubin/da3f740308eb26b20e70
Is there a definitive example?
All links to the "super-secret guide" mentioned in this question (which does not use Jest) are now broken. When I was able to view that guide, it didn't provide any more information than the first link listed above.
Not sure if this is what you're looking for exactly, but I got around this by making a helper function that I use when writing jest tests for components that depend on router state.
//router-test-helper
var Router = require('react-router'),
Route = Router.Route,
TestLocation = require('react-router/lib/locations/TestLocation');
module.exports = function(React){
TestUtils = React.addons.TestUtils;
return {
getRouterComponent: function(targetComponent, mockProps) {
var component,
div = document.createElement('div'),
routes = [
React.createFactory(Route)({
name: '/',
handler: targetComponent
})
];
location = new TestLocation('/');
Router.run(routes, location, function (Handler) {
var mainComponent = React.render(React.createFactory(Handler)(mockProps), div);
component = TestUtils.findRenderedComponentWithType(mainComponent, targetComponent);
});
return component;
}
};
};
I didn't write all of this on my own, most of it I think I pulled from that now defunct guide you linked to. If I remember right... it's been a while.
After you have that you can use this in your tests kinda like this.
//test-example
jest.dontMock('../src/js/someComponent');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var routerHelper = require('../router-test-helper')(React);
var SomeComponent = require('../srcs/js/someComponent');
describe('Some Component', function(){
it('should be testable', function(){
var mockProps = {};
var renderedComponent = routerHelper.getRouterComponent(SomeComponent, mockProps);
// Test your component as usual from here.....
///////////////////////////////////////////////
var inputs = TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'input');
//blah blah blah
});
});
This assumes you have React and the helper in your unmocked module paths
If you're actually trying to test things specific to certain routes, or transitioning between routes... I'm not sure if this is a good approach. May be better to use something more intergration test-y, like selenium or something.. Also... this probably won't work once the 1.0 of react router comes out. But it may be even easier to test things thing 'The React Way'(tm) because all the routing stuff will be handled through props. At least that's the impression I get from the the little bit I've read into it.
To anyone who happens to stuck with this problem.
Here's the setup that I've ended up with for my context dependent components (stripped down for simplicity, of course):
// dontmock.config.js contains jest.dontMock('components/Breadcrumbs')
// to avoid issue with hoisting of import operators, which causes
// jest.dontMock() to be ignored
import dontmock from 'dontmock.config.js';
import React from "react";
import { Router, createMemoryHistory } from "react-router";
import TestUtils from "react-addons-test-utils";
import Breadcrumbs from "components/Breadcrumbs";
// Create history object to operate with in non-browser environment
const history = createMemoryHistory("/products/product/12");
// Setup routes configuration.
// JSX would also work, but this way it's more convenient to specify custom
// route properties (excludes, localized labels, etc..).
const routes = [{
path: "/",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "products",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "product/:id",
component: React.createClass({
// Render your component with contextual route props or anything else you need
// If you need to test different combinations of properties, then setup a separate route configuration.
render() { return <Breadcrumbs routes={this.props.routes} />; }
}),
childRoutes: []
}]
}]
}];
describe("Breadcrumbs component test suite:", () => {
beforeEach(function() {
// Render the entire route configuration with Breadcrumbs available on a specified route
this.component = TestUtils.renderIntoDocument(<Router routes={routes} history={history} />);
this.componentNode = ReactDOM.findDOMNode(this.component);
this.breadcrumbNode = ReactDOM.findDOMNode(this.component).querySelector(".breadcrumbs");
});
it("should be defined", function() {
expect(this.breadcrumbNode).toBeDefined();
});
/**
* Now test whatever you need to
*/
I usually use Rails for my Ember apps. However this time we opted to decouple the API from the Ember app, and as such I'm trying EmberCLI. So far it's lovely to setup and use. However when using attempting to use fixtures it doesn't load the data.
As listed in this post I am using reopenClass when declaring the fixtures.
If I do not override the model, it does not error but the Ember inspector also shows no data was loaded. If I override my file with:
// routes/campaigns/index.js
export default Ember.Route.extend({
model: function() {
return this.store.find('campaign');
}
});
And visit the /campaigns path then I get the error I get the error Error while loading route: undefined.
From what I can find this seems to happen when Ember cannot find the data.
My router and model with obvious items like export default excluded:
// app/router.js
Router.map(function() {
this.resource('campaigns', function() {
});
});
// models/campaign.js
var Campaign = DS.Model.extend({
name: DS.attr('string')
});
Campaign.reopenClass({
FIXTURES: [
{ "id": 1, "name": "Campaign #1" },
{ "id": 2, "name": "Campaign #2" }
]
});
I have tested the same setup in a Rails app I just made, and it works perfectly. I'd love any insight people could give, as EmberCLI seems lightweight and worth the effort.
Edit: Adding my app.js file to answer question about whether I included DS.FixtureAdapter:
// Import statements
Ember.MODEL_FACTORY_INJECTIONS = true;
var App = Ember.Application.extend({
modulePrefix: 'nala', // TODO: loaded via config
Resolver: Resolver
});
loadInitializers(App, 'nala');
App.ApplicationAdapter = DS.FixtureAdapter({});
export default App;
You need to set up your application adapter located at the filepath adapters/application.js as follows:
export default DS.FixtureAdapter.extend({});
See the first paragraph under ember-cli Naming Conventions. N.B. you won't need to import DS or Ember if you're using ember-cli and have them listed in your .jshintrc file.
I'm trying out Ember.JS and I'm having a really tough time using it with Require.JS so far, even with a (pretty) basic example.
First of all, I'd like to say that Require.JS is supposed to (I think) improve two weak points I see in Ember.JS :
Organizing the app, especially in separate js files
Not loading unnecessary code
I'm basically trying to display an app with header/content/footer. So, when I'm creating my App I'm binding a ApplicationController and an ApplicationView, and the view handles the template. This works great in displaying (pretty easily) the header and the footer.
Then, I'm trying to render a template for the index (for example), and I would like to dynamically load IndexView/IndexController (for example) and bind it with a route. That's where I'm having a tough time.
I found an easy way to do this by setting IndexView directly as App.IndexView, but the problem with this solution is that if I load IndexView, I'm also loading the index template file content (using text.js plugin). That would be okay for my example, BUT since I'm trying to build a complex website, that would mean loading all the templates when loading the website, which is exactly what Require.JS was trying to avoid.
Where am I wrong here? How do I dynamically load the template depending on the routing?
EDIT: It's not really needed to declare a placeholder in the main html document as it's injected using view.append().
I've been struggling with the same thing, and I finally came up with a way to split over router, controller, views and templates loading them dynamically.
This is my main "embermain.js" file:
window.MyRanks = Ember.Application.create();
MyRanks.Router.map(
function() {
this.route('about');
}
);
MyRanks.AboutRoute = Ember.Route.extend({
setupController: function(controller, model) {
require(['app/controller/AboutController'], function(controller) {
});
}
});
Here is my AboutController:
require(
['app/view/AboutView'],
function (view) {
var controller = MyRanks.AboutController = Ember.Controller.extend({
});
return controller;
});
Here is my AboutView:
define(
['text!app/templates/about.html'],
function (template) {
var view = Ember.View.create({
template: Ember.Handlebars.compile(template),
templateName: 'about',
variable: 'my value',
didInsertElement: function() {
console.log( "Yes the view was included");
}
});
view.append();
return view;
}
);
And here is the template about.html
This is the template {{view.variable}}
Hope it helps! :)
I'm trying to use Jasmine (gem with yaml config) to test a Backbone.js application. I'm using the underscore templating like the Todo example.
template: _.template($('#item-template').html())
My problem is that I'm unable to have the templates loaded before my models/views so the template call causes those classes to error out at load.
I've read about the jasmine-jquery plugin to do fixtures but the problem is that my src files (models/views) are being loaded and failing before I ever get the the spec file and am able to setup the fixtures needed.
How do I get the templates loaded early enough they can be used for the reset of my classes?
You can delay the jQuery selector until you need it:
render: function(){
var templateHtml = $(this.template).html();
_.template(templateHtml);
}
Or you can run the selector when the view initializes:
initialize: function(){
this.template = _.template($(this.template).html());
}
Or, if you really want to leave your code as-is and have the selector evaluate when you define the View, you can wrap all of your Backbone code in a function that you call when you want to initialize your entire app code... such as a jQuery $(function(){} function on your real HTML page, or a beforeEach function in your Jasmine tests:
MyApp = (function(){
var myApp = {};
myApp.MyView = Backbone.View.extend({
template: _.template($("#item-template").html())
// ...
});
return myApp;
});
Then in your app, to start this up:
$(function(){
var myApp = MyApp();
new myApp.MyView();
// ...
});
And in your Jasmine test:
describe("how this thing works", function(){
beforeEach(function(){
var myApp = MyApp();
this.view = new myApp.MyView();
// ...
});
});
Once you have one of these solutions in place, you can use something like Jasmine-jQuery to load your fixtures.
FWIW: I tend to use a combination of these techniques, as needed.
The one issue with the first part of the accepted answer is that the template gets compiled every time the view is instantiated. One alternative is to set the template directly on the prototype of the view:
app.TodoView = Backbone.View.extend({
initialize: function(){
if (!app.TodoView.prototype.template) {
app.TodoView.prototype.template = _.template($("#item-template").html());
}
}
});
This way, the template is compiled once, when the first instance of TodoView is instantiated.