How do you get a Loading template in ember-cli PODS? - ember.js

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

Related

Clean Ember 1.13+ way of knowing if child route is activated

Assume we have an Article model as follows:
export default DS.Model.extend({
author: DS.belongsTo('user'),
tagline: DS.attr('string'),
body: DS.attr('string'),
});
Assume also that we have a lot of pages, and on every single page we want a ticker that shows the taglines for brand new articles. Since it's on every page, we load all (new) articles at the application root level and have a component display them:
{{taglines-ticker articles=articles}}
{{output}}
That way we can visit any nested page and see the taglines (without adding the component to every page).
The problem is, we do not want to see the ticker tagline for an article while it's being viewed, but the root-level taglines-ticker has no knowledge of what child route is activated so we cannot simply filter by params.article_id. Is there a clean way to pass that information up to the parent route?
Note:
This is not a duplicate of how to determine active child route in Ember 2?, as it does not involve showing active links with {{link-to}}
Ember is adding a proper router service in 2.15; this exposes information about the current route as well as some methods that allow for checking the state of the router. There is a polyfill for it on older versions of Ember, which might work for you depending on what version you're currently using:
Ember Router Service Polyfill
Based on the RFC that introduced that service, there is an isActive method that can be used to check if a particular route is currently active. Without knowing the code for tagline-ticker it's hard to know exactly how this is used. However, I would imaging that you're iterating over the articles passed in, so you could do something like:
export default Ember.Component.extends({
router: Ember.inject.service(),
articles: undefined,
filteredArticles: computed('articles', 'router.currentRoute', function() {
const router = this.get('router');
return this.get('articles').filter(article => {
// Return `false` if this particular article is active (YMMV based on your code)
return !router.isActive('routeForArticle', article);
});
})
});
Then, you can iterate over filteredArticles in your template instead and you'll only have the ones that are not currently displayed.
You can still use the link-to component to accomplish this, and I think it is an easy way to do it. You aren't sharing your taglines-ticker template, but inside it you must have some sort of list for each article. Make a new tagline-ticker component that is extended from the link-to component, and then use it's activeClass and current-when properties to hide the tagline when the route is current. It doesn't need to be a link, or look like a link at all.
tagline-ticker.js:
export default Ember.LinkComponent.extend({
// div or whatever you want
tagName: 'div',
classNames: ['whatever-you-want'],
// use CSS to make whatever class you put here 'display: none;'
activeClass: 'hide-ticker',
// calculate the particular route that should hide this tag in the template
'current-when': Ember.computed(function() {
return `articles/${this.get('article.id')}`;
}),
init() {
this._super(arguments);
// LinkComponents need a params array with at least one element
this.attrs.params = ['articles.article'];
},
});
tagline-ticker being used in taglines-ticker.hbs:
{{#tagline-ticker}}
Article name
{{/tagline-ticker}}
CSS:
.hide-ticker {
display: none;
}
I tried to extend the LinkComponent, but I ran into several issues and have still not been able to get it to work with current-when. Additionally, if several components need to perform the same logic based on child route, they all need to extend from LinkComponent and perform the same boilerplate stuff just to get it to work.
So, building off of #kumkanillam's comment, I implemented this using a service. It worked perfectly fine, other than the gotcha of having to access the service somewhere in the component in order to observe it.
(See this great question/answer.)
services/current-article.js
export default Ember.Service.extend({
setId(articleId) {
this.set('id', articleId);
},
clearId() {
this.set('id', null);
},
});
routes/article.js
export default Ember.Route.extend({
// Prefer caching currently viewed article ID via service
// rather than localStorage
currentArticle: Ember.inject.service('current-article'),
activate() {
this._super(arguments);
this.get('currentArticle').setId(
this.paramsFor('articles.article').article_id);
},
deactivate() {
this._super(arguments);
this.get('currentArticle').clearId();
},
... model stuff
});
components/taglines-ticker.js
export default Ember.Component.extend({
currentArticle: Ember.inject.service('current-article'),
didReceiveAttrs() {
// The most annoying thing about this approach is that it
// requires accessing the service to be able to observe it
this.get('currentArticle');
},
filteredArticles: computed('currentArticle.id', function() {
const current = this.get('currentArticle.id');
return this.get('articles').filter(a => a.get('id') !== current);
}),
});
UPDATE:
The didReceiveAttrs hook can be eliminated if the service is instead passed through from the controller/parent component.
controllers/application.js
export default Ember.Controller.extend({
currentArticle: Ember.inject.service('current-article'),
});
templates/application.hbs
{{taglines-ticker currentArticle=currentArticle}}
... model stuff
});
components/taglines-ticker.js
export default Ember.Component.extend({
filteredArticles: computed('currentArticle.id', function() {
const current = this.get('currentArticle.id');
return this.get('articles').filter(a => a.get('id') !== current);
}),
});

Shared data between various controllers

I have a project where I need to build an Ember application. The application will have many routes and some of the routes will have some model.
My problem at the moment is that some information is global, meaning they are present in each page (.hbs) and I need to update it periodically.
I've tried to put information on the application route like the following but it didn't work, the content is not accessible on other routes:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/api/users/current')
}
});
I've also tried to reload the information with a setInterval but this didn't work either.
import Ember from 'ember';
export default Ember.Route.extend({
init: function() {
var thyself = this;
var interval = setInterval(function(){
thyself.my_reload()
}, 1000);
this.set('interval', interval);
this.set('counter', {});
},
my_reload: function() {
var counter = this.get('counter');
if (counter >= 10) {
clearInterval(this.get('interval'));
}
this.set('data', Ember.$.getJSON('/api/users/current'));
}
});
Where can I place this information so it will be available on all routes? And how can I reload the information periodically?
I'm using ember-cli
#NicosKaralis,
you should use service for it.
You can generate it by command: ember generate service name-of-service
And there you should create methods.
When you want to get access from your controller you should inject it in your controller:
nameOfService: Ember.inject.service(), (remember camelCase here)
and if you want some method from your service in your controller you will use it like this (example with computed property, you can also use it without computed property):
someComputedFunctionInController: Ember.computed(function() {
this.get('nameOfService').yourFunctionFromService();
},
nextComputedFunctionInController: Ember.computed(function() {
this.get('nameOfService.getSomethingFromService');
}
for more:
https://guides.emberjs.com/v2.7.0/tutorial/service/
Hope, it will help you.

How to properly use Fixtures with ember-cli project structure

I'm using ember-cli and trying to make some sense of the structure of the app and how it is all wired together. There are some differences in the main Ember guide docs and what I'm seeing in the ember-cli generated project. I understand the API's are moving fast so I just need to be pointed in the right direction.
In router.js I have the following:
Router.map(function() {
this.route('domains', {path: "/domains" });
});
Then I have models/domain.js
import DS from 'ember-data';
var Domain = DS.Model.extend({
name: DS.attr('string')
});
Domain.reopenClass({
FIXTURES: [
{ id: 1, name: 'User'},
{ id: 2, name: 'Address'}
]
});
export default Domain;
And I have routes/domains.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.all('domain');
}
});
And finally ( I think ), I have templates/domains.hbs
<h1>Domains</h1>
{{#each}}
<p>{{name}}</p>
{{/each}}
Only the header is being rendered when I visit the http://localhost:4200/domains url. I'm using the ember chrome extension and I don't see any data coming back in the request. I'm not sure if it is a naming convention issue or what I'm doing wrong so any help is appreciated.
all just returns records that have already been found in the store. find will issue a request (in this case hitting the fixtures) and populate the store, and also return all of the records in the store.
this.store.find('domain');
The problem ended up being 2-fold. Kingpin2K was right in that I needed to use find instead of all. I also had to change the adapter to the following in adapters/application.js:
export default DS.FixtureAdapter.extend();

EmberCLI app errors when using fixtures

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.

Get current route name in Ember

I need to get the current route name in my ember application; I tried this:
Ember App.Router.router.currentState undefined
but it doesn't work for me (there is probablig something i'm missimg...) I use Ember rc6 and I have a multilingual app; in the applicationRoute I detect the browser's language and I redirect to the correct page with:
this.transitionTo(userLang);
but I would like this to be executed only when user are on the home page, so something like this:
if (currentRoute == 'home'){
this.transitionTo(userLang)
}
NOTE: as of Ember 3.16, the original answer is not only recommended, but observers are strongly discouraged.
To get the current route name, you can utilize the Router Service: https://api.emberjs.com/ember/3.18/classes/RouterService/properties/currentRouteName?anchor=currentRouteName
export default class MyComponent extends Component {
#service router;
get activeRoute() {
return this.router.currentRouteName;
}
}
Original answer below
You could observe the application's currentPath and set it to the current route accordingly when it changes:
App = Ember.Application.create({
currentPath: '',
});
App.ApplicationController = Ember.Controller.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
}.observes('currentPath')
}),
This way you have access to the currentPath when ever you want with App.get('currentPath');
E.g.
if (App.get('currentPath') == 'home'){
this.transitionTo(userLang);
}
Hope it helps.
This worked for me on 1.3.0-beta (and a quick glance at the source for 1.1.2 suggests it would work there too):
App.__container__.lookup('router:main').location.lastSetURL
Note that the documentation states:
At present, it relies on a hashchange event existing in the browser.
However, I believe it's strongly suggested that App.__container__ not be used in production code. A more acceptable alternative would be to use App.Router.router.currentHandlerInfos, which provides information on the current Ember route.
Yet another option is currentRouteName on the ApplicationController. You can add needs: ['application'] to your controller, then access the route name with controllers.application.currentRouteName. This will return something like posts.index.
With the shift to components, it is harder to get route name. The best way is to add an initializer such as
ember g initializer router
(from command line), and
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
in a initializers/router.js. You can also inject into controller if you need to. Then just do simply
this.get('router.currentRouteName');
in JS, or
{{router.currentRouteName}}
in template.
This is the only way I have found to get it reliably, and observable in Ember 2.4
If you want to get current route in your component or controller you can inject routing service (routing: Ember.inject.service('-routing'))
(for more) and use:
this.get('routing.currentRouteName') or this.get('routing.currentPath')
Example with component and computed property:
import Ember from 'ember';
export default Ember.Component.extend({
routing: Ember.inject.service('-routing'),
checkMyRouteName: Ember.computed('routing.currentRouteName', function() {
return this.get('routing.currentRouteName');
})
})
Example with controller and computed property:
import Ember from 'ember';
export default Ember.Controller.extend({
routing: Ember.inject.service('-routing'),
checkMyRouteName: Ember.computed('routing.currentRouteName', function() {
return this.get('routing.currentRouteName');
})
})
Current route in your route you just need this.routeName
Example with route:
import Ember from 'ember';
export default Ember.Route.extend({
checkMyRouteName() {
return this.routeName;
}
})
Just as an update, in Ember 1.8.1, we can get the routeName inside an Ember.Route object by doing this.routeName.
Currently as of Ember 1.7.0 you can get the current route from within a route by calling this.routeName.
The Ember namespace API now has a getOwner method, which is very useful for looking up the currentRouteName, or, other route properties.
const owner = Ember.getOwner(this);
const currentRoute = owner.lookup('router:main').currentRouteName;
const routeInfo = owner.lookup(`route:${currentRoute}`).get('info');
// etc.
I've created an Ember Twiddle example to demonstrate. Use the text input above the "Output" pane to hit other routes like /blue, /green, or /red.
Ember has a RouterService since 2.15. It provides the name of the current route as currentRouteName property. A polyfill exists for Ember 2.4 - 2.14 if you are still on such an old version.
import Component from '#ember/component';
export default Component.extend({
router: service(),
isHomeRoute: computed('router.currentRouteName', function() {
return this.router.currentRouteName === 'home';
}),
});
All other solutions mentioned here are relying on private API that might already be deprecated / removed. Using RouterService is working at least up the current version, which is 3.12 at the time of writing this.
Please note that the "home" is not /. The root URL is called "index".
I had the same problem for a while. then i started exploring router. It always have a state object which can be obtained from any route using
var route = this;
var handlerInfos = route.get("router.router.state.handlerInfos");
var currRouteHandlerInfo = handlerInfos[handlerInfos.length-1];
var currRouteName = currRouteHandlerInfo.name; //"home"
that's it. Now you have the current route name!
if you want the current route params,
var routerParams = this.get("router.router.state.params");
var currRouteParams = routerParams[currRouteName]; //{ homeId : "1" }
You can simple parse the current URL. This way you can use your full url for example:
http://127.0.0.1:8000/index.html/#/home
and extract from this string the suffix:
/home
which is the current route name.
A simple JS function (that works regardless to your Ember version) will be:
function getCurrentRoute()
{
var currentRoute;
var currentUrl = window.location.href; // 'http://127.0.0.1:8000/index.html/#/home'
var indexOfHash = currentUrl.indexOf('#');
if ((indexOfHash == -1) ||
(indexOfHash == currentUrl.length - 1))
{
currentRoute = '/';
}
else
{
currentRoute = currentUrl.slice(indexOfHash + 1); // '/home'
}
return currentRoute;
}
Example of use:
if (getCurrentRoute() == '/home')
{
// ...
}