Reach through several relationships to get model from ember-data - ember-data

I am setting the contents of one menu based on the selected value of another one. The first menu is a set of companies. A Company has an OrderProfile; an OrderProfile has a Warehouse. So based on a known Company, I need to reach through the OrderProfile and get a Warehouse.
Here's what happens in the console when I have the Company in a company variable:
company.get('orderProfile.warehouse.name') # Ember-data gets the OrderProfile but not the Warehouse
// => null
var op = company.get('orderProfile')
// => undefined # The OrderProfile assigned to the variable
var warehouse = op.get('warehouse')
// undefined # Now the Warehouse is assigned to the variable
warehouse.get('name')
// => "Warehouse 1" # This was null when we asked for company.orderProfile.warehouse.name
So I know I need to unravel this in several steps, waiting for each to complete. Is there an Ember idiom for doing this?
N.B. this is using:
DEBUG: Ember : 1.5.0-beta.1+canary.13995621 index.js:3496
DEBUG: Ember Data : 1.0.0-beta.7+canary index.js:3496

Here's how we worked this out. As I suspected, the key was handling the promises correctly, but we also had to be sure we had async: true set on the relevant relationships.
warehouse = company.get("orderProfile.warehouse.name") # Doesn't work
# Instead, we need to treat orderProfile as a promise, and ask for the warehouse
# once it resolves:
company.get("orderProfile").then(
(orderProfile) ->
return orderProfile.get("warehouse") if orderProfile
).then(
(warehouse) =>
# Do something with the warehouse
)
As I understand it, the relationships - specifically orderProfile: DS.belongsTo 'orderProfile', async: true in the Company model - need async: true to remind Ember-Data that it might not already have the referenced models, and it needs to fetch them. I was on the right track figuring out the promise handling, but the async: true part was the real key.

Related

Difference between update and update_attributes

In Rails 5, what is the difference between update and update_attributes methods. I'm seeing the following results for both the methods
Returns true/false
Checking for active record validation
Call backs are triggered
and also regarding update method a new thing was introduced in active record relation. I'm not able to understand it. What is the difference?
Moreover are we using update_attributes in Rails 5. It's not there in active record documentation.
I'm confused with all update methods. I need clarity
As of Rails 4.0.2, #update returns false if the update failed. Before Rails 4.0.2, #update returned the object that got updated. The main difference therefore was the return value. After this change, #update_attributes is just an alias of #update. It seems there are talks to deprecate #update_attributes in Rails 6 which is not released yet.
https://github.com/rails/rails/pull/31998
https://github.com/rails/rails/commit/5645149d3a27054450bd1130ff5715504638a5f5
From the rails 5 files it seems to me update can be used to update multiple objects(array of records) but update_attributes only work on single records otherwise both are same
From rails core files for update_attributes:
Updates a single attribute and saves the record.
This is especially useful for boolean flags on existing records. Also note that
Validation is skipped.
\Callbacks are invoked.
updated_at/updated_on column is updated if that column is available.
Updates all the attributes that are dirty in this object.
This method raises an ActiveRecord::ActiveRecordError if the
attribute is marked as readonly.
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
save(validate: false)
end
For Update
Updates an object (or multiple objects) and saves it to the database, if validations pass.
The resulting object is returned whether the object was saved successfully to the database or not.
==== Parameters
+id+ - This should be the id or an array of ids to be updated.
+attributes+ - This should be a hash of attributes or an array of hashes.
==== Examples
# Updates one record
Person.update(15, user_name: "Samuel", group: "expert")
# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)
# Updates multiple records from the result of a relation
people = Person.where(group: "expert")
people.update(group: "masters")
Note: Updating a large number of records will run an UPDATE
query for each record, which may cause a performance issue.
When running callbacks is not needed for each record update,
it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
for updating all records in a single query.
def update(id, attributes)
if id.is_a?(Array)
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
object.update(attributes[idx])
}
else
if ActiveRecord::Base === id
raise ArgumentError,
"You are passing an instance of ActiveRecord::Base to `update`. " \
"Please pass the id of the object by calling `.id`."
end
object = find(id)
object.update(attributes)
object
end
end
When we are working with update_column that time update is done on the database level there is no any contact with the rails ORM so whatever logic we have implemented like callbacks and validations all will be waste and wont be useful as this is going to be bypassed.
I found this article explained really well in just 30 seconds.
.update
Use update when you want to return false, for example in an if/else:
if record.update(params)
display_success
else
react_to_problem
end
.update!
Use update! when you want an error (for example: to avoid erroring silently, which could be very bad if an error was unexpected and you needed to know about it to fix it!):
record.update!(params) # raises is invalid
'update' respects the validation rules on model, while 'update_attributes' ignores validations.

How to add one item from observable to an array of items in a different observable?

I am struggling with using observables and pipes where I want to add one item from one observable to another observable containing a list of items of the same type.
I have type X. And of the type X I have an observable array:
readonly arrayOfx$: Observable<X[]>;
I also have an observable of only the the type X:
private readonly _x$: Observable<UpdateOfX>;
interface UpdateOfX {
x: X,
updateState: "Add" | "Modified" | "Removed"
}
All this code is in a Service Class where the service should only expose the array of X. The data in the array I want to show in my html with async piping and this part of the functionality works. The host and the client are connected with the signalR technique and onConnected, an array of items of type X is retrieved. But as the application runs, in the backend new items of type X can be created, existing items can be changed or can be removed and when this occures, only this item will be send via the signalR connection and the modification state.
In the front end, this item must be added to the already retrieved array of items of type X. In the service, the pipe technique is used and my question is, how do I add the single item I get in a later moment to the list of items I retrieved earlier?
constructor() {
this.arrayOfx$ = this._someSignalRHelperService.retrieveMultipleItems$.pipe(
tap((xArray: X[]) => console.log(xArray)),
//can I somehow get the a later created x from the server here...
);
this._x$ = this._someSignalRHelperService.retrieveOneItem$.pipe(
tap((updateOfX: UpdateOfX) => console.log(updateOfX)),
map((updateOfX: UpdateOfX) => {
//process the updateState
//... or must I do something here to get x into x[]?
})
);
}
Since SignalR is used, the backend is in control when the client receives a new item of type X when there is one created.
You can use combineLatest(), and do whatever manipulation you want as soon as your receive the two emissions:
constructor() {
this.combinedOfX$ = combineLatest(
this._someSignalRHelperService.retrieveMultipleItems$,
this._someSignalRHelperService.retrieveOneItem$
).pipe(
map(([singleOfX, multipleOfX]) => {
// do your adding or mapping and whatever here.
})
)
}
Not sure if that is by design but the problem with having the frontend (client) to deal with the data is not so ideal. Your single source of truth is now based on your client, which different machines have different processing speed and may cause nuances and inconsistent displays. Also, the code will be messy in a sense that you will now need to check through the entire arrayOfX every time a singleOfX gets updated - you need to check if it exists in the current list, if yes, edit/delete; else, append to the list. What if the user refreshes his browser accidentally? You lost all of your processing.
Since you are already using SignalR, it would be more advisable that you let the server handle all the data, and let the server be single source of truth. Then you will just need to subscribe to one hub and listen to the changes of the arrayOfX; and pretty much don't care of the single updates.

Ember-data creating extraneous record in memory

I have a many to many relationship table with payload (additional field) coming from .Net WebAPI that I have modelled in ember-data. When I add a record into this table/relationship ember is creating an additional record that is held in memory until the user performs a browser page refresh. My models are:
// student.js
export default DS.Model.extend({
name: DS.attr('string'),
studentsClasses: DS.hasMany('student-class')
})
// class.js
export default DS.Model.extend({
desc: DS.attr('string'),
studentsClasses: DS.hasMany('student-class')
})
// student-class
export default DS.Model.extend({
studentId: DS.attr('string'),
student: DS.belongsTo('student'),
class: DS.belongsTo('class'),
grade: DS.attr('number') // payload
})
Here is the code I use to create and add the many to many record.
let newRecord = this.get('store').createRecord('student-class');
newRecord.studentId = 1;
newRecord.grade = 3;
class.get('studentsClasses').pushObject(newRecord);
The new record gets created and added and everything looks good on screen, until I come back to the same page and there is an extra record in the class.studentClasses array.
Any idea why ember-data is creating an extra record in memory and how I can stop it doing it please?
Thanks
As you said, ember-data keeps records in memory. And you must keep in mind that ember-data will not remove those records by it own. It can only be removed from memory by yourself, page refresh or replaced by new payload if has same id property. You can observe that behavior by using ember debug plugin for browsers like chrome and firefox.
In your case, you've created a new record by store.createRecord(). In this moment, it had added this record to your memory already, and it was pushed to your class record. If you didn't save these models successfully, it will keep in a status called 'dirty', and if you never clean your store memory (using something like store.unloadRecord() which has some side effects, or remove this unsaved new record from your related model), the next time you use store.findRecord() to find a record, useless you force record to be reloaded like store.findRecord('class', 1, {reload: true}), it will use the existing data in your memory as first priority.
So my suggestion for this is to force reload this class model when entering this class page.

How to return a domain from Odoo server action

I am trying to create a server action via the Odoo UI that will alter the domain of another field in the view. This seems to be a pretty common use-case when dealing with the Odoo source code as you can see in the following documentation:
https://www.odoo.com/documentation/10.0/reference/orm.html#odoo.api.onchange
In those docs, they indicate that if I were in the source code of the model, I can define an onchange method and return a domain, for example, the behavior I'm trying to accomplish in the sale.order.line model would be:
#api.onchange('product_id')
def _onchange_product(self):
return {
'domain': {'route_id': [('id', 'in', x_all_route_ids.ids)]}
}
In other words, when the product of an sales order line changes, update the available options in the route_id field.
Is there any way to accomplish this same thing via a server action created through the UI? I am having trouble figuring out how to return a domain from the Python code.
The notes in the code section say:
# Available variables:
# - time, datetime, dateutil, timezone: Python libraries
# - env: Odoo Environement
# - model: Model of the record on which the action is triggered
# - record: Record on which the action is triggered if there is one, otherwise None
# - records: Records on which the action is triggered if there is one, otherwise None
# - log : log(message), function to log debug information in logging table
# - Warning: Warning Exception to use with raise
# To return an action, assign: action = {...}
I don't see how I can use this to return a domain. Does anybody have any idea?
I have tried setting the python code field to simply:
domain = {'route_id': [('id', 'in', record.x_all_route_ids)]}
But that doesn't work. The route_id list is unchanged.
I got some insight from Odoo technical support and it turns out this is possible. Anything assigned to the action variable will be treated as the return value for the server action.
So you can simply do:
action = {
'domain': {
'route_id': [('id', 'in', record.x_all_route_ids.ids)]
}
}
It is not possible. Only these special on-change methods return values will be evaluated correctly.

RoR: how to tell which params in a form have changed

In a Rails application, I have a particular form with many fields for editing a resource. Since I also want to log what was changed for this particular resource, I need to know which params changed.
Currently in this form, I have duplicated every field in the form with hidden field tags, so in the controller every field is compared to the corresponding hidden field to determine if the value was changed. But it's a LOT of work in the view and in the controller.
Being relatively new to Rails, I'm finding all kinds of Rails "magic" as I go along, so I wonder: does the framework provide a way to do this for me? Or is this pretty much the only way?
ActiveModel has exactly what you're looking for, take a look at the examples in the docs...
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
person = Person.find_by_name('Uncle Bob')
person.changed? # => false
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_was # => 'Uncle Bob'
person.name_change # => ['Uncle Bob', 'Bob']
person.name = 'Bill'
person.name_change # => ['Uncle Bob', 'Bill']
You can try Dirty attributes for this task.
#post.attributes = params[:post]
#post.changed # or changes if you want to see what values on which was changed
More on that you can find by this link: http://ar.rubyonrails.org/classes/ActiveRecord/Dirty.html
Also if you want to use versioning or auditing, try this gems (best ones are: paper_trail, acts_as_audited, vestal_versions):
https://www.ruby-toolbox.com/categories/Active_Record_Versioning
https://www.ruby-toolbox.com/categories/Active_Record_User_Stamping