I want to let the user create the structure of my website. For example, I have buildings and rooms. The user must be able to create a building and subsequently insert rooms into it. However, what I tried to do seems not to achieve it:
JSFiddle of what I have done so far.
js
new Vue({
el: '#vue-app',
data: {
buildings: []
},
computed: {
buildingCount() {
return this.buildings.length
},
getBuildingRoomsLength(section) {
return this.buildings.rooms.length
}
},
methods: {
addNewRoomToBuilding(buildingId, newRoom) {
if(newRoom !== undefined) { this.buildings[parseInt(buildingId)-1].rooms.push(newRoom.title)
console.log(this.buildings[parseInt(buildingId)-1])
}
},
addNewBuilding() {
this.buildings.push({
id: this.buildings.length+1,
rooms: []
})
},
deleteTodo(todo) {
this.todos.$remove(todo)
}
}
});
I am not sure how to make it work. A couple of the things I have noticed is that the room model now is same for all buildings and I have to change it according to the buildingId, however, I can't figure it out yet. Could you please assist me how to do this.
Make your model unique for each item in the buildings array by appending the building id to the end of the name.
So the model name becomes v-model="newRoom[building.id]"
And pass the same into your method addNewRoomToBuilding(building.id, newRoom[building.id])
Related
The Problem
I have a "products" array with multiple objects. Each product object contains the property "price". I want to watch this property in each product for possible changes. I am using this to calculate a commission price when the user changes the price in an input box.
My products array looks like this;
[
0: {
name: ...,
price: ...,
commission: ...,
},
1: {
name: ...,
price: ...,
commission: ...,
},
2: {
name: ...,
price: ...,
commission: ...,
},
...
...
...
]
My code
I've tried this, but it doesn't detect any changes except for when the products are first loaded;
watch : {
// Watch for changes in the product price, in order to calculate final price with commission
'products.price': {
handler: function (after, before) {
console.log('The price changed!');
},
deep : true
}
},
The products are loaded like this;
mounted: async function () {
this.products = await this.apiRequest('event/1/products').then(function (products) {
// Attach reactive properties 'delete' & 'chosen' to all products so these can be toggled in real time
for (let product of products) {
console.log(product.absorb);
Vue.set(product, 'delete', false);
Vue.set(product, 'chosen', product.absorb);
}
console.log(products);
return products;
})
}
Other questions I've looked at
Vue.js watching deep properties
This one is trying to watch a property that does not yet exist.
VueJs watching deep changes in object
This one is watching for changes in another component.
You can't really deep-watch products.price, because price is a property of individual product, not the products array.
Declarative watchers are problematic with arrays, if you attempt to use an index in the watch expression, e.g products[0].price, you get a warning from Vue
[Vue warn]: Failed watching path: “products[0].price”. Watcher only accepts simple dot-delimited paths. For full control, use a function instead.
What this means is you can use a programmatic watch with a function, but it's not explained all that well.
Here is one way to do it in your scenario
<script>
export default {
name: "Products",
data() {
return {
products: []
};
},
mounted: async function() {
this.products = await this.apiRequest('event/1/products')...
console.log("After assigning to this.products", this.products);
// Add watchers here, using a common handler
this.products.forEach(p => this.$watch(() => p.price, this.onPriceChanged) );
// Simulate a change
setTimeout(() => {
console.log("Changing price");
this.products[0].price= 100;
}, 1000);
},
methods: {
onPriceChanged(after, before) {
console.log(before, after);
}
}
};
</script>
Here is my test Codesandbox (I use color instead of price because there's no price in the test api)
I got the following "problem". I am used to having an API like that.
/users
/users/{id}
The first one returns a list of users. The second just a single object. I would like the same with GraphQL but seem to fail. I got the following Schema
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
users: {
type: new GraphQLList(userType),
args: {
id: {type: GraphQLString}
},
resolve: function (_, args) {
if (args.id) {
return UserService.findOne(args.id).then(user => [user]);
} else {
return UserService.find()
}
}
}
}
})
});
How can I modify the type of users to either return a List OR a single object?
You shouldn't use one field for different purposes. Instead of that, make two fields. One for single object and another for list of objects. It's better practice and better for testing
fields: {
user: {
type: userType,
description: 'Returns a single user',
args: {
id: {type: GraphQLString}
},
resolve: function (_, args) {
return UserService.findOne(args.id);
}
},
users: {
type: new GraphQLList(userType),
description: 'Returns a list of users',
resolve: function () {
return UserService.find()
}
}
}
The above answer is correct, the usual approach is to add singular and plural form of queries. However, in large schema, this can duplicate a lot of logic and can be abstracted a little bit for example with Node interface and node, nodes queries. But the nodes query is usually applied with ids as argument (in Relay viz node Fields), but you can build your own abstracted way for fetching so that you have just nodes with some argument for type and based on that you can say what type of list to fetch. However, the simpler approach is to just duplicate the logic for every type and use singular and plural form of query and do the same type of queries as above or in this code snippet for every type. For more detail explanation on implementing GraphQL list modifiers in queries or even as an input for mutations. I just published the article on that.
Sorry if I'm not getting the terminology right. Here's what I have currently my MongoDB user docs db.users:
"liked" : [
"EBMKgrD4DjZxkxvfY",
"WJzAEF5EKB5aaHWC7",
"beNdpXhYLnKygD3yd",
"RHP3hngma9bhXJQ2g",
"vN7uZ2d6FSfzYJLmm",
"NaqAsFmMmnhqNbqbG",
"EqWEY3qkeJYQscuZJ",
"6wsrFW5pFdnQfoWMs",
"W4NmGXyha8kpnJ2bD",
"8x5NWZiwGq5NWDRZX",
"Qu8CSXveQxdYbyoTa",
"yLLccTvcnZ3D3phAs",
"Kk36iXMHwxXNmgufj",
"dRzdeFAK28aKg3gEX",
"27etCj4zbrKhFWzGS",
"Hk2YpqgwRM4QCgsLv",
"BJwYWumwkc8XhMMYn",
"5CeN95hYZNK5uzR9o"
],
And I am trying to migrate them to a new key that also captures the time that a user liked the post
"liked_time" : [
{
"postId" : "5CeN95hYZNK5uzR9o",
"likedAt" : ISODate("2015-09-23T08:05:51.957Z")
}
],
I am wondering if it might be possible to simply do this within the MongoDB Shell with a command that iterates over each user doc and then iterates over the liked array and then updates and $push the new postId and time.
Or would it be better to do this in JavaScript. I am using Meteor.
I almost got it working for individual users. But want to know if I could do all users at once.
var user = Meteor.users.findOne({username:"atestuser"});
var userLiked = user.liked;
userLiked.forEach(function(entry) {
Meteor.users.update({ username: "atestuser" },
{ $push: { liked_times: { postId: entry, likedAt: new Date() }}});
console.log(entry);
});
Still a bit of a newbie to MongoDB obviously......
Here is something i made real quick you should run this on the server side just put it into a file e.g. "migrate.js" in root meteor and run the meteor app
if (Meteor.isServer) {
Meteor.startup(function () {
var users = Meteor.users.find().fetch();
users.forEach(function (doc) {
liked.forEach(function (postId) {
Meteor.users.update(doc._id, { $push: { liked_times: { postId: postId, likedAt: new Date() } } });
});
});
console.log('finished migrating');
});
}
p.s I didn't test it
If this is a one time migration i would do something like this in a one time js script.
Get all users
Iterate over each user
Get all likes
Iterate over them, get likedAt
var liked_times = _.collect(likes, function (likeId) {
return {
'postId' : likeId,
'likedAt': // get post liked time from like id.
}
});
Insert the above in the collection of choice.
Note:
The above example makes use of lodash
I would rather just save likedAt as a timestamp.
I am trying to populate references nested within other references. I have it working but it seems kinda hacky and was wondering if there is any other way to accomplish this:
return Q.ninvoke(BoardSchema, 'find', {'_id': id}).then(function(board) {
return Q.ninvoke(BoardSchema, 'populate', board, {path: 'lanes'}).then(function(board){
return Q.ninvoke(LaneSchema, 'populate', board[0].lanes, {path: 'cards'}).then(function(lanes){
board.lanes = lanes;
return board;
});
});
});
Is there some method to populate all references, or return the second populate as part of the board call without manually setting it like I am now?
You should be able to populate multiple to populate nested documents like so:
Item.find({}).populate('foo foo.child').exec(function(err, items) {
// Do something here
});
This requires that refs are setup in the Schema definitions.
If this doesn't work, which to be honest is most of the times for some reason, you can chain your finds. But this doesn't differ much from your code.
Item.find({}).populate('foo').exec(function(err, items) {
Item.find(items).populate('bar').exec(function(err, items) {
// Even more nests if you like
});
});
Based on the response of Gideon
Item.find({ _id: id})
.populate({
path: 'foo',
model: 'FooModel',
populate: {
path: 'child',
model: 'ChildModel'
}
})
.exec(function(err, items) {
// ...
});
I'm currently looking into the Twitter-API - specifically the daily trends-API (http://search.twitter.com/trends/current.json).
Example return from Twitter
{
trends: {
2009-11-19 14:29:16: [
{
name: "#nottosayonfirstdate",
query: "#nottosayonfirstdate"
},
{
name: "New Moon",
query: ""New Moon""
},
{
name: "#justbecause",
query: "#justbecause"
}
]
}
}
I wonder how I can return the values within there without knowing the exact date at the time of the call, since it won't be possible to synchronise the client-time with the server-time.
Normally, I'd go for trends.something[1].name to get the name, but since the timestamp will change all the time, how can I get it when trends is no array?
you can use this:
for (var i in trends) {
alert (i); // "2009-11-19 14:29:16"
alert (trends[i][0].name); // "#nottosayonfirstdate"
}