Removing an item from an observable array

I have an ASP.NET MVC application that uses durandal, knockout.js and a breeze on the client. I have a problem that I run into repeatedly, and yet I have not mentioned this anywhere. Not sure if I was in a unique situation, or if I was just not looking for the right way.

I need to know how to remove a Breeze object from an observable array so that the commit succeeds (see option A below) and the user interface reflects the change (see option B below).

I have the following models (abbreviated):

public class Donor { [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public virtual IList<Contact> Contacts { get; set; } } public class Contact { [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [ForeignKey("Donor")] public int? DonorId { get; set; } public virutal Donor Donor { get; set; } } 

I am trying to remove Contact from a Donor. I find it difficult to receive the flow directly between Breeze and Knockout, so that the element is removed from observableArray (notification), and can also be removed through Breeze.

Here is what I tried (javascript):

Option A :

 function deleteContact(contact){ viewModel.donor().contacts.remove(contact); contact.entityAspect.setDeleted(); viewModel.uow.commit(); } 

When I use this approach, I get the following error from Breeze.WebApi:

Int32Converter cannot convert from System.Int64

I looked at the stack and reviewed the source code of Breeze (although I have not yet set up a solution for passing it), and the error comes from Breeze.WebApi.EFContextProvider :: RestoreOriginal, where it restores the original properties of the object. I don’t know why he believes that my value is Int64, but I couldn’t find a good job, so I tried ...

Option B :

 function deleteContact(contact){ contact.entityAspect.setDeleted(); viewModel.uow.commit(); } 

This approach allows me to successfully save the deletion (since the item was not manually removed from the collection and therefore does not have any “initial values”). However, the problem is that setDeleted effectively removes the element from the observed array without notifying my knockout bindings that the array has changed. So the item was deleted and deleted, but my user interface still shows the item. Future attempts to call donor().contacts.remove(contact) futile because the observed array no longer has an element.

+4
source share
3 answers

Have you tried calling valueHasMutated() in your observable array after using option b?

This will notify subscribers that the observed has changed.

+2
source

If you delete or delete an entity, Breeze should automatically delete this object from any navigation collections. Therefore, I would try to eliminate

  viewModel.donor().contacts.remove(contact); 

and see if the setDeleted call really does what you need.

+1
source

TL: DR

Never set the FK in the createEntity initializer, and also redirect the object to the parent navigation property. Supports either of them, but not both.

Basic principles

After you come across a similar problem and have done a lot of research, I would like to offer an alternative answer. The problem you're experimenting with is not how you delete the item, but how to create the item.

A breeze is very useful when it comes to managing data context. He knows about navigation properties and foreign keys and how to handle them. As a side effect of the local data context, all observables also have this intelligence. This is the place where you probably missed something and ran into this problem.

Breeze Tracking Code

What does all this mean specifically? Well, there are two ways you can use to create an object with a parent using the breeze. The first is to set the parent identifier in the initializer, for example:

 var c = manager.createEntity('contact', { 'donorId': 12, 'name': 'bob' }); 

Another is to add an object to the navigation property in the parent object in the context of the breeze data.

 var parents = ko.observableArray(); manager.runQuery(..., parents); var c = manager.createEntity('contact', { 'name': 'bob'}); parents.contacts.push(c); 

Both cases have their pros and cons. The problem arises when you try to do both:

 var parents = ko.observableArray(); manager.runQuery(..., parents); var c = manager.createEntity('contact', { 'donorId': 12, 'name': 'bob' }); parents.contacts.push(c); 

Breeze tries to optimize its queries when working with inserts to prevent the user interface from flickering. When push called, it disables notifications on the target observable until the entire operation is completed. Then it will call valueHasMutated internally, which will result in a UI update. The fact is that a call to createEntity interferes with this mechanism and causes the notification to be reinitialized too soon. Then push save this invalid state, replace it and reset to it, leaving the observable in the invalid state.

When you ultimately call setDeleted , notifications will still be turned off at an observable level, preventing the user interface from updating, even if the data is correctly inserted into the breeze data context. This will happen only once after inserting a new item. Deleting an element will cause the state to change to its correct value, and all subsequent deletion in the navigation property will cause the interface to be updated.

Looking at your two options

In the end, you should use setDeleted to properly remove the object from the wind navigation property. There is no need to remove it from the manually observable, and in fact it will reset the foreign key for null , which can cause problems of deserialization on the server if the type cannot be null in your model or errors when trying to delete a row from the database depending on how your primary key is determined. Option B is the one to go with.

0
source

Source: https://habr.com/ru/post/1501564/


All Articles