I'd like to reopen Ember or Ember Data framework classes. Using Ember CLI, where is the right place to put these so that they get initialized property? Here's an example of something I'd like to do:
import DS from 'ember-data';
DS.Model.reopen({
rollback: function() {
this._super();
// do some additional stuff
}
});
I think the best way to execute modules that have side effects would be to create an initializer. Something like this:
// app/initializers/modify-model.js
import DS from 'ember-data';
let alreadyRun = false;
export default {
name: 'modify-model',
initialize() {
if (alreadyRun) {
return;
} else {
alreadyRun = true;
}
DS.Model.reopen({
// ...
});
}
};
Initializers are automatically run by Ember-CLI, so there's no need to call them yourself.
EDIT: As Karim Baaba pointed out, it's possible for initializers to run more than once. For an easy way around that, I've included an alreadyRun flag.
Using an initializers is sufficient but isn't a good practice for writing tests as they're ran multiple times.
Here is an example of how to reopen the text field view to clear the input when focusIn is triggered
app/overrides/textfield.js:
import Ember from 'ember';
export default Ember.TextField.reopen({
focusIn: function(evt) {
this._super(evt);
this.set('value', '');
}
});
app/app.js
import './overrides/textfield';
The pattern is very simple and can easily be used for DS.Model
Export your content as an ES6 module:
import DS from 'ember-data';
export default DS.Model.reopen({
rollback: function() {
this._super();
// do some additional stuff
}
});
Put the file with your reopen content somewhere like app/custom/model.js, then import the file in app/app.js like this:
import SuperModel from './custom/model';
Now all your models have the custom code.
Related
In my Ember app, I have a JSON file filled with data in my public directory (I.E. public/data/articles.json.
I have an article route where I would like to load in this data and display it in the front-end. How could I do this successfully? I am willing to use the model hook, however I don't know of a solution off-hand.
I currently have the following code in my controller. However, this doesn't work (most likely because the data is loaded in after it is rendered).
import Controller from '#ember/controller';
import $ from 'jquery';
export default Controller.extend({
init() {
this._super(...arguments);
$.getJSON("/data/articles.json", function (data) {
console.log("test");
console.log(data);
this.articleData = data;
}
})
You can literally just write return $.getJSON("/data/articles.json"); in your routes model hook and then access the data as model in your template/controller.
A bit more elegant would be to use fetch:
async model() {
const res = await fetch('/data/articles.json');
return res.json();
}
Are you accessing that file somewhere else? I would recommend putting the file inside your app folder and importing it. It could be inside app/utils/sample-articles.js or another folder for "static data". That way you don't have to do that extra request.
// app/utils/sample-articles.js
export default {
// Articles
};
Then in your controller you can just import it like:
import Controller from '#ember/controller';
import sampleArticles from 'your-app/utils/sample-articles';
export default Controller.extend({
articleData: sampleArticles
});
Also in your example, you're assigning the value of data to a variable in an inner scope. You would need to refactor that like:
import Controller from '#ember/controller';
import { set } from '#ember/object';
import $ from 'jquery';
export default Controller.extend({
init() {
this._super(...arguments);
$.getJSON("/data/articles.json", (data) => {
console.log("test");
console.log(data);
set(this, "articleData", data); // Use `set`
}
});
I see that Ember.js includes files using 'import name from "module-name"' syntax. For example, in app.js:
import Ember from 'ember';
import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers';
import config from './config/environment';
I want to include my JS files using this method. But I don't know how to do that. Where should I put my JS files? Or should I do something else?
Here is an example.
My component:
//file app/components/small-logo.js
import Ember from 'ember';
import Logo from 'library/logo';
export default Ember.Component.extend({
mouseEnter: function() {
var logo = new Logo();
logo.changeColor();
}
});
Logo - is a big class, that has many functions. On mouseEnter event I want change the logo's color. Also, I want to start animation when this component shows, but I don't know how to execute a function at this time, it will be another one question.
Here is my component's template:
//file app/templates/components/small-logo.hbs
{{#link-to 'index'}}
<img alt="Logo" src="img/pixel.gif" class="logo">
{{/link-to}}
Here is my js file with class Logo. I don't know how to include it:
//file app/library/logo.js
var Logo = function() {
...
}
Logo.prototype.changeColor = function() {
...
}
In order to be able to import a function/module you must export it in the file, adding an export default Logo; will make it available for import though.
I also recommend using absolute paths to reference it, in your case applicationName/library/logo.
Another more ember-ish way to do what you are trying to accomplish would be to move the logic into the component.
export default Ember.Component.extend({
mouseEnter: function() {
this.changeColor();
},
changeColor: function() {
// your color change logic, you can access the element via this.$()
}
});
And while we're at it, since you are using the ember-cli you can use ES6 syntax:
const { Component } = Ember;
export default Ember.Component.extend({
mouseEnter() {
this.changeColor();
},
changeColor() {
// your color change logic, you can access the element via this.$()
}
});
I'm build a list-view, which renders a list of records in a table. The list-view is build as a reusable mixin, and has a reusable template as well. I want the list-view to be as easy to use as possible, and not have to write too much code, to make it work - but only overwrite what I want to change.
Idealy I only want to tell the controller (or perhaps even better) the router, that it's going to render a list-view, and only render custom template, if I have one defined.
Example:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Route.extend(MixinList, {
model: function() {
return this.store.find('category');
}
});
Currently I have to write this code, to make the list-view work:
Categories route:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('category');
}
});
Categories controller:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Controller.extend(MixinList, {
actions: {
itemAction: function(actionName, item) {
if (actionName === 'edit') {
this.transitionToRoute('categories.edit', item.get('id'));
}
}
}
});
Categories template:
<h1>Categories</h1>
{{partial 'mixin-list'}}
Is it possible to setup conventions, so routes which are using a specific mixin, are given a default controller and template, if they arent added to the project by the user?
After some further research (and some fresh eyes), I found the solution:
import Ember from "ember";
export default Ember.Mixin.create({
renderTemplate: function() {
var currentController = this.container.lookup('controller:' + this.routeName);
if (currentController.isGenerated) {
currentController.reopen(MixinList);
this.render('mixin-list-view');
}
else {
this.render();
}
}
});
That allows me to only define the route, and include the mixin, and let that mixin do the magic:
import Ember from 'ember';
import MixinList from '../../mixins/mixin-list';
export default Ember.Route.extend(MixinList, {
model: function() {
return this.store.find('category');
}
});
The important part here, is the renderTemplate method, and the lookup to the currentController. The currentController exposes a property, that tells if it's autogenerated (not explicitly created by the user). In that case, we can overwrite the rendered template, and even add functionallity to the controller - for example by adding a mixin to the controller (.reopen(...)).
I have an ember application with a controller header.js and a template header.hbs.
Now I have some javascript I need to execute at document $( document ).ready()
I saw on Ember Views there is didInsertElement but how do I do this from the controller?
// controllers/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
});
// views/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
});
// templates/header.js
test
I read several times it's not good practice to be using Ember Views?
the controller is not inserted (the view is) hence there is no didInsertElement.
If you need something to run once, you can write something like this:
import Ember from 'ember';
export default Ember.Controller.extend({
someName: function () { // <--- this is just some random name
// do your stuff here
}.on('init') // <---- this is the important part
});
A more actual answer (Ember 2.18+) while honouring the same principle as mentioned in user amenthes' answer: it's no longer advised to use Ember's function prototype extensions. Instead you can override the init() method of a controller directly. Do note that you'll have to call this._super(...arguments) to ensure all Ember-related code runs:
import Controller from '#ember/controller';
export default Controller.extend({
init() {
this._super(...arguments); // Don't forget this!
// Write your code here.
}
});
I have imported moment.js into my project and it seems to work just fine in my controllers but for some reason it is not working in my routes.
Controller:
// controllers/users.js
import Ember from 'ember';
export default Ember.Controller.extend({
date: function() {
alert(moment().format('X'));
}.property()
...
});
Route:
// routes/users.js
// (Error: /routes/users.js: line 5, col 29, 'moment' is not defined.
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var data = { start: moment().startOf('month').startOf('day').format('X') };
return this.store.find('event', data);
}
});
Brocfile:
var app = new EmberApp();
app.import('vendor/moment/moment.js');
I guess this is a JsHint Error. You may want to add the following comment to your Route code.
/* global moment:true */
import Ember from "ember";
....
Also (from ember-cli documentation):
If you want to use external libraries that write to a global namespace (e.g. moment.js), you need to add those to the predef section of your project’s .jshintrc file and set its value to true. If you use the lib in tests, need to add it to your tests/.jshintrc file, too.
Then you don't have to do it to every file your using moment.js in.