I would like to use the Angular directives concept to display a popupwindow for a child node of a tree view upon a Rightclick event. Below is my sample code:
Tree.html
<div
data-angular-treeview="true"
data-tree-model="roleList"
data-node-id="roleId"
data-node-label="roleName"
data-node-children="children"
data-ng-rigtclick="onItemRightClick()"
data-node-children="children">
</div>
treeViewcontroller.js
$scope.roleList1 = [
{ "roleName" : "User", "roleId" : "role1", "children" : [
{ "roleName" : "subUser1", "roleId" : "role11", "children" : [] },
{ "roleName" : "subUser2", "roleId" : "role12", "children" : [
{ "roleName" : "subUser2-1", "roleId" : "role121", "children" : [
{ "roleName" : "subUser2-1-1", "roleId" : "role1211", "children" : [] },
{ "roleName" : "subUser2-1-2", "roleId" : "role1212", "children" : [] }
]}
]}
]},
{ "roleName" : "Admin", "roleId" : "role2", "children" : [] },
{ "roleName" : "Guest", "roleId" : "role3", "children" : [] }
];
Treeview.js
scope.onItemRightClick= function(val)
{
alert(val.roleName);
}
How can I achieve this?
In order to achieve a right click, you have to write a custom directive which will catch the event for you.
Here an example:
Markup
<div id="name" ng-controller='myController'>
<button name="button" my-right-click='test()'>my button</button>
</div>
The directive
app.directive('myRightClick', function($parse) {
return {
scope: false,
restrict: 'A',
link: function(scope, element, attrs) {
var fn = $parse(attrs.myRightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
}
}
});
The controller
app.controller('myController', function($scope) {
$scope.test = function() { // method is passed in by attribute
console.log('hello from controller');
};
});
angular-treeview directive doesn't have right click attribute exposed.
You can refer Angular treeview git repository.
If you need this feature, you can start introducing your custom attribute in existing directive and push your changes back to git. It's up to you.
I tried this however the event console.log() is being triggered X amount of times based on nodes level.
So lets say i clicked on a node 4 levels deep.
It will console.log() 4 times iterating through each nodes parent.
Related
I'm updating the age and name of a character with a specific _id from an array of characters that is inside a document of model Drama.
The document I'm working with:-
{
"_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
"characters" : [
{
"_id" : ObjectId("619fdac5a03c8b10d0b8b13c"),
"age" : "23",
"name" : "Vinay",
},
{
"_id" : ObjectId("619fe1d53810a130207a409d"),
"age" : "25",
"name" : "Raghu",
},
{
"_id" : ObjectId("619fe1d53810a130207a502v"),
"age" : "27",
"name" : "Teju",
}
],
}
So to update the character Raghu I did this:-
const characterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": characterObj,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
This updated the character but it also assigned a new ObjectId to the _id field of the character. So, I'm looking for ways on how to prevent the _id update.
Also, I know I can set the individual fields of character instead of assigning a whole new object to prevent that but it will be very tedious if my character's object has a lot of fields.
//Not looking to do it this way
$set: {
"characters.$.age": characterObj.age,
"characters.$.name": characterObj.name,
},
Thanks.
I found something here, just pre define a schema (a blueprint in a way) that affects the id
var subSchema = mongoose.Schema({
//your subschema content
},{ _id : false });
Stop Mongoose from creating _id property for sub-document array items
Or I would say, when you create a character assign it a custom id from the start, that way it will retain that id throughout.
I'm leaving this question open as I would still like to see a simpler approach. But for now, I did find one easy alternative solution for this issue which I'm will be using for some time now until I find a more direct approach.
In short - Deep merge the new object in the old object using lodash and then use the new merged object to set field value.
For example, let's update the character Raghu from my question document:-
First install lodash(Required for deep merging objects) using npm:
$ npm i -g npm
$ npm i --save lodash
Import lodash:
const _ = require("lodash");
Now update the character Raghu like this:-
const newCharacterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.findById(
{ _id: req.drama._id, "characters._id": characterId },
"characters.$",
function(err, dramaDocWithSpecificCharacter) {
console.log(dramaDocWithSpecificCharacter);
// ↓↓↓ console would log ↓↓↓
// {
// "_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
// "characters" : [
// {
// "_id" : ObjectId("619fe1d53810a130207a409d"),
// "age" : "25",
// "name" : "Raghu",
// }
// ],
// }
const oldCharacterObj = dramaDocWithSpecificCharacter.characters[0];
const mergedCharacterObjs = _.merge(oldCharacterObj, newCharacterObj);
// _.merge() returns a deep merged object
console.log(mergedCharacterObjs);
// ↓↓↓ console would log ↓↓↓
// {
// _id: 619fe1d53810a130207a409d,
// age: "26",
// name: "Dr. Raghu",
// };
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": mergedCharacterObjs,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
Note: We can also use the native Object.assign() or … (spread operator) to merge objects but the downside of it is that it doesn’t merge nested objects which could cause issues if you later decide to add nested objects without making changes for deep merge.
You can pass your payload or request body like this if we provide _id it will prevent update to nested document
"characters" : [
{
"_id" : "619fdac5a03c8b10d0b8b13c",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a409d",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a502v",
"age" : "updated value",
"name" : "updated value",
}
],
It works for me for bulk update in array object
I have developed a code where I am able to retrieve all videos from filesystem whose file path is stored in mongo database:
app.get("/myvideos", function(request, result){
database.collection("videos").find({}).toArray(function(error, videos){
result.render("myvideos", {
"isLogin": request.session.user_id ? true : false,
"videos": videos
});
});
});
the above code is retreiving the files properly. However I wish to retrieve files of a particular user. My database schema is:
{
"_id" : ObjectId("60914d02185c2a08add68fa2"),
"user" : {
"_id" : ObjectId("60914c73185c2a08add68fa0"),
"name" : "Saumitra Lele",
"image" : "",
"subscribers" : 0
},
"filePath" : "public/videos/1620135170700-bigbuck.mp4",
"thumbnail" : "public/thumbnails/1620135170700-Photo - Saumitra Lele.jpeg",
"title" : "First video uploaded",
"description" : "First video uploaded by Saumitra Lele",
"tags" : "Saumitra Lele video",
"category" : "Technology",
"createdAt" : 1620135170708,
"minutes" : 0,
"seconds" : 32,
"hours" : 0,
"watch" : 1620135170708,
"views" : 0,
"playlist" : "",
"likers" : [ ],
"dislikers" : [ ],
"comments" : [ ]
}
However when I try to retrieve files of a particular user like so it doesn't work:
app.get("/myvideos", function(request, result){
database.collection("videos").find({user:{_id:{$in:[user._id]}).toArray(function(error, videos){
result.render("myvideos", {
"isLogin": request.session.user_id ? true : false,
"videos": videos
});
});
});
I got the solution. This worked:
When user session got logged in, I created a new variable to store user name like so:
request.session.user_name = user.name
Then used the above for comparison as:
app.get("/myvideos", function(request, result){
//var logInId = request.session.user_id;
database.collection("videos").find({"user.name":request.session.user_name}).sort({
"createdAt": -1
}).toArray(function(error, videos){
result.render("myvideos", {
"isLogin": request.session.user_id ? true : false,
"videos": videos
});
});
});
And the above worked properly!
I am currently building a cart system on my mongodb ecommerce app. I need help on how to query and compare array.
here document of cart:
{
"_id" : ObjectId("5d0531e27c8fa1029017ea20"),
"user" : ObjectId("5d0371319315c715fc34b0b0"),
"active" : true,
"item" : [
{
"product" : ObjectId("5d013eb63a2bdd11a46c8dd3"),
"option" : [
{
"name" : "Ukuran",
"value" : "Biru"
}
],
"quantity" : 1
},
{
"product" : ObjectId("5d013eb63a2bdd11a46c8dd3"),
"option" : [
{
"name" : "Ukuran",
"value" : "Biru"
}
],
"quantity" : 1
}
],
"created_at" : ISODate("2019-06-15T17:58:58.762Z"),
"updated_at" : ISODate("2019-06-15T17:59:13.334Z"),
"__v" : 0
}
I want to compare object of item.option field, so my cart system is if cart on database have same object option i will add quantity, otherwise push new object to item.
so current I am not asking on how to implement my cart system, but I want to compare each item.option object
I've already tried this
const cart = await CartModel.find({
"item.option": option
})
and get error Error: Query filter must be an object, got an array
Solved by myself, after many experiment finally i combine $in and $elemMatch for compare each array of object
// this is will dynamic
const optionArray = [
{
"name": "Warna",
"value": "Biru"
},
{
"name": "Ukuran",
"value": "XL"
}
]
const compareOptionQuery = []
for (let i = 0; i < optionArray.length; i++) {
compareOptionQuery.push({
$elemMatch: {
...option[i]
}
})
}
const cart = await CartModel.aggregate([
{
$and: [
{
_id: cartId,
user: userId
},
{
'item.option': {
$in: [...compareOptionQuery]
}
}
]
}
])
The issue with your implementation is that in CartModel.find("item.option": option) your filter (first parameter) encounters an array instead of an object.
This is because you are trying to call for an object option from item, however option is as element of an array field item.
If you wish to access item from an array field, you must specify conditions on the elements in the array field item using {<array field>: {<operator1>: <value1>}} like so:
CartModel.find({
item: {$option: option}
})
I have data in mongodb like this:
{
"_id" : ObjectId("55a12bf6ea1956ef37fe4247"),
"tempat_lahir" : "Paris",
"tanggal_lahir" : ISODate("1985-07-10T17:00:00.000Z"),
"gender" : true,
"family" : [
{
"nama" : "Robert Deniro",
"tempat_lahir" : "Bandung",
"tanggal_lahir" : ISODate("2015-07-09T17:00:00.000Z"),
"pekerjaan" : "IRT",
"hubungan" : "XXX",
"tanggungan" : false,
"_id" : ObjectId("55a180f398c9925299cb6e90"),
"meta" : {
"created_at" : ISODate("2015-07-11T20:59:25.242Z"),
"created_ip" : "127.0.0.1",
"modified_at" : ISODate("2015-07-12T15:54:39.682Z"),
"modified_ip" : "127.0.0.1"
}
},
{
"nama" : "Josh Groban",
"tempat_lahir" : "Jakarta",
"tanggal_lahir" : ISODate("2015-06-30T17:00:00.000Z"),
"pekerjaan" : "Balita",
"hubungan" : "Lain-Lain",
"tanggungan" : true,
"_id" : ObjectId("55a29293c65b144716ca65b2"),
"meta" : {
"created_at" : ISODate("2015-07-12T16:15:15.675Z"),
"created_ip" : "127.0.0.1"
}
}
]
}
when i try to find data in sub-document, with this code:
person.findOne({ _id: req.params.person, {'family.nama': new RegExp('robert', 'gi') }}, function(err, data){
// render code here
});
It show all data in Family Data,
Can we fetch or display a data only match with criteria/keyword, for example only "Robert Deniro" row
Thank You
In 'regular' MongoDB, you can use the $ operator for that. I'm not sure if it works with Mongoose, but it's worth a try:
person.findOne({
_id : req.params.person,
'family.nama' : new RegExp('robert', 'gi')
}, {
// Only include the subdocument(s) that matched the query.
'family.$' : 1
}, function(err, data){
// render code here
});
If you need any of the properties from the parent document (tempat_lahir, tanggal_lahir or gender; _id will always be included), you need to add them to the projection object explicitly.
One caveat: the $ operator will only return the first matching document from the array. If you need it to return multiple documents, you can't use this method and (AFAIK) have to postprocess the results after they are returned from the database.
It solved with this code:
var options = {
family: {
$elemMatch: { nama: req.query.keyword }
},
};
person.findOne({ _id: req.params.person, 'family.nama': keyword }, options, function(err, data){
//render code here
});
Thanks to #hassansin & #robertklep
This is my entry in database in mongodb which is of type object in schema
"_id" : ObjectId("5539bed4b417d75d1fee5df7"),
"favMovies" : {
"alternate_ids" : {
"imdb" : "2820852"
},
"studio" : "Universal Pictures",
"abridged_directors" : [
{
"name" : "James Wan"
}
],
"abridged_cast" : [
{
"characters" : [
"Dominic Toretto"
],
"id" : "162652472",
"name" : "Vin Diesel"
},
{
"characters" : [
"Brian O'Conner"
],
"id" : "162654234",
"name" : "Paul Walker"
},
{
"characters" : [
"Louie Tran"
],
"id" : "162684066",
"name" : "Tony Jaa"
},
{
"characters" : [
"Deckard Shaw"
],
"id" : "162653720",
"name" : "Jason Statham"
},
{
"characters" : [
"Luke Hobbs"
],
"id" : "770893686",
"name" : "Dwayne \"The Rock\" Johnson"
}
],
"synopsis" : "Continuing the global exploits in the unstoppable franchise built on speed, Vin Diesel, Paul Walker and Dwayne Johnson lead the returning cast of Fast & Furious 7. James Wan directs this chapter of the hugely successful series that also welcomes back favorites Michelle Rodriguez, Jordana Brewster, Tyrese Gibson, Chris \"Ludacris\" Bridges, Elsa Pataky and Lucas Black. They are joined by international action stars new to the franchise including Jason Statham, Djimon Hounsou, Tony Jaa, Ronda Rousey and Kurt Russell.",
"ratings" : {
"audience_score" : 88,
"audience_rating" : "Upright",
"critics_score" : 82,
"critics_rating" : "Certified Fresh"
},
"release_dates" : {
"theater" : "2015-04-03"
},
"critics_consensus" : "",
"runtime" : 140,
"mpaa_rating" : "PG-13",
"genres" : [
"Mystery & Suspense",
"Action & Adventure"
],
"year" : 2015,
"title" : "Furious 7",
"id" : 771354922
},
"username" : "punk",
"__v" : 0
}
In my Node JS code I use the following query
app.delete('/favMovies/:user/:movid',function(req, res){
var user = req.params.user;
var mid = req.params.movid;
console.log(mid);
console.log(user);
MovModel.find({username:user,'favMovies.id':mid}, function (err, doc) {
doc.remove();
MovModel.find({username: user},function (err, data) {
res.json(data);
});
});
});
In the above snippet mid is movie id. For the above entry in database mov
"id" : 771354922
and user is username but I am getting following error for my query which is working fine in mongo client.
/Users/pankajtripathi/Documents/ECLIPSE-FILES/MyProject/server.js:132
doc.remove();
^
TypeError: Cannot read property 'remove' of null
at /Users/pankajtripathi/Documents/ECLIPSE-FILES/MyProject/server.js:132:5
at /Users/pankajtripathi/Documents/ECLIPSE-FILES/MyProject/node_modules/mongoose/lib/query.js:1169:16
at /Users/pankajtripathi/Documents/ECLIPSE-FILES/MyProject/node_modules/mongoose/node_modules/kareem/index.js:103:16
at process._tickCallback (node.js:355:11)
You should use findOneAndRemove()
MovModel.findOneAndRemove({username:user,'favMovies.id':mid}, function (err, doc) {
if (err) console.log(err);
res.json(doc);
}
Finds a matching document, removes it, passing the found document (if
any) to the callback. Executes immediately if callback is passed.
I changed the query and its working fine now.
MovModel.findOneAndRemove({username:user,_id:mid}, function (err, doc) {
console.log(doc);
MovModel.find({username: user},function (err, data) {
res.json(data);
});
});