Update/Remove items in Ember Array - ember.js

I'm doing prototype small app on emberjs for my project.
It's here - jsbin
I have a list of transactions, which are displayed to the user. User may update or delete some of it.
For example, after update - transaction general status must be change on "Done" if both user status is "Done" (see properties of App.Transaction model).
After "Remove" user action is simply remove from array :)
How it is correctly implemented in ember.js methodology with Ember Arrays?
P.S. Pay no attention to that transaction list is static, in the future I would use ajax-request on load of app for fill transaction list. Currently, statics is made for simplicity.
Thanks.

Here is your working jsbin.
I've changed a few things, instead of passing the id when updating a transaction we pass now the transaction itself, so you can call setProperties on it and set the general_status to Done. I've also changed this behavior when deleting a transaction. And when adding a transaction you where using always the same id which is not optimal, I've used Ember.uuid to get always on creation a new id for your new record.
Have a look at the changed code to see the changes.
As a side note I should mention that you overall approach is not quite following ember's conventions, but ember is flexible enough to make it work anyway :)
Let me know if this is what you where looking for.

Related

Ember: Edit model object without setting isdirty

This topic har been discussed on stackoverflow before, but not with latest version of ember data, I think. At least none of the suggestions I have found have worked for me.
I use the latest version of Ember and Ember data (versjon 2.13.0). I use the JsonApiAdapter.
Scenario
After I load a record from the server I want to do a few changes to some of its properties. These changes shall not make the record dirty, and the changed attributes shall not show up in the record.changedAttributes(). Any changes the user may do after that shall make the record dirty.
Searching for a solution
I have tried to manually change the isDirty flag, but it didn't do it. I have also tried to find the place in the ember data code that sets the state after a record has been loaded (because essentially I am trying to do the same thing) but I haven't found where it is.
I have also tried record.send('pushedData'), but I didn't change anything of the state of the record.
Any help appreciated.
I know 3 methods which allow to modify server's response without dirtying records:
You can override adapter's handleResponse method and make modifications right in payload.
You can override serializer's normalize method.
You can load records from server with Ember.$.ajax method, modify response and then pass it to store's pushPayload method.
First two methods are good if you need to modify record after every load from server (no matter from what route/controller you do it). Both adapter and serializer can be model-specific. If you need to do it in only one place (controller or route), or if you need an access to route's/controller's variables - 3rd method is good.
I'm not aware about any way to mark record as dirty/not dirty. If you modify a record after it was stored, it becomes dirty.

How to not lose hasMany changes on Ember query?

In my application, I search for documents with query.
Then I edit one attribute in a single document, and then I call search query again.
Result is OK, I see document still in a dirty state with changed attribute.
Then I again pick one of documents and edit its hasMany relation (from 2 items to 4 items). And then I call search query again.
Result is NOT OK, hasMany relation change is lost/disposed/rollbacked.
Is there a way so Ember query (i guess it's some Ember internal reload) does not rollback not saved relation changes ?
I am using Ember 2.9.1
For now i have no other way than prohibit any filter query actions or route actions anything that could call query again, since that would cause lost data that user set.
Ember's store.query method always refetches the models from the backend (unlike the find* methods). With a query, it's the only way to make sure you have the most up-to-date data (and that the models loaded into the store are still valid). Even in your instance, you may run into unexpected results if you change the data such that it no longer meets your query criteria.
If you would like to keep edits around between queries, I would recommend making a copy of all models which are dirty (check the hasDirtyAttributes attribute). You can gather them with peekAll. Once copied, you can then make the query and patch the records by ID. Perhaps by using Ember.assign.
Even using that method, I would still think that you will get into trouble tracking the changes and making sure the records stay consistent with your query. Like what do you if that record wasn't returned (deleted on the server or no longer meets your criteria)? Do you drop your edits? What if you have a conflict between the data from the server and your local version (e.g. another user patched the hasMany relationship that the other user is now querying and modifying)?
ember-changeset seems like it could be useful here. However, it's not obvious how to get a ChangeSet to apply to a new instance of the same model. Though it may be possible to keep a snapshot and match them up manually. However, you still run into data consistency issues.

Where to store transient UI state in Ember 2.0

Ember 2.0 has gone great lengths towards making everything a component. With routable components coming soon, controllers will probably phased out as well.
Context
However, there is a recurring problem I face when building User interface, which I don't have a satisfying pattern for so far: user interface state.
What am I taking about?
Selection state
Current focus
Folded/unfolded state in some tree display
Basically, any state that is not part of the actual data, yet has to be tracked on an object-by-object basis. In the past, this used to be done with Controllers, acting as proxies to models. This approach is now obsolete. The new approach is Components everywhere, for the better. Component does the bookkeeping, tracks transient state and you get actions back.
A typical pattern I have, though, is with shared state, such as a list with selectable items.
The issue
Building a list component, with the following requirements:
Each item can be selected.
Selection state changes the DOM (some class bindings).
User can draw a rectangle in the list component to select several items at once.
Ideally, the whole behavior can be abstracted out as a mixin.
Question: where does the selection flag live?
Attempts
1) Everything is a component.
I make each item a sub-component, say {{my-list-item}}. The component tracks the selection state. Problem: how can the list component update the selection state?
2) Move state out of sub-components.
Put it on the list component. Within a separate state array alongside the item list. Pros: the list has all the state it needs. Cons: it's a nightmare to keep it synced when items are added or removed from the list. And it means the sub-component have no access to the state. Or maybe I could pass it to them as a bound value?
3) Reintroducing proxies.
Coming to think of it, there is a way to share some state: put it on the models. Well, not on the actual models so as not to pollute them with local state, but by setting up an ArrayProxy that will return some ObjectProxy for every item, to hold the state.
Pros: this is the only solution I managed to implement completely. Cons: encapsulation and decapsulation of items is a hassle. Also, after a few layers of being passed around, get and set have to go through 4 ou 5 proxies, which I fear will be a problem with performance.
Also, it does not work well for mixins. Should I want to abstract out some HasSelection mixin, and a HasFoldableItems mixin, and a Sortable mixin, they all need some state.
Back to the drawing board
Is there some better pattern I have not found?
I found the following relevant questions, but that led me nowhere:
Ember.js - where should interface state be stored? (2012, suggests something close to my #3 above)
Road to Ember 2.0 - High level Ember app structure feedback? (some of the key questions in the list are relevant to this one)
Great question - I actually went to one of the core ember team members to find out and the answer currently is services. Because the component is best left stateless (for the most part) you can leverage a service to persist this state that doesn't fit into a server persisted model.
Here is a great example that Stef Penner put together showing how you might save a "email draft" in your ember app (that isn't persisted backend)
https://github.com/stefanpenner/ember-state-services
Example component for reference (from the github project above)
export default Ember.Component.extend({
tagName: 'form',
editEmailService: Ember.inject.service('email-edit'),
state: Ember.computed('email', function() {
return this.editEmailService.stateFor(this.get('email'));
}).readOnly(),
actions: {
save() {
this.get('state').applyChanges();
this.sendAction('on-save', this.get('email'));
},
cancel() {
this.get('state').discardChanges();
this.sendAction('on-cancel', this.get('email'));
}
}
});

What can you do with Ember Data Models when in the error state?

I'm struggling to understand the workflow that would be used in the following scenario:
A user creates a model, let's call it Product. We present them with a form to fill in. The save errors for some reason other than validations (timeout, access denied etc...) In Ember, this puts the model into an error state. From a UI perspective, all I want to do is put a message on the screen (easy) and allow the user to try again (apparently not so easy).
I've seen it written many times not to reuse a transaction. I understand the logic of that. In the case of a new Product, I simple create another new Product, merge in the data from the original product (attributes, relationships) and replace the content of my controller with the new Product. This wasn't hard and appears to work nicely, although there may be (hopefully) a better way.
However, when I'm editing a Product, I have run into a serious issue and the above solution does not work. The Product model is now in the error state and I can not find any way to get a copy of this Product that isn't also in the same state.
What I cant' figure out is what I can do with this model once it hits the error state. I have tried the following:
Rollback: This doesn't work. You can't rollback a transaction in the error state.
Reload: Same as above. Not allowed to reload a record in the error state.
Grab a new copy of the record: So I try App.Product.find(id) with the same id as the existing record. It just gives me a copy of the existing record, in the error state.
I'm hoping I'm missing something fairly basic here. Is it possible to roll a record nicely out of an error state (or invalid state for that matter)?
If there is a simple way to change the state of these models, should we still be creating a new transaction for further attempts to commit?
So after a few days of reading source and experimenting, I have come to the conclusion that this is functionality that is not yet implemented. To move a record into another state you are supposed to send an event to it which passes it on the statemanager. There appears to be no events registered on the error state that allows us to recover the record.
There is an ugly workaround - I can call transitionTo on the statemanager of the record and force it into the state we want. I did not decide to do this lightly, but at this point I must continue on with the project while I wait for ember-data to evolve. So if the record is so far unsaved, we can rescue it from an invalid or error state by calling:
model.get('stateManager').transitionTo('loaded.created.uncommitted')
or for an existing record:
model.get('stateManager').transitionTo('loaded.updated')
Once this has been called, you can attempt to call commit again on the transaction that the model resides in. This will be the default transaction as the behaviour of ember-data is to move a model into the default transaction once commit has been called on it's original transaction. You can always retrieve the current transaction for a model by calling model.get('transaction')
So at the end of this, I have a way to create the typical CRUD scenario that we might see in Ruby on Rails, but I don't believe this is the ideal way to do it. I do believe however that at this point in time, neither does the ember-data team.
For those of you interested in having CRUD functionality as controller and route mixins for Ember, I have a Gist that contains the code I cam currently using. This is working fine, recovers from save errors as well as validation errors. Hopefully I can continue to refine this as ember-data evolves.
With the addition of DS.Errors in 1.0.0-beta5 (see https://github.com/emberjs/data/commit/994f3b234ef899b79138ddece60d8b214c5449e3) you should be able to call...
record.get("errors").clear();
This will clear out the previous errors and triggers becameValid.
You can trigger a becameValid event on it:
record.send("becameValid");
This should transition the model to uncommitted state.
You could try creating a parallel representation of the model as an Ember.Object that is not persisted but has the same properties as your persisted model. If your ajax bounces back in an error state you can use the error callback provided by the ajax method to do something.
In this case, the "something" might be to delete the record, and then clone the properties from your dummy object into a new record and re-save the record. On a success callback simply destroy your temp object and if all records are clean then clear your temp objects (to prevent lingering temp objects).
This could also be insane... but it strikes me as an option.

Backbone, selectively render parts in template

I am listing products as table rows, each row contains input fields for specifying the quantity of products.
I made a Fiddle for it here, http://jsfiddle.net/kroofy/4jTa8/19/
As you can see, after the input in the Qty field have been made, the whole row render again. And because of that the focus of the input field will be lost, which is not good if you want to input more than just one digit, or tab between input fields.
What would be the most elegant way to solve this?
I would handle this by setting model.set({qty: _qty}, {silent: true}) and then updating the non-input fields with this.$.
As an alternative to the silent treatment: rather than listening for change events, listen for change:qty and change:sellPrice and have a method that updates just the HTML that needs updating within this.$, rather than re-rendering the DOM object and breaking your focus.
Either way, your comment about "selective updating" on the fiddle is certainly the right way to go.
(this.$ is a backbone hack to jQuery that restricts all selectors to search only within the DOM of the View's element. The element doesn't even need an ID or class; it just needs to exist and the View maintains a handle to it. It's incredibly useful.)
i built a plugin for backbone called Backbone.ModelBinding that may be able to help in this situation. my plugin allows you to update portions of a view with data from a model, when the model changes.
i've forked / updated your fiddle to show it in action: http://jsfiddle.net/derickbailey/FEcyF/6/
i removed the binding to the model change. i've also added id attributes to the inputs of the form to facilitate the plugin (the attribute that the plugin uses is configurable, though). and lastly, i've added a data-bind attribute to the sell price total td.
you can get the plugin here: http://github.com/derickbailey/backbone.modelbinding/
hope that helps
FWIW: my plugin is an automated version of what Elf is suggesting. I've written code exactly like he is describing, numerous times, which is where the plugin came from. I just got tired of writing that code by hand :)