I have a bulk insert screen which allows the user to insert products line by line.. Each product has it's own Units of measurement.
Here is my save changes Code:
save = function (product) {
var entitiesToSave = product.units().slice();
entitiesToSave.push(product);
var so = new breeze.SaveOptions({ allowConcurrentSaves: true })
return manager.saveChanges([entitiesToSave],so)
.then(saveSucceeded)
.fail(saveFailed);
}
Once I try to save; I get this message:
The 'entities' parameter is optional or it must be an array where each element must be an entity
Modifying the code to:
save = function (product) {
var so = new breeze.SaveOptions({ allowConcurrentSaves: true })
return manager.saveChanges([product,product.units()[0]],so)
.then(saveSucceeded)
.fail(saveFailed);
}
Works fine for one product unit.. However, I needed to save a specific product with all of it's units
in one shot..
Any help is appreciated.
For those who might have similar issue; I got it fixed by modifying the code to the following:
save = function (product) {
var entitiesToSave = new Array(product);
product.Units().forEach(function (Unit) {
entitiesToSave.push(Unit);
});
var so = new breeze.SaveOptions({ allowConcurrentSaves: true })
return manager.saveChanges(entitiesToSave,so)
.then(saveSucceeded)
.fail(saveFailed);
}
Regards to all.
Related
I am testing the OData Action using ODataActionsSample, which is downloaded from http://aspnet.codeplex.com/sourcecontrol/latest#Samples/WebApi/OData/v4/ODataActionsSample/ODataActionsSample/, as a server and calling the "CheckOut" action which is,
[HttpPost]
public IHttpActionResult CheckOut(int key)
{
var movie = _db.Movies.FirstOrDefault(m => m.ID == key);
if (movie == null)
{
return BadRequest(ModelState);
}
if (!TryCheckoutMovie(movie))
{
return BadRequest("The movie is already checked out.");
}
return Ok(movie);
}
The action returns the movie with updated "DueDate" proprety in the sample program which is calling the action from javascript as below:
// Invoke "checkout" or "return" action. Both actions take no parameter data.
function invokeAction(url) {
ajaxRequest("post", url)
.done(function (updated) {
updateMovie(updated);
})
.fail(function (jqXHR, textStatus, errorThrown) {
//parent.errorMessage(errorThrown);
parent.errorMessage(url);
});
}
self.update(data);
// Update the model with new data from the server.
function updateMovie(data) {
var dueDate = data.DueDate ? new Date(data.DueDate) : null;
self.dueDate(dueDate);
if (data["#ODataActionsSample.Models.CheckOut"]) {
self.checkoutUrl(data["#ODataActionsSample.Models.CheckOut"].target);
}
else {
self.checkoutUrl(null);
}
if (data["#ODataActionsSample.Models.Return"]) {
self.returnMovieUrl(data["#ODataActionsSample.Models.Return"].target);
}
else {
self.returnMovieUrl(null);
}
}
However, the call from OData Client returns the movie without the DueDate updated. The client code is as below:
string serviceUri = "http://localhost:44221/OData/";
var container = new Container(new Uri(serviceUri));
var movieQuery = from movie in container.Movies select movie;
DataServiceCollection<ODataActionsClient.Movie> trackedMovies = new DataServiceCollection<ODataActionsClient.Movie>(movieQuery, TrackingMode.AutoChangeTracking, "Movies",null,null);
var myMovie = trackedMovies[0];
try
{
var checkouttedMovie = myMovie.CheckOut().GetValue();
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
What is wrong with my code in the client side ?
The default value for merge option is AppendOnly : No current values are modified.
OverwriteChanges : All current values are overwritten with current store values, regardless of whether they have been changed.
PreserveChanges: Current values that have been changed are not modified, but any unchanged values are updated with the current store values. No changes are lost in this merge.
So you have to decide which option you want to achieve, in this case I think OverwriteChanges is good enough.
FYI : https://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption(v=vs.110).aspx
I have the following code that went from this:
var getUserByGuid = function (guid, entityObservable) {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
function fetchSucceeded(data) {
var entity = data.entity;
if (ko.isWriteableObservable(entityObservable))
entityObservable(entity);
return entity;
}
function queryFailed(error) {
logger.error(error);
}
};
to this:
var getUserByGuid = function (guid, entityObservable) {
if (datacontext.manager.user.metadataStore.isEmpty()) {
return datacontext.manager.user.metadataStore.fetchMetadata('breeze/user')
.then(function () {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
});
} else {
return datacontext.manager.user.fetchEntityByKey('User', guid, true)
.then(fetchSucceeded)
.fail(queryFailed);
}
function fetchSucceeded(data) {
var entity = data.entity;
if (ko.isWriteableObservable(entityObservable))
entityObservable(entity);
return entity;
}
function queryFailed(error) {
logger.error(error);
}
};
Notice the extra check to verify metadataStore is ready? Since I am making a call to fetch, I would assume this check would happen internally but for some reason it is not.
My code runs well with the following "work-around" in place but wanted to bring this to light.
Updated 3/1/2014
As of Breeze 1.4.9 (or later), available now this has been fixed.
Previous post
I think you are right. The problem, I think, is that fetchEntityByKey doesn't actually have to perform a fetch when you tell it to search the local cache first. But in this case, if you don't have metadata then the localQuery fails. I'll try to get this fixed in the next release, probably out later this week.
I have an application that records a transaction and the user can pick the category from a drop down.
Categories are loaded up at application startup as they are "mostly" static / rarely going to change.
So, in my datacontext.js I do the usual and prime my data;
var primeData = function () {
var promise = Q.all([
getLookups(),
getBankAccountPartials(null, true)])
.then(applyValidators);
return promise.then(success);
function success() {
datacontext.lookups = {
categories: getLocal('Categories', 'name', true),
transactiontypes: getLocal('TransactionTypes', 'name', true),
payees: getLocal('Payees', 'name', true)
};
log('Primed data', datacontext.lookups);
}
function applyValidators() {
model.applyBankAccountValidators(manager.metadataStore);
}
};
function getLookups() {
return EntityQuery.from('Lookups')
.using(manager).execute()
.then(processLookups)
.fail(queryFailed);
}
Now, occasionally in an Admin screen the user can edit and add a category.
In the categoryadd.js viewmodel my save code looks something like this (extract shown);
save = function () {
isSaving(true);
datacontext.saveChanges()
.then(goToEditView).fin(complete);
function goToEditView(result) {
router.replaceLocation('#/categorydetail/' + category().id());
}
function complete() {
isSaving(false);
}
},
How do I refresh just the Categories lookup data? Or, am I just doing this wrong and should perhaps NOT have categories as a lookup?
Thanks.
Breeze.js synchronises automatically and knows to search out the Category and update it in its lookup list.
I checked this by calling datacontext.lookups from the browser console after the save had been performed and inspecting the objects it showed me the category name had been refreshed.
My collection and model like this:
detail_userid = 0;
detail_contactid = 0;
var ContactDetail = Backbone.Model.extend({
urlRoot: URL_CONTACTS1+detail_userid+"/"+detail_contactid
});
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: URL_CONTACTS1+detail_userid+"/"+detail_contactid
})
The entrance is:
ContactDetailManagePageModel.prototype.init = function(m,n){
detail_userid = m;
detail_contactid = n;
var myContactDetails = new ContactDetailCollection();
var contactDetailListView = new ContactDetailListView({
collection: myContactDetails
});
myContactDetails.fetch({reset:true});
}
But when it runs,the url is :http://localhost:8080/ws/users/contacts/0/0,it means that the assignment to detail_userid and detail_contactid is unsuccessful,I don't know why.
Hope for your help.Thanks.
I think you are statically definining the urlRoot and url properties before you are running the init of the PageModel (not quite sure where you are getting m and n from though...)
Both url and urlRoot can be a function, so you can pass in options during instantiation and have them dynamically set on the model.
Simple example covering defining the collection and then creating one
var ContactDetailCollection = Backbone.Collection.extend({
model: ContactDetail,
url: function(){
return URL_CONTACTS1 + this.options.detail_userid + "/" + this.options.detail_contactid;
}
});
var myContactDetails = new ContactDetailCollection({
detail_userid: foo,
detail_contactid: bar
});
As I mentioned, I'm not sure what your init function is doing, I'm guessing it's something custom from your app that I don't need to worry about.
I'm fairly sure the main thing to take away is to set url and urlRoot dynamically
I would fulfill the accepted answer with few remarks.
First parameter when initializing Backbone.Collection is array of models, then options. To create an empty collection with options you should do next
var c = new Backbone.Collection(null, {opt1: val1, opt2: val2});
Actually, you can't access this.options in url function, bec. there are no options like in a model. What you can do, is assign required properties from options upon initialization.
initialize: function (models, options) {
// `parseInt()` is used for consistency that `id` is numeric, just to be sure
this.detail_userid = parseInt(options.detail_userid);
this.detail_contactid = parseInt(options.detail_contactid);
}
Later you can access them like this:
url: function() {
return URL_CONTACTS1 + this.detail_userid + "/" + this.detail_contactid;
}
I wanted to use the HATEOAS href from one model to fetch data of another model. It worked to simply set the url on the newly created collection instead of defining it right away in the constructor.
var DailyMeasuresCollection = Backbone.Collection.extend({
//url : set dynamically with collection.url = url
model : DailyMeasuresModel,
parse : function(data) {
return data._embedded.dailyMeasures;
}
});
var DailyMeasuresTopicListItemView = Backbone.View.extend({
//...
events : {
'click .select-topic' : 'onClick'
},
onClick : function() {
var topicMeasures = new DailyMeasuresCollection()
topicMeasures.url = this.model.attributes._links.measures.href // <- here assign
var topicMeasuresView = new DailyMeasuresListView({
collection : topicMeasures
});
topicMeasures.fetch()
}
});
I develop an application with asp.net mvc + breeze.
So far, I retrieve a specific record (based on id) like this:
var getTransportById = function (transportId, transportObservable) {
return manager.fetchEntityByKey('Transport', transportId, true)
.then(fetchSucceeded)
.fail(queryFailed);
}
function fetchSucceeded(data) {
var s = data.entity;
return ...
}
Now I need to retrieve the same record but need to 'expand' the property named sender which links to another entity (table). I did not find a way to 'expand' one property through fetchEntityByKey so I used a query like this:
var getTransportById = function (transportId, transportObservable) {
var query = EntityQuery.from('Transports')
.where('id', 'eq', transportId)
.expand('Sender')
.orderBy(orderBy.transport);
return manager.executeQuery(query)
.then(fetchSucceeded)
.fail(queryFailed);
}
function fetchSucceeded(data) {
var s = data.results[0];
return ...
}
My question: is it the good way to proceed? Is there another way of doing?
Thanks.
You can create a query from an EntityKey and then expand whichever properties you want. Something like this:
var entityKey = new EntityKey("Transport", transportId);
// expand whichever nav props you want here.
var query = EntityQuery.fromEntityKey(entityKey).expand("Sender").orderBy(...);
return entityManager.executeQuery(query).then( {
...
});