I'm working my way with BreezeJS in combination with Durandal. This all works fine but I have a question about creating a new entity that has a relation with another entity.
Lets say we have an entity customer and order. When I create a new order which relies on customer it has 2 fields one for customerID and one for Customer it self! The customerID is given correctly but it crashes for not having a Customer object! How do I solve this?
My Dataservice.createT is:
// This function creates an entity for the specific table (entity)
// that is passed as an parameter. The entity is the same as the table name
//in the map folders. So for creating a new Memo entity should be tblMemo
Dataservice.prototype.createT = function (initialValues, entity) {
return this.manager.createEntity(entity, initialValues);
};
var repairOrderLineN = dataservice.createT({
RepairOrderID: '1', CustomerID: result[0].CustomerId,
Description: result[0].Description, InternalCode: result[0].Code,
Hours: result[0].Hours, Tariff: result[0].Tariff,
Costs: result[0].Costs, Quantity: result[0].Qty,
DamageCode: result[0].Dam, RepairCode: result[0].Rep,
MaterialCode: result[0].Mat, LocationCode: result[0].Location,
IsAgreement: result[0].IsAgreement, IsAuthorized: result[0].IsAuthorized,
DoRepair: result[0].DoRepair
}, 'tblRepairOrderLine');
if (repairOrderLineN.entityAspect.validateEntity()) {
extendItem(repairOrderLineN);
repairorderlines.push(new repairorderline(repairOrderLineN));
dataservice.saveChanges();
updateFinance();
}
A JSON is given from a modal to create the entity and off course I understand the customer object isn't in it! damn i'm lost :S Help :(
I need more information about your case and specifically what you mean by "crashes".
I don't know what your dataservice.createT does. It's actually a little too complex for easy answer here. Can you simplify a test case for us?
Meanwhile, here is a passing test that I'm adding to "entityTests.js" of the DocCode sample that demonstrates your scenario with Northwind.
/*********************************************************
* Add an Order with initializer that set its parent Customer by Id
*********************************************************/
test("add Customer created using initializer with parent Customer Id", 4, function () {
var em = newEm();
// create a new parent Customer
var parentCustomer = em.createEntity("Customer", {
CustomerID: breeze.core.getUuid(),
CompanyName: 'TestCo'
});
// a new Order which is a child of the parent Customer
var newOrder = em.createEntity("Order", { CustomerID: parentCustomer.CustomerID() });
ok(newOrder.entityAspect.entityState.isAdded(), "newOrder should be 'added'");
ok(parentCustomer.entityAspect.entityState.isAdded(), "parentCustomer should be 'added'");
var orderCustomer = newOrder.Customer();
ok(orderCustomer, "newOrder's parent 'Customer' property should return a Customer entity");
ok(orderCustomer === parentCustomer,
"newOrder's parent Customer should be " + parentCustomer.CompanyName());
});
It would work just as well if the parent Customer previously existed (e.g., had been fetched).
Btw, I had to initialize parentCustomer.CustomerID because it is defined in metadata as a client-generated key. In most of our examples, the entity key is store-generated (e.g., Order.OrderID) and would not be specified.
Update 4/30/2013
I suspect that somewhere in your initializer object is a result[0].something where something is an entity rather than a simple data value (e.g,. an integer id).
As I write, the createEntity blows up if any initializer value is a Breeze entity. That will be fixed in v.1.3.2 which should be out soon. Note that this new feature will be beta at first. The API won't change but there are many edge conditions and it's hard to cover them all. It will take time to drive out lingering bugs.
No matter what, you have alternatives today. You would be fine if you just initialized using the foreign key ids for the related entities instead of initializing with the related entities themselves. Or you could assign the related entities after first creating the entity as in:
...
var thing = manager.createEntity('Thing', {
... initialize with simple data values
}
var thing.relatedEntityA = relatedEntityA;
var thing.relatedEntityB = relatedEntityB;
...
Related
I recently started using Bookshelf. Examples in the documentation aren't very clear to me. This is one such example:
var knex = require('knex')({
client: 'mysql',
connection: process.env.MYSQL_DATABASE_CONNECTION
});
var bookshelf = require('bookshelf')(knex);
var User = bookshelf.Model.extend({
tableName: 'users',
posts: function() {
return this.hasMany(Posts);
}
});
var Posts = bookshelf.Model.extend({
tableName: 'messages',
tags: function() {
return this.belongsToMany(Tag);
}
});
var Tag = bookshelf.Model.extend({
tableName: 'tags'
})
User.where('id', 1).fetch({
withRelated: ['posts.tags']
}).then(function(user) {
console.log(user.related('posts').toJSON());
}).catch(function(err) {
console.error(err);
});
After the creation of three models (User, Posts, and Tag) - there is a query.
What exactly is the meaning of this query?
User.where('id', 1).fetch({withRelated: ['posts.tags']}).then(function(user) {
console.log(user.related('posts').toJSON());
}).catch(function(err) {
console.error(err);
});
What are withRelated, 'posts.tags', user.related('posts')? Can anyone tell me in simplest form, what those terms are and where they come from?
Finally, what is the meaning of the complete query?
It's all answered in the documentation, although not everything in the exact same place, so I can understand your confusion. You really have to read the whole thing to better understand it.
From the documentation of Model.fetch():
withRelated: Relations to be retrieved with Model instance. Either one or more relation names (...) A single property, or an array of properties can be specified as a value for the withRelated property.
From Collection.fetch():
The withRelated option may be specified to fetch the models of the collection, eager loading any specified relations named on the model.
From Collection.load():
... Nested eager loads can be specified by separating the nested relations with .
From Model.related():
The related method returns a specified relation loaded on the relations hash on the model, or calls the associated relation method and adds it to the relations hash if one exists and has not yet been loaded.
All of these are involved in the eager loading of relations on models.
What these methods do is load some data from a table that is related to the parent model's table in some way. In the example you posted, the User model has some Posts and the Posts themselves have some tags, so the relations go something like:
User
|_ Post
|_ Tag
These are expressed as methods on each model, for example, posts: function() { ... } in the User model.
When you specify a relation to eager load using withRelated, you use these method names. Furthermore you can eager load deeply nested relations by separating them with a ., and that's what you're seeing in the example.
So, putting everything together what that example is doing is searching for the User with id = 1 and also retrieving all the posts that belong to that user as well as all the tags that belong to all the posts that belong to the user.
Then to access these related objects you use the related('something') method of the model.
I'm creating a multipage survey with Node.js 6, Express.js 4 and Sequelize 4.4.2. While the user fills out the survey several model objects are build, but not persisted, this will not happen until the survey is completely done.
Some of these models are associated with each other and I want to know if it's possible to use the .build() function of an defined model with initial values (such as "name" or "address") as well as an previously build but not persisted model object.
Maybe a simple example, what I mean:
const comp = Company.build({
Name: 'My Company',
Location: 'Ireland',
Employees: [] // Employee would be another model in this case
});
It seems, that Employees is ignored while the obejct is built. Is there a way to attach properties which are NOT defined as field for the model (in this case: Company) but as association?
Hope you got, what I mean ...
Thank you in advance! :)
Solved it by myself! I just append the built models after to the dataValues property of the previously built model object and not directly whilst the build process.
consider this data structure referenced on the Firebase quick start guide (here)
{"name": {"first": "Fred","last": "Flintstone"}
The docs say that one can access the datasnapshot location of each child object of "name" returned from a query using:
var ref = new Firebase("https://docs-examples.firebaseio.com/samplechat/users/fred");
ref.once("value", function(snapshot) {
var nameSnapshot = snapshot.child("name");
var name = nameSnapshot.val();
name === { first: "Fred", last: "Flintstone"}
var firstNameSnapshot = snapshot.child("name/first");
var firstName = firstNameSnapshot.val();
firstName === "Fred"
var lastNameSnapshot = snapshot.child("name").child("last");
var lastName = lastNameSnapshot.val();
lastName === "Flintstone"
var ageSnapshot = snapshot.child("age");
var age = ageSnapshot.val();
age === null (because there is no "age" child in the data snapshot)
});
But what's a little weird about this is when the following lines are processed.
var nameSnapshot = snapshot.child("name");
var name = nameSnapshot.val();
name.first, and name.last are also retrieved. So why would one use this snapshot method "child()"? Or rather when would it be beneficial to use this method, since when you pull the parent object, Firebase pulls all children, or is there a way to retrieve a parent node/object without pulling some of it's children? Then this method to me would make sense.
Any information would be gratefully appreciated! Thanks
is there a way to retrieve a parent node/object without pulling some of it's children?
The Firebase JavaScript API always retrieves the complete node. So: no, there isn't a way in the JavaScript API to get a shallow result/
why would one use this snapshot method child()?
If we compare snapshot.child("property") with snapshot.val().property. The DataSnapshot.child() method returns a DataSnapshot, from which you can get a ref again. The val() method deserializes the snapshot's value into JSON. So you'll have to construct your own ref if you'd need one. But the value of each depends highly on your use-case, so "why" is not something I can answer for you.
The snapshot is a exact picture of everything in the node at the time of the call. What it contains however, will vary depending on how you get the snapshot.
The example provided in the guide is slightly one-dimensional. In general you would not have a node called name with just one person listed.
A better example would be a node called users with data as such
users
user_id_0
firstName:
lastName:
age:
user_id_1
firstName:
lastName:
age:
When used with the Value parameter, the snapshot of the users node contains all of the node's children and all of the data within each child (in this case, all of the users and their data), and the block that handles it is called once. We use this to read, for example, all of the users in the users node and then iterate over the users for some specific data. We also use it to do multi-parameter queries, which are not supported directly by firebase. So for example, we want to get all users named Elmo, age 20.
The Add parameter reads each child, one at a time, calling the block once for child, which would be each user in this case. Typically we use this to keep UI tableView's updated (ObjC) so when a new child data is added to Firebase, all of the apps who are observing will be notified, so we can then update our UI table.
You cannot retrieve a parent object without also retrieving the children. However, you can directly access a child if you know the parent object if you are looking for a specific piece of data. So you could retrieve users/user_id_0/age
For UI purposes, when I load the array my viewModel is based on I add a new property to each object based on some other properties:
item.forEach(function (party) {
if (party.AcknowledgementDate() === null) {
party.Agreed = ko.observable(false);
}
else {
party.Agreed = ko.observable(true);
}
vm.Parties.push(party);
});
"Parties" is defined as ko.observableArray when the page starts.
The items in this array are edited in a separate UI window. When those changes are saved and the window closed, I call this function to update those values:
function updateAgreed() {
vm.Parties().forEach(function (i) {
if (i.AcknowledgementDate() === null) {
i.Agreed(false);
}
else {
i.Agreed(true);
}
});
}
This all works fine, and makes me very happy. The problem arrives when users create a new party item. We're using Breeze too, so we go off to the data service which requests entity framework create a new object of the appropriate type, then add an observable:
var lp = manager.createEntity('Party_dto'. { [an array of initial values] });
lp.Agreed = ko.observable('');
return lp;
Thanks to Breeze, this adds itself to the Parties observableArray because it's related to the same parent object. I can then call updateAgreed again to populate the Agreed observable with the appropriate value.
Logically, this work as expected - you can step through it and watch the Agreed observable of the new item be added and populated with the expected values. The problem comes in the UI - it doesn't update as having changed. Yet running the same code against an already-loaded object does cause the UI to update.
I'm stumped by this. I can't replicate it in Fiddle because we create objects in Breeze and not on the fly - and making a mock version without Breeze works perfectly. Why do my observables update on already loaded objects, but the same observable not update on a new object?
There are a few things that I see that need to be addressed. One, since you are using Breeze, take advantage of the model constructors and initializers. Wherever you are defining properties for your models, add the following code -
metadataStore.registerEntityTypeCtor(
'Party', null, partyinitializer);
function partyinitializer(party) {
party.Agreed = ko.observable(false);
}
Now all of your party entities have an agreed property that you can access. Next, make sure you aren't setting the Party's parent navigation property in the createEntity method, as that will break your binding.
var lp = manager.createEntity('Party'. { [an array of initial values] });
lp.parentParty(something); // Set the parent here
return lp;
This will make sure that before the party is bound back to the parent and shown in the view, all of the properties will be set. Then when you set the navigation property, it will show up in your view all happy-like.
In this stackoverflow post i read about filtering backbone collections and using subsets.
One answer (by sled) recommends using backbone.subset.js (usage example).
I could not find any further resources on backbone.subset.js and I failed implementing it into my project.
It seems like backbone.subset.js is the perfect solution for what i'm trying to achieve.
(Having one "parent" collection that holds all models at all times, and depending on user input filtering the relevant models from the parent collection into a backbone.subset collection.)
My "parent" collection, holding all tasks:
var TasksAll = Backbone.Collection.extend({
url: '/tasks', // the REST url to retrieve collection data
model: Task // the models of which the collection consists of
});
var allTasks = new TasksAll();
Now i want to create a subset collection for e.g. tasks where task.status = 0:
var TasksTrash = new Backbone.Subset({
superset: allTasks,
filter: function(Task) {
return Task.isTrash();
}
});
var trashTasks = new TasksTrash();
Whereas inside the Task model, the method "isTrash" returns true if:
this.get('status') == 0
a) Are there any more resources on backbone.subset.js?
b) How do I implement above scenario?
c) Can I pass 'superset' and 'filter' options as params to the Backbone.Subset init function?
d) I looked into the backbone.subset.js code, when I 'reset' my parent Collection my subset Collections should be updated straight away, right?
PS: I'm fairly new to Backbone. Thanks for your help.
Looking at the source for backbone-subset, it looks as though there is a pre-initialization hook which you could utilize in order to make the 'sieve' or filter available as an option or argument:
https://github.com/masylum/Backbone.Subset/blob/master/backbone.subset.js#L50
As for providing parent as an argument, there is an outstanding patch to add that exact functionality:
https://github.com/masylum/Backbone.Subset/pull/5
With it, you can pass in parent as an option, if it is not an option the library will fall back to looking for it on the object Prototype