My dilemma is that I would like to pass multiple object properties to an iron:router route in Meteor. The reasoning is that I would like to pass it a property to name my url with and a property to find a collection item with. They are completely independent of each other and I can't use the url property because it is not a value in the collection item. This is what I have:
Template.items.events({
'click': function () {
itemName = this.name.replace(/ /g,'')
Router.go('itemDetails', {itemName: itemName})
}
});
The problem is that although the Router handles this fine and sends me to the correct url, I cannot use itemName to find the collection item object that I am looking for (assume this is impossible).
Router.route('/items/:itemName', {
name: 'itemDetails',
data: function() {return Items.findOne({name: this.params.itemName})}
});
The above Router configuration will not return anything because name != this.params.itemName for any object.
I've tried passing the this object, or creating objects with multiple properties, but iron:router won't have it.
Any help is appreciated, thanks.
Edit #1: To help explain the question further, my problem is the same as routing to a page that uses multiple id's in the URL. For example, how would I go about passing properties to iron:router to fill the :_id and :itemId properties?
Router.route('items/:_id/:_itemId', {
name: 'detailDetails',
data: function() {...}
});
Edit #2: What I would like to do specifically is pass two properties to iron:router and have one of them be appended to the URL, and the other be used in the data property of the route to return a collection item. Example:
....
Router.go('itemDetails', {_id: itemBeingPassedId, itemName: nameToBeAppendedToURL})
....
Router.route('/items/:itemName', {
name: 'itemDetails',
data: function(){return Items.findOne(_id)
});
Whenever I try to do that, it says that _id is undefined. So basically, how can I pass a property to data without having it be a part of the URL and using this.params?
Is the question how to pass multiple parameters to Router.go? Just put all of them in the object for the second parameter:
Router.go('itemDetails', {_id: 'foo', '_itemId': bar});
Edit:
Ok, if you want to pass arbitrary values to the url, you can use query paramters:
Router.go('itemDetails', {itemName: 'foo'}, {query: 'id=bar'});
The id will still be in the url though, it will look like this:
http://example.com/items/foo?id=bar
And you can retrieve it like this:
Router.route('/items/:itemName', {
name: 'itemDetails',
data: function(){
return {
item: Items.findOne(this.params.query.id),
itemName: this.params.itemName
};
}
);
Related
I've got a dynamic select list created with vue.js. I want to update a "details" box on the page with data grabbed via an ajax call. The basic idea is here: https://jsfiddle.net/pznej8dz/1/
I don't see why the sf_detail data isn't being updated when the object is updated from the webservice call. Is there a different way this should be done in vue?
Your object references are getting out of sync! Calling getSourceFieldDetails causes field and sf_detail to reference different objects.
The Problem
At the start of the script, an object is created
{
Name: 'Test',
Label: 'Data',
Type: 'Boolean'
};
And that object is given a reference named sf_detail.
In sf_detail_info, field is set equal to the reference named sf_detail
data: {
field: sf_detail
}
But, in getSourceFieldDetails, sf_detail is set to reference a new object. Thus sf_detail references the new object, but field still references the old one.
The Solution
The simplest solution is to never set sf_detail equal to a new object. Instead, update the properties of the existing object. The modified version of getSourceFieldDetails looks like this:
function getSourceFieldDetails(val) {
// this would actually call an ajax endpoint to get this data
console.log(val[0]);
sf_detail.Name = val[0];
sf_detail.Label = val[0] + 'Label',
sf_detail.Type = val[0] + 'DataType'
console.dir(sf_detail);
}
Here is a fork of your fiddle with the change.
I'm trying to load includes on an existing model in sequelize. In express we pre check the models to see if they exist in the middleware.
So once we're in the actual "controller" we want to run some includes on that existing model that is passed in.
req.models.item.incude([
{model: Post, as: 'posts'}
])
Is there any way to accomplish this?
EDIT:
I know we can do something like this.
return req.models.item.getThing()
.then(function (thing) {
req.models.item.thing = thing;
return req.models.item;
});
But:
My expansions for includes are a dynamic property that come via url parameters, so they are not know ahead of time.
It I return the above you will not see the "thing" in the response. I need it nicely built as part of the original instance.
Something like a .with('thing', 'other.thing'); notation would be nice. Or in the case of sequelize .with({include: ...}); or .include([{model: ...}]);
If the variable req.models.item is already an Instance but without its other related instances ("includes"), then you could include them using something like the following code:
Item.findAll({
where: req.models.item.where(),
include: [{
model: SomeAssociateModel,
}]
})
.then(function(itemWithAssoc) {
// itemWithAssoc is an Instance for the same DB record as item, but with its associations
});
See here for some documentation. See here for a script demo'ing this.
Update: Given the instance, how do I just get the associated models?
To do this just use the automatically generated "getAssociation" getter functions, e.g.:
function find_associations_of_instance(instance) {
return instance.getDetails();
}
I've updated the script to include this as an example. For more information on these functions, see the SequelizeJS docs.
In my Menu controller I have a function to save a Menu. The $scope.menu variable holds the object that represents my menu data. The saveMenu function sends an Ajax call off to a REST API endpoint and then receives the updated menu back as a response. The problem is that when I assign $scope.menu to the response all my data bindings in the HTML template break. Suddenly all the menu data disappears.
The controller code.
$scope.saveMenu = function() {
var menu = $scope.createJsonMenuRequest();
var method = "PUT";
var url = FoodUrls.foodAPI + "menus/" + menu.id;
var req = {
method: method,
url: url,
headers: {
"Content-Type": "application/json"
},
data: angular.toJson(menu)
};
$http(req).success(function(data) {
$scope.menu = $.extend(true, {}, data);
});
};
The createJsonMenuRequest function simply goes through the menu and removes some properties from the copy that the API doesn't like.
Why does the binding to the HTML template break?
Updated
Before the assignment statement in the success function, the $scope.menu looks something like this.
{
name: "My Menu",
sections: [
{ $$hashKey: "object:17", id: 1, name: "blarg"}
]
}
Afterwards it looks like this...
{
name: "My Menu",
sections: [
{ id: 1, name: "blarg-edited"}
]
}
It loses the $$hashKeys that Angular is putting in there when the menu is originally created. Not sure what the significance of that is.
I would not recommend using jQuery's extend functions against any property on an angular $scope. $scope is a complicated object with lots of pointers to specific properties and such. I would recommend using angular.merge, as it should do a better job of merging the objects correctly without breaking the scope.
https://docs.angularjs.org/api/ng/function/angular.merge
I can't comment yet, so I'll just put this here.
How far did you debug?
I'd look at $scope.menu before the update, and then again after in your success method.
What's your data / template look like?
And just for my own curiosity, why the deep $.extend ? Could be totally valid, I've just never used it in this way.
Saw your update, the $hashkey shouldn't be an issue, if you don't want it, you angular.copy(data) or simply put a track by in your ng-repeat :
data-ng-repeat="item in menuItems track by $index"
Using Mongoose is it possible to have a field that references another object, when the model/type of that document is unknown?
For example, I have the models: Photos, Comments, Submissions, Posts, etc., and I would like to have a Like model that refers back to them:
var Like = new Mongoose.Schema({
// What would the value of `ref` be, should it just be left out?
target: { type: Schema.Types.ObjectId, ref: '*' }
});
From what I understand, ref needs to be a Model. I could leave it out all together, but would I still get the benefit of the Mongoose's populate method that way?
There are two approaches you can take.
1. Pass in the value of ref when you call populate
Based on the section Populating across Databases. When you call populate, you can specify the model you want to use.
Like.find().populate({
path: 'target',
model: 'Photo'
})
This requires that you know the model you want before you populate.
2. Store the value of ref together with the target
Based on the section Dynamic References.
You need to first adjust the target to something similar to the following:
var Like = new Mongoose.Schema({
target: {
kind: String,
item: {
type: Schema.Types.ObjectId,
refPath: 'target.kind'
}
}
});
target.kind is the value of "ref" that will be used for populate, and target.item is the ObjectId. We use refPath instead of ref for dynamic references.
Then, when you call populate, you will instead do something like:
Like.find().populate('target.item')
Note that we populate 'target.item' as opposed to just 'target'.
Using meteor for a test project. Can't figure out how to pass an ID and a search parameter when playing with the sample todo app they have.
For the moment, I have in my iron router:
this.route('team', {
path: '/team/:_id',
onBeforeAction: function() {
this.todosHandle = Meteor.subscribe('todos', this.params._id);
// Then filter mongoDB to search for the text
}});
The thing is, I also want to pass an optional search parameter to search for todos. So something like path: '/team/:_id(/search/:search)?'
Any ideas how to do this?
From your explanation, it sounds like you would like to carefully control which documents are actually published to the client, rather than publishing all of them and narrowing down your result set on the client. In this case, I would suggest first defining a publication on the server like so:
Meteor.publish('todosByTeamIdAndSearch', function(todoTeamId, searchParameter) {
var todosCursor = null;
// Check for teamId and searchParameter existence and set
// todosCursor accordingly. If neither exist, return an empty
// cursor, while returning a subset of documents depending on
// parameter existence.
todosCursor = Todos.find({teamId: todoTeamId, ...}); // pass parameters accordingly
return todosCursor;
});
To read more about defining more granular publications, check this out.
With a publication like the one above defined, you can then setup your route like so:
Router.route('/team/:_id/search/:search', {
name: 'team',
waitOn: function() {
return Meteor.subscribe('todosByTeamIdAndSearch', this.params._id, this.params.search);
},
data: function() {
if(this.ready()) {
// Access your Todos collection like you normally would
var todos = Todos.find({});
}
}
});
As you can see from the example route definition, you can define the path for the route exactly as you would like to see it directly in the call to the Router.route() function and access the parameters directly passed in like in the waitOn route option. Since the publication has been defined like I suggested, you can simply pass those route parameters right to the Meteor.subscribe() function. Then, in the data route option, once you have checked that your subscription is ready, you can access the Todos collection like normal with no further narrowing of the result set if you do not need to do so.
In order to learn more about how to configure your routes, check these two links out: Iron Router Route Parameters and Iron Router Route Options
On the client, you would just use Meteor.subscribe('todos'); in top-level code. 'todos' here doesn't refer to the Collection, it's an arbitrary string. Subscriptions don't care about what route you're on.
On the server, you would have a publish function like this:
Meteor.publish('todos', function() {
if (!Meteor.userId()) return;
// return all todos (you could pass whatever query params)
return Todos({});
});
Then, on your route definition:
Router.route('team', {
path: '/team/:_id',
data: function() {
if (this.params.query) { //if there's a query string
return Todos.find(/* according to the query string */).fetch();
else {
// return all the user's todos
return Todos.find({ uid: this.params._id }).fetch();
}
}
});