Ember has method rollbackAttribute() which is very similar to the default method rollbackAttributes(). The difference is rollbackAttribute() can be used to rollback ONLY specific model attribute.
By default this method is not available and to use it you need to enable ds-rollback-attribute and run the canary build as written here: https://docs.w3cub.com/ember/classes/ds.model/methods/#rollbackAttribute
Where can I enable the ds-rollback-attribute and how can I ran the canary build?
I fear you are looking at non-official and outdated API docs. The API docs for Ember Data are hosted here: https://api.emberjs.com/ember-data/release
The rollbackAttribute() method is not documented anymore for the latest release, which is 3.13 at the time writing this. It was last documented for 3.1. I think it was removed as a stale feature flag in this PR: [FEAT] remove all stale feature flags #5384
Actually the implementation of the rollbackAttribute() is quite simple.
We can create our own method and extract it into the service.
app/services/rollback-attribute.js
import Ember from 'ember';
export default Ember.Service.extend({
rollback(model, attribute) {
const changedAttributes = model.changedAttributes();
if (changedAttributes[attribute]) {
model.set(attribute, changedAttributes[attribute][0]);
}
}
});
After creating this service you can use it for example in the route.js
import Ember from 'ember';
import service from 'ember-service/inject';
export default Ember.Route.extend({
rollbackAttribute: service('rollback-attribute'),
_rollbackAttribute(model, attribute) {
this.get('rollbackAttribute').rollback(model, key);
},
});
Related
Standard project set up with ember cli seems to be using ES6 modules when I generate controllers/routes/models etc. with the cli. Sometimes though I want to import/export an additional function/module ie. I may want to write a function that I use in the controller in a separate file.
When I try to import the function in the standard ES6 way ember-cli seems to have a problem with handling it.
Let's say I've created controller with:
ember g route tesit
then I create a function in app/routes/testit/logger.js
const logger = function(msg) {
console.log(msg);
};
export default logger;
and import it in my controller app/routes/testit.js:
import Ember from 'ember';
import logger from './testit/logger.js'
export default Ember.Route.extend({
beforeModel() {
logger('it works');
}
});
then I get the following error:
Error: Could not find module myproject/routes/testit/logger.js imported from myproject/routes/testit
How can I resolve it?
Remove .js extension from import logger from './testit/logger.js'; line.
See Description section from MDN.
Just upgraded to 1.12, and where I had a project route that returns a single project from the store, previously I could access the properties of that model directly like {{projectName}}, but now I have to use {{model.projectName}}. can anyone shed light on what's going on?
Link-to from my projects route:
{{#link-to 'project.details' project.id title="Go to project details"}}
Model hook in project route:
model: function(params) {
var record = this.store.getById('project', params.project_id)
if(record) {
return record.reload()
} else {
return this.store.find('project', params.project_id)
}
}
Have you defined the controller for the projects route yourself?
I'm guessing that maybe you were relying on an Ember.ObjectController being generated for you, and that Ember is now generating a regular Ember.Controller for you.
If that's the case, you can revert to the old behavior by defining your own controller for the projects route. If you're using ember-cli:
// app/controllers/projects.js
import Ember from 'ember';
export default Ember.ObjectController.extend({
});
However...
ObjectController, along with the proxying behavior that you're expecting has been deprecated and is going to be removed in Ember 2.0. I'd recommend using Ember.Controller and model.property going forward.
You can read more about that at http://emberjs.com/deprecations/v1.x/#toc_objectcontroller
I try to make an addon using ember-cli. Here it is step by step what I have done so far:
sudo ember addon test-addon
cd test-addon
sudo ember serve
now the server runs and on localhost:4200 I can see the test/dummy app's application hbs.
Welcome to Ember.js
Its time to make some components for the addon:
sudo ember g component my-form
In the tests/dummy/app/templates/application.hbs I added
{{my-form}}
And now I'm getting the following js error:
Uncaught Error: Could not find module test-addon/components/my-form imported from dummy/components/my-form
edit
After struggling a little bit with npm, I tried it again (without sudo) and the same thing happened. I'm on Ember CLI 0.2.1. Here are my files, but they should be the same since they are auto-generated. The error is thrown from bower-components/loader.js/loader.js line 110.
addon/components/my-form.js
import Ember from 'ember';
import layout from '../templates/components/my-form';
export default Ember.Component.extend({
layout: layout
});
addon/templates/components/my-form.hbs
{{yield}}
app/components/my-form.js
import myForm from 'test-addon/components/my-form';
export default myForm;
It looks like (as of July 2015, anyway) that the fact templates don't work in addons is partially by design. Philosophically, I guess the justification is that the styling should be app-specific, but the JS logic can be shared. Or it's just a bug/oversight.
It turns out that if you simply remove that layout line and the import layout, it will work.
So the result looks like:
<app-name>/app/templates/includes-a-shared-component.hbs:
What follows is my shared component! {{my-shared-component}}
<addon-name>/addon/components/my-shared-component.js:
import Ember from 'ember';
export default Ember.Component.extend({
valueFromProperty:function() { // simple hello-world-style function
return 5;
}.property()
});
<app-name>/app/templates/components/my-shared-component.hbs:
Hey look I'm the app-specific style for your component <marquee>Hello world</marquee>
Here's a value from a property: {{valueFromProperty}}
My versions:
Ember: 1.13.1
node: 0.12.0
npm: 2.12.1
And here's a very different (and IMO better, but not perfect) answer than my earlier one.
<addon-name>addon/components/test-component.js:
import Ember from 'ember';
export default Ember.Component.extend({
valueFromProperty:function() {
return 5;
}.property(),
layout:Ember.HTMLBars.compile("I'm the addon's layout {{valueFromProperty}}")
});
You will need to add the template compiler to your app:
<app-name>/app/ember-cli-build.js:
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
});
app.import('bower_components/ember/ember-template-compiler.js');
... other stuff...
Note that the layout property in the addon's component completely overrides your in-app template, so per-app customizing becomes more difficult. But if you want your template customizable, you could probably use my other answer where you don't specify layout at all and just let the resolver find the template in your app.
One more approach I've found that works and is different than my other answers.
Let's say you've done ember g component my-addon-component in your addon.
This will result in you having a component at: <addon-name>/addon/components/my-addon-component.js and a template at <addon-name>/addon/templates/my-addon-component.hbs (at least with my current ember-cli).
You'll also have a tiny component stub at <addon-name>/app/components/my-addon-component.js
The fix:
1. Move the component guts from <addon-name>/addon/components to <addon-name>/app/components (replacing the stub).
2. Move the template from <addon-name>/addon/templates/components to <addon-name/app/templates/components
After 1: The import layout from ../templates/components/my-addon-component will now have a different meaning: it'll be importing from the including-app's namespace instead of the addon's namespace.
After 2: The template's import location will be in the including-app's namespace. This also seems to mean it gets compiled by the app, so you won't throw the "addon templates were detected, but there are no template compilers registered"
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.
I am brand new to Ember, and am having trouble with getting Ember/Ember Data to cache the results of an API call to the Rails backend.
I found this topic: Ember-Data .find() vs .all() - how to control cache?
Which answered a few questions about what find() vs all() does, using that I found a workaround which looks ugly and I am wondering if there is a better, more concise way than this:
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
if (this.store.all('facility').get('content.length')) {
return this.store.all('facility');
} else {
return this.store.find('facility');
}
}
});
A couple of notes:
You shouldn't cache data inside model hook. Pass cached data with #link-to or similar API.
You shouldn't cache data at all if you don't have a notification system to tell Ember a resource has been changed. Keep in mind that your app may run for a very long time.
You can also do both at same time.
Here is an example to use cached data and update them in background. (Not tested)
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
var currentData = this.store.all('facility');
this.store.find('facility'); // Ember automatically picks new changes
return currentData;
}
});