Getting a weird persistance issue with ember data fragments and localstorage - ember.js

Apologies if this isn't quite the right place (as opposed to either libraries own github issue page, but as I've not been able to determine exactly which library is not quite working correctly hard to log it specifically).
I'm using ember data fragments on my model (an array), and localstorage to save down my model. When calling rollback upon the saved model, it seems to reset the fragments back to their original state (i.e. no values), but it still maintains the fragment itself on the array, rather than dropping the item out of the array.
I've got a fiddle setup, click 'add' to add a model, click to view it's details, then click 'add' in there, followed by 'cancel'. You can see that the type + desc values drop out, but the element is still there.
If I switch out to using the Fixture adapter then it all works as expected, just not sure where to start even attempting to debug, I've stepped through many lines of _super calls, and what not trying to figure it out, but just get lost.
Note
This is a pseudo version of my actual app, and curiously enough when you navigate to the home page and then back to the details page, it seems to resolve the type/desc correctly, which it is not doing on my actual app, it still maintains the default values. However refreshing the page makes it work perfectly from then onwards.
Any help greatly appreciated!
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="//builds.emberjs.com/tags/v1.7.0/ember.js"></script>
<script src="//builds.emberjs.com/canary/ember-data.js"></script>
<script src="//raw.githubusercontent.com/lytics/ember-data.model-fragments/master/dist/ember-data.model-fragments.js"></script>
<script src="//raw.githubusercontent.com/kurko/ember-localstorage-adapter/master/localstorage_adapter.js"></script>
<script>
window.App = Ember.Application.create();
App.ApplicationStore = DS.Store.extend();
App.ApplicationSerializer = DS.LSSerializer.extend();
App.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'cars'
});
App.Car = DS.Model.extend({
make: DS.attr(),
model: DS.attr(),
features: DS.hasManyFragments('feature')
});
App.Feature = DS.ModelFragment.extend({
type: DS.attr(),
description: DS.attr()
});
App.Router.map(function () {
this.route('index', { path: '/' });
this.route('car', { path: '/car/:car_id'});
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('car');
},
actions : {
add: function(model) {
var car = this.store.createRecord('car', {
make: 'Dodge',
model: 'Viper',
features: []
});
car.save();
}
}
});
App.CarRoute = Ember.Route.extend({
actions: {
add: function(model) {
model.get('features').createFragment({
type: 'Something',
description: 'Some desc'
});
model.save(); //*/
},
cancel: function(model) {
model.rollback();
}
}
});
</script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
{{#link-to 'index'}}Home{{/link-to}}
<ol>{{#each}}
<li>{{#link-to 'car' this}}{{name}} {{model}}{{/link-to}}</li>
{{else}}
<button {{action 'add' model}}>Add</button>
{{/each}}</ol>
</script>
<script type="text/x-handlebars" data-template-name="car">
{{#link-to 'index'}}Home{{/link-to}}
<dl>
<dt>Make</dt>
<dd>{{make}}
<dt>Model</dt>
<dd>{{model.model}}</dd>{{#each features}}
<dt>{{_view.contentIndex}}. {{type}}</dt>
<dd>{{description}}</dd>
{{/each}}
</dl>
<button {{action 'add' model}}>Add</button>
<button {{action 'cancel' model}}>Cancel</button>
</script>
</body>
</html>

I havent worked with data fragments but fragment is a model itself so the element/fragment is still there because you have created a record for it.
This record is stored in the ember store until you do something with it.
Rollback, via emberjs.com,does this - "If the model isDirty this function will discard any unsaved changes".
The model in this case seems to be the fragment. Rollback gets rid of the changes, which is what it is doing in your case, removing the type and desc values, but the record itself is still in the store.
If you want to get rid of the fragment altogether you would have to delete it. http://emberjs.com/guides/models/creating-and-deleting-records/

Related

Ember transitionTo removed but still firing

This one has me baffled. I am still new to ember but I had some nested routes where I was calling transitionTo with the redirect hook on the parent route to pull in a nested child route.
App.CategoryRoute = Ember.Route.extend({
redirect: { transitionTo('subcategories'); }
});
I since removed the redirect hook and the transitionTo however the transition is still occurring. I even have closed my project and reopened, cleared the cache on all browsers but it still is holding onto the transition. I have also tried calling a bogus route under the redirect and it errors out. As soon as I remove, it goes back to this behavior and transitions into the child route. Has anyone ever had this happen? What am I doing wrong? Please let me know if you need more code.
Here is the rest of the code. in my troubleshooting i have removed and added so much stuff I will try to get it back to what it was when it started happening. I also removed as much 'fluff' as I could from the handlbars code sections to make it clearer.
App = Ember.Application.create({
rootElement: "#app",
LOG_TRANSITIONS: true
});
App.Router.map(function () {
this.resource("category", function () {
this.resource('subcategories', { path: "/" }, function () {
//subcategory route show list of items in that subcategory
this.resource('subcategory', { path: "/:subcategoryslug" }, function () {
//this is individual item
this.route("item", { path: "/:itemid" });
});
});
});
});
App.CategoryRoute = Ember.Route.extend({
redirect: { transitionTo('subcategories'); }
});
App.CategorySubcategoriesRoute = Ember.Route.extend({
model: function (params) {
Ember.Logger.warn("Inside category route");
categoryid = 47
//this calls extended object for json data
//works just fine when routes are rendered.
return App.Subcategory.findAll(categoryid);
}
});
Here is the handlebars blocks:
<script type="text/x-handlebars" data-template-name="index">
<h2>APPLICATION TITLE</h2>
<div>
<strong>{{#link-to 'category'}}CategoryName{{/link-to}}</strong>
</div>
</script>
<script type="text/x-handlebars" data-template-name="category/index">
<h2>Category Name</h2>
<div>
{{outlet "subcategories"}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="category/subcategories">
{{#each subcategory in model}}
<div>
<strong>{{#link-to 'category.subcategory.index' subcategory}}{{subcategory.subcategoryname}}{{/link-to}}</strong><br>
{{subcategory.description}}
</div>
{{/each}}
</script>

Periodically update data state

I'm creating a browser game that doesn't connect to a server. It stores all the state in Ember Data using the fixture adapter. One part of the game is a user a can click a button and "go on a mission". The mission has a duration. I would like to update the state of the game when the mission duration has elapsed.
I was going about this by periodically updating the mission controller and checking the time until the mission was over and then updating the state with the results. However I noticed that when I navigated to a different route it would destroy the controller. With the controller destroyed I had nothing to update the mission periodically. So even though the duration elapsed it didn't update the state of the game.
I don't understand why the controller is being destroyed? This may be because the controller is an item-controller? This is my first ember app so I may have set up something incorrectly.
Also if there is a better way to go about this I'd love to hear it.
Yes, itemControllers are destroyed, when the route is left:
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
this.route('other');
});
App.Router.reopen({
location: 'none'
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
App.IndexController = Ember.ArrayController.extend({
_setup: function () {
alert('_setup IndexController')
}.on('init'),
itemController: 'item'
});
App.OtherController = Ember.Controller.extend({
_setup: function () {
alert('_setup OtherController')
}.on('init')
});
App.ItemController = Ember.ObjectController.extend({
_setup: function () {
alert('_setup ItemController')
}.on('init'),
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
<script src="http://builds.emberjs.com/tags/v1.7.0/ember.js"></script>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li>{{item}}</li>
{{/each}}
</ul>
{{#link-to 'other'}}Other{{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="other">
{{#link-to 'index'}}Back{{/link-to}}
</script>
In this case, I'd say create a service object attached to your application controller, that is scheduling updates with Ember.run.later().
Also, please note that the FixtureAdapter is not able to actually store data.

Accessing a single record from a simple model

I'm starting simple, trying to display a single value from a simple model.
This answer to "accessing the model from the template" suggests that it's necessary to extend ObjectController. At this point, there's have no application logic, so it doesn't seem like a controller (or a view) is really needed yet.
Likewise, there are no routes yet, so it doesn't seem like anything should be needed beyond App.IndexRoute.
The single object in the dictionary fixture has a title property with the value Hello Ember. I'm expecting to see that text displayed between two hard-coded arrows. Instead, all I get is the arrows.
The Index.html is:
<!DOCTYPE html>
<html>
<head>
<title>Dictionary</title>
</head>
<body>
<!-- Main body of the application -->
<script type="text/x-handlebars">
<p>Title: -->{{title}}<--</p>
</script>
<!-- ... Ember.js and other JavaScript dependencies ... -->
<script src="js/libs/jquery-1.10.2.min.js"></script>
<script src="js/libs/handlebars-1.0.0.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/app/application.js"></script>
<script src="js/routers/router.js"></script>
<script src="js/models/dictionary_model.js"></script>
<script src="js/controllers/dictionary_controller.js"></script>
</body>
</html>
And then the JavaScript:
// application.js
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter.extend();
// router.js
App.Router.map(function() {
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('dictionary', 0);
}
});
// dictionary_model.js
App.Dictionary = DS.Model.extend({
title: DS.attr("string")
});
App.Dictionary.FIXTURES = [
{
id: 0,
title: "Hello Ember"
}];
// dictionary_controller.js
App.DictionaryController = Ember.ObjectController.extend({
});
I'm not sure where you're reading in the documentation that's contradicting, please update your question with the contradicting statements so they can be fixed.
The controller really only need be defined if you need to add additional computed properties, actions, or other methods. In your case you are correct in that it needn't be defined.
That being said, the application template (or unnamed template as in your case) is the root of your ember app. Any child routes/resources will be rendered in the {{outlet}} located in the application template(examples below).
The index route is a route underneath the application route. Resources are considered routes that can have children and generally associated with a model.
All this comes up to the main problem you're seeing. You've returned your model from the index route, but you are attempting to use it in the application route's template.
Here's a simplified version of your code
Code
App = Ember.Application.create();
App.ApplicationAdapter= DS.FixtureAdapter;
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('dictionary', 0);
}
});
App.Dictionary = DS.Model.extend({
title: DS.attr("string")
});
App.Dictionary.FIXTURES = [
{
id: 0,
title: "Hello Ember"
}];
Templates
<script type="text/x-handlebars">
<h2>Application Template</h2>
Here we Are in the Application Template
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Index Template</h2>
{{title}}
</script>
Example in action
http://emberjs.jsbin.com/OxIDiVU/443/edit

Ember.js: ArrayController undefined in template

Problem:
I am kind of struggling with the organization of my first ember app. The current issue is that the my Items ArrayController is not defined in my dashboard template:
<script type="text/x-handlebars" data-template-name="dashboard">
{{#if controllers.items}}
<p class="alert alert-error">Dashboard can access item's info - Nice!</p>
{{else}}
<p class="alert alert-error">Dashboard cannot access items... :-/</p>
{{/if}}
</script>
Likely cause: *
**EDIT: after talking with #conrad below, I'm kind of questioning this:*
I had a similar issue in an earlier post and kingpin2k suggested the cause was that I:
"never created anything that uses the options controller".
This is probably the case here as well. This quick screencast shows that a breakpoint on my ArrayController is not hit on page load - but it is hit when I inspect the Items controller in the Ember inspector tool (eg, Ember creates the ArrayController object right then for the first time).
Apparent non-solutions:
My Dashboard controller says it needs the Items controller. I guess that isn't enough to instantiate the ArrayController?
App.ItemsController = Ember.ArrayController.extend({
len: function(){
return this.get('length');
}.property('length'),
totalCost: function() {
return this.reduce( function(prevCost, item){
return parseInt(item.get('values').findBy('type', 'cost').price, 10) + prevCost;
}, 0);
}.property('#each.values')
[more computed properties...]
});
App.DashboardController = Em.Controller.extend({
needs: ['items'],
itemsLength: Ember.computed.alias('controllers.items.len'),
itemsTotalCost: Ember.computed.alias('controllers.items.totalCost'),
[more computed properties...]
});
Furthermore, each item in Items is being rendered in my items template. I guess that does not create the missing controllers.items either...
<script type="text/x-handlebars" data-template-name="items">
{{#each}}
[these render fine]
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="display">
<!-- DISPLAY TEMPLATE -->
{{!- DASHBOARD -}}
{{render dashboard}}
{{!- ITEMS -}}
{{render 'items' items}}
</script>
So then.. what?
I can imagine many possible avenues, but haven't gotten any of them to work yet:
Specify the Items ArrayController in {{render dashboard}}?
Some configuration in a Route?
Maybe my templates/routes are not correctly arranged?
You could make sure that the ItemController is instantiated in the dashboard template by calling it in the DashboardController's init function:
App.DashboardController = Em.Controller.extend({
needs: ['items'],
init: function() {
this._super();
this.get('controllers.items.length');
}
});
/edit:
removed the part that was not helpful

Using the new Ember RouterV2, how do you immediately redirect to another state from the index state?

What I have so far:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function(match){
match('/').to('application');
match('/edit').to('edit');
});
App.ApplicationRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('edit');
},
events: {
startEdit: function( context ){
this.transitionTo( 'edit' );
}
}
})
App.EditRoute = Ember.Route.extend({
init: function(){
this._super()
console.log('EditRoute')
},
});
Handlebars:
<script type="text/x-handlebars" data-template-name = 'application'>
Hello World
{{ outlet main }}
</script>
<script type="text/x-handlebars" data-template-name = 'edit'>
<div class = 'edit-background'> Edit State: {{ title }} </div>
</script>
I have four questions:
When I open the application it just remains in the home page, is the redirectTo hook suppose to immediately redirect you to another state?
In addition, I have this events hash in AplicationRoute per suggestion from here: How to programmatically transition between routes using Ember.js' new Router. but I read through the answers and still am not sure how you are supposed to use it.
How do I test the router on the console? before you could navigate between the states by calling transitionTo commands, what do I do now?
For some odd reason, my application template seem to rendered twice, as in there are two 'Hello World' up there, and when try to add something like: <li>{{#linkTo edit}}edit{{/linkTo}}</li>
I get this error:
'Uncaught TypeError: Cannot read property 'container' of undefined -- ember.js:2223'
This is how you would initially load the editView/route/template on application start up:
Router
App.Router.map(function(match){
match('/').to('application',function(match){
match('/').to('edit')
})
})
ApplicationTemplate
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
EditTemplate
<script type="text/x-handlebars" data-template-name="edit">
I am embedded!
</script>
EditRoute
EditRoute = Ember.Route.extend({
renderTemplates:function () {
this.render('edit', {
into:'application'
});
})