I have two Collections in Meteor, and trying to join them. As defined in collections/collections.js
Producers = new Mongo.Collection('producers');
Projects = new Mongo.Collection('projects');
Conceptually, one Producer has 0 to many Projects. One Project must have a Producer. So, a ProducerID field is in each Project document (row) in Mongo. I seeded the Mongo database with data.
When my template for viewing Projects is displayed, I want it to have access to the Producer's attributes.
In Iron Router's config (in /app.js), I have
Router.route('project', {
path: '/project/:name',
template: 'project',
waitOn: function() {
return Meteor.subscribe('ProjectInfo', this.params.name);
},
data: function() {
return Projects.find();
}
});
I have two publishes on the server (in /server/publish.js):
Meteor.publish("ProjectInfo", function(projectName) {
return Projects.find( {name: projectName} );
});
Meteor.publish("ProducerInfo", function(producerid) {
return Producers.find( {_id: producerid});
});
Question 1:
How to join? I definitely don't want to just throw Producer data into each Project, because it makes it harder to update the database when a Producer data changes.
Question 2:
Why does Iron Router need a "data:" field, when it already has a Subscribe in the waitOn?
data: function() {
return Projects.find();
}
Thanks in advance.
How to join: I'll just give you link to package doing just that: publish with relations
And you don't need data field when you publish, if your router loads OK and you have waitOn with publishes then on client side all your data should be visible when you do Producers.find() or Projects.find()
Although, waitOn requiers an array, so try
return [Meteor.subscribe('ProjectInfo', this.params.name)];
Related
I am new to VUE and trying to post a complex object with a list of tokens to an MVC c# route.
The network tab from my post request shows:
tokens[0][field]: 129
tokens[0][id]: 1
tokens[0][name]: MyPriority
tokens[0][operator]:
tokens[1][field]:
tokens[1][id]: 3
tokens[1][name]: -
tokens[1][operator]: -
The MVC controller shows the object, and it shows the number of tokens passed. However, the framework is not binding to the properties being passed in (field, id, name, operator). I am unsure if I need to amend the MVC part to bind, or change the JS object before posting.
the MVC controller is
public ActionResult CreateRule(Rule rule, List<Token> tokens)
the Js Code is:
methods: {
saveRule() {
let tokens = [];
debugger;
for (let i = 0; i < this.tokens.length; i++) {
var t = {};
t.field = this.tokens[i].field;
t.id = this.tokens[i].id;
t.name = this.tokens[i].name;
t.operator = this.tokens[i].operator;
tokens.push(t);
}
let newRule = {
id: this.rule.id,
type: this.rule.type,
name: this.rule.name,
field: this.rule.field,
tokens: tokens
};
this.$emit("save-rule", newRule);
},
It does not feel like a great way to have to copy all the parameters into a new object, so I assume this is not the best way. The vue tutorials had it cloning the data for posting to the server. In any case, it has not made any difference to the MVC reading the post data.
I have seen people try Stringify, but issues around datatypes the { ...rule } seemed to make no difference. I was hoping it was a quick answer as it must be possible, it is far from specialized or unique action!
Consider the below code. It works fine when getting data from the server. I have a custom data adapter (staffManagemetnService) which creates client-side entities from the json returned by the server.
However, if I make a call to executeQueryLocally, it fails and raises the following exception: Cannot find an entityType for resourceName: 'GetInternalResourcesByCompetence'. Consider adding an 'EntityQuery.toType' call to your query or calling the MetadataStore.setEntityTypeForResourceName method to register an entityType for this resourceName
var query = breeze.EntityQuery.from('GetInternalResourcesByCompetence').withParameters(parameters);
var result = self.manager.executeQueryLocally(query.using(dataService.staffManagementService));
if (result) {
return $q.resolve(result);
} else {
return this.manager.executeQuery(query.using(dataService.staffManagementService))
.then(function (data) {
return data.results;
})
.catch(function (err) {
logError('Restrieving resources days off failed', err, true);
});
}
I'm not sure what this means. Should it not work out-of-the-box since I've specifically asked breeze to use the custom dataAdapter ?
It's important to different between resource names and entity type names. Resource names are usually part of an endpoint and in plural (eg orders). Type names are typically singular (eg order).
Locally breeze cannot do much with the resource name, since it won't call the endpoint. Instead you ask for a certain entity type name.
You can map an entityType to a resourcename using the setEntityTypeForResourceName function:
metadataStore.setEntityTypeForResourceName('Speakers', 'Person');
See chapter "Resources names are not EntityType names" and the following chapters here: http://www.getbreezenow.com/documentation/querying-locally
I am writing an application using backbone + rails.
The application allows users to create tickets, and show these tickets in real-time for all other users using real-time service pusher.
My problem is that when a user creates a ticket I add it to the collection:
addTicket: function( newTicketData ) {
var newTicket = new app.Ticket( newTicketData );
this.collection.add(newTicket, {merge: true});
newTicket.save(null, {
wait: true,
success: this.addTicketSuccess,
error: this.addTicketError
});
},
The pusher listener code:
channel.bind('new_ticket', function(data) {
var ticketDataObj = jQuery.parseJSON( data.content );
app.ticketsView.addTicket(ticketDataObj);
});
In this point the new model has a cid but not an id from the database, which might be obtained in the addTicketSuccess callback.
On the other hand, the server sends a pusher signal in the create action which sends the new ticket with its id, but without id.
The result is two different models in the collection which represent the same object, one with cid and other with id. I am aware to the race-condition and looking for nice and robust solution.
Thanks!!!
The cid is a client id and is never send to the server.
When Backbone model save is called successfully the data received from the server are set on the same object, not creating another model.
So your are manually adding the two models to your collection, try to remove this line :
channel.bind('new_ticket', function(data) {
var ticketDataObj = jQuery.parseJSON( data.content );
// create a new method addAndMergeTicket
app.ticketsView.addAndMergeTicket(ticketDataObj);
});
In this new method, before you add the model to the collection, iterate through the collection models and if you find that it already exists don't add it again
(Contrived) example collection urls:
VERB /leagues
VERB /leagues/{leagueId}/teams
VERB /leagues/{leagueId}/teams/{teamId}/players
The goal is to configure my associations and proxies to automatically target these urls.
Currently, I have a model for each of League, Team, and Player, with a hasMany association chain in the direction of
(League) ---hasMany--> (Team) ---hasMany--> (Player)
with the the Id's of the owning model used as the foreign key in the associated model.
(I.e. each Team has a leagueId which equals the id of it's owning League, and each Player has a TeamId which equals the id of it's owning Team)
One attempt at solving this can be found here. However, it didn't work for me. My initial attempt was overriding the buildUrl method of the proxies as:
buildUrl: function(request) {
var url = this.getUrl(request),
ownerId = request.operation.filters[0].value;
url = url.replace('{}', ownerId);
request.url = url;
return Ext.data.proxy.Rest.superclass.buildUrl
.apply(this, arguments);
},
Which works perfectly for a url resource depth of 1 (VERB /leagues/{leagueId}/teams). The problem is when I do something like:
League.load(1, {
callback: function(league) {
league.teams().load({
callback: function(teams) {
// all good so far
var someTeam = teams[0];
someTeam.players().load({
// problem here. someTeam.players() does not have a filter
// with leagueId as a value. Best you can do is
// GET/leagues/undefined/teams/42/players
});
}
});
}
});
What do I need to override in order to get all the information I need to build the url in the buildUrl methods? I don't want to manually add a filter each time - that sort of defeats the purpose and I might aswel set the proxy each time.
Thanks for your help
My current (working) solution I came up with was to modify the constructor of internal resources and manually add filters to the generated stores there. For example:
App.models.Team
constructor: function() {
this.callParent(arguments);
this.players().filters.add(new Ext.util.Filter({
property: 'leagueId',
value: this.raw.leagueId
}));
return this;
},
and that way all the filter property:value pairs are available in the buildUrl methods via request.operation.filters.
I'll accept this answer for now (since it works), but will accept another answer if a better one is suggested.
I've implemented a custom DataService adapter for BreezeJS - I wanted to use Breeze with a RESTful back end service (not OData or ASP.NET Web API).
So far - decent results after a learning curve.
I'm having an issue that when I call save changes - afterwards my entities on the client do not get marked as 'Unchanged'. They keep the same entityState.
I assume it has something to do with the success handler of the AJAX request to the backend service (looking at the source code to the WebAPI adapter):
success: function(data, textStatus, XHR) {
if (data.Error) {
// anticipatable errors on server - concurrency...
var err = createError(XHR);
err.message = data.Error;
deferred.reject(err);
} else {
// HACK: need to change the 'case' of properties in the saveResult
// but KeyMapping properties internally are still ucase. ugh...
var keyMappings = data.KeyMappings.map(function(km) {
var entityTypeName = MetadataStore.normalizeTypeName(km.EntityTypeName);
return { entityTypeName: entityTypeName, tempValue: km.TempValue, realValue: km.RealValue };
});
var saveResult = { entities: data.Entities, keyMappings: keyMappings, XHR: data.XHR };
deferred.resolve(saveResult);
}
},
It looks like the response includes an array of 'Entities'. What do these 'Entities' look like? It echoes what the client sent with an updated entityAspect.entityState value (server responses with 'Unchanged')?
Is that what should be passed into the deferred.resolve call?
I've got a working solution for this.
In a nutshell here's what is required for the object that is passed to the
deferred.resolve(saveResult);
Call in the success handler of the save change AJAX request.
Server response should include information about how to map from the client generated id to the server generated id (if the server generated one). This can be one keyMapping property returned in the response (like the Breeze API controller does) or what my service does is return a keyMapping property as a child property of a particular resource
The client code should create an array of objects that look like:
{ entityTypeName: "fully qualified entity type name",
tempValue: "client generated id",
realValue: "server generated id"
}
this array is the keyMappings property of the saveResult object
the entities property of the saveResult object is a flat list of all the entities that were modified from the server. Because of the design of my service API, it can return an entity, and child entities embedded in it, which I had to traverse and pull out into a flat list. Additionally these entity objects should be 'raw' and not include the entityAspect property or anything Breeze might interpret as a 'real' entity.
Also - something that can also be helpful is to look at the new sample from the Breeze folks - the MongoDB Breeze sample. They've implemented a custom dataServiceAdapter that hooks up their NodeJS/MongoDB backend. That provided some additional insight as well.
Good luck!