GORM - MongoDB does not save an item on a parent class set - javascript

Here are the domain classes
class Settings {
static constraints = {
uid(nullable: false, unique: true)
data()
}
static hasMany = [items: Item]
Map data
}
class Item{
static constraints = {
name()
email()
approved()
}
static mapping = {
email index: true, indexAttributes: [unique: true]
}
String name
String email
Boolean approved = false;
}
Basically there are many Settings objects that have many items (see illustration below):
Now I'm finding and updating an item like so:
...
def item = (Item)Item.findByEmail(email);
if (!item.approved) {
item.approved = true;
item.save(flush: true);
}
...
Not Saved what am I missing here?

MongoDB does not except null fields by default. Once I've added a value all works well:
...
def item = (Item)Item.findByEmail(email);
if (!item.approved) {
item.approved = true;
item.name = "MyName";
item.save(flush: true);
}
...

Related

Using custom Angular Material Sort?

I want to create a dropdown (or mat-select) to use as a sorting mechanism instead of the Angular Material Sort Header. So, if I for example click on the 'username' inside the dropdown, I want the table to sort by the username (instead of clicking on the header).
How can I do it? Any documentation online on how to achieve this?
Thank you for any help.
As required, I attach some code:
ngOnInit(): void {
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(""),
map((value) => this._filter(value))
);
}
ngAfterViewInit() {
this.providersAdmin.sort = this.sort;
}
getAllAdmins() {
this.isLoading = true;
this.homeService.getAllAdmins().subscribe(
(response) => {
this.admins = response;
this.providersAdmin = new MatTableDataSource(this.admins);
this.isLoading = false;
},
(error) => {}
);
}
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
console.log(event);
}
The sortTableBy method is the one I found on here but nothing happens.
I added matSort on the mat-table and I added mat-sort-header on the header cell.
EDIT:
Hi, I managed to fix the problem by writing the following:
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
this.providersAdmin.sort = this.sort;
}
There is an example for you:
Exmaple
Your sort function has a wrong implementation, this is work for me:
sortData(fieldName: string) {
if (!fieldName) {
return;
}
const sortState: MatSortable = {
id: fieldName,
start: 'desc',
disableClear: true
};
this.sort.sort(sortState);
}
I am going to set up an example which you can adapt easily:
compare(a: number | string, b: number | string, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
sortData() {
let isAsc = this.sort.direction != "" ?
event.direction == SortDirection.asc :
true;
let data = this.dataSource.data.slice();
data.sort((a, b) => {
switch (this.myChosenSort) {
case 'healthCareCenterName':
return this.compare(a.healthCareCenterName, b.healthCareCenterName, isAsc);
case 'address':
return this.compare(a.address, b.address, isAsc);
case 'contact':
return this.compare(a.contact, b.contact, isAsc);
default:
return 0;
}
});
this.dataSource = new MatTableDataSource<ServiceProviderTable>(data);
}
To change the sort.direction you need to play around a little bit with the code, maybe directly from the dropdown and hardcoding the isAsc when calling the compare method, depending on the value of the this.myChosenSort.

Adonisjs How to cast boolean to false and true in a model instead of 0 or 1?

This is my table field (open) but in response, it returns 0 1 but I want true false instead of 0 1
I am using adonis MySQL
table.boolean('open').notNullable().defaultTo(true).comment('true = open, false = close')
const Model = use('Model')
class Markup extends Model {
static boot() {
super.boot()
this.addTrait('#provider:Lucid/SoftDeletes')
}
static getColumns() {
return ['assignee_id', 'editor_details', 'visibility', 'image_url', 'priority', 'open']
}
comments() {
return this.hasMany('App/Models/Comment', 'id', 'markup_id')
}
assignee() {
return this.belongsTo("App/Models/User", "assignee_id", "id")
}
created_by() {
return this.belongsTo("App/Models/User", 'created_by_id', 'id')
}
resolved_by() {
return this.belongsTo("App/Models/User", 'resolved_by_id', 'id')
}
}
module.exports = Markup
This just a simple fix.
you just have to do this in your column.
#column({ serialize: Boolean })
As #asad-jivani said:
Boolean isn't a distinct datatype in MySQL; it's just a synonym for tinyint. what you can do is write an after hook in your model to convert 1/0 to true/false.
In these cases I use a property of laravel/lumen calls $casts, this is specified in the model.
here is an example without the $cast to is_draft field.
JSON Response:
{
"areas": [
{
"id": 1,
"is_draft": 1,
"title": "Example"
}
]
}
to convert the filed is_draft to true or false I just added this in my model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Area extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $table = 'areas';
protected $guarded = [];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $casts = [
'is_draft' => 'boolean',
];
}
and here is the parsed JSON Response:
{
"areas": [
{
"id": 1,
"is_draft": true,
"title": "Example"
}
]
}
I hope this helps you 👍

Waiting for data to be retrieved from local storage

When the gallery is loaded in my gallery app, a functioned is called to check whether the current user has liked an image in an array of images. If the user has liked the image, a solid heart icon is displayed on top of the image. If the hasn't liked the image, a heart outline icon is displayed.
Within the function, we get the current users index in an array of users. This information is retrieved from a user object which is stored in the browsers local storage. The problem I am having is that the function is called before the user data is retrieved from the local storage and I'm not sure how to get around this problem?
Here is the relevant html from the gallery-list component:
<div class="gallery-container">
<div
class="image-container"
*ngFor="let image of galleryList">
<div *ngIf="checkLike(image.imagePath)" class="heart-icon-container">
<ion-icon name="heart" class="heart-icon" (click)="updateLike(image.imagePath)"></ion-icon>
</div>
<div *ngIf="!checkLike(image.imagePath)" class="heart-icon-container">
<ion-icon name="heart-outline" class="heart-icon" (click)="updateLike(image.imagePath)"></ion-icon>
</div>
</div>
</div>
Here is the relevant code from the gallery-list ts file:
currUsersIndex: number;
ngOnInit() {
this.currUsersIndex = this.usersService.getCurrUserArrIndex();
}
checkLike(imageUrl: string): boolean {
const users = this.usersService.getUsers();
if(this.isLoggedIn) {
const FavouritesList = users[this.currUsersIndex].likes;
if(FavouritesList.includes(imageUrl)) {
return true;
} else {
return false;
}
}
}
Finally, this is where the users index is retrieved from the users.service file:
private users: User[] = [];
getCurrUserArrIndex() {
const usersEmail = this.getCurrentUser().email;
const users = this.getUsers();
const usersIndex = users.findIndex(user => user.email === usersEmail);
return usersIndex;
}
getCurrentUser() {
const userData: {
email: string,
id: string,
_token: string,
_tokenExpirationDate: string
} = JSON.parse(localStorage.getItem('authUserData'));
return userData;
}
getUsers() {
return this.users.slice();
}
Please note, the users array is initially set to empty but it is set with the users from a firebase database when the app initializes.
The error message I am receiving in the console is: "can't access property "likes", users[this.currUsersIndex] is undefined"
I think, the issue here is that this.currUsersIndex is not set when you call checkLike() in the HTML.
Alternatively what i could recommend you is that, define a boolean variable in your component as,
checkLike = false;
and then call the function checkLike() after setting currUsersIndex
ngOnInit() {
this.currUsersIndex = this.usersService.getCurrUserArrIndex();
this.checkLike();
}
checkLike(imageUrl: string): boolean {
const users = this.usersService.getUsers();
if(this.isLoggedIn) {
const FavouritesList = users[this.currUsersIndex].likes;
if(FavouritesList.includes(imageUrl)) {
this.checkLike = true;
} else {
this.checkLike = false;
}
}
You must be already having the imageUrl in the component.ts just replace with that.
You don't need to separate things IMO. You can update your code to this, this will be error-proof.
checkLike(imageUrl: string): boolean {
const user = this.usersService.getCurrentUser();
if(user && user.likes) {
const FavouritesList = user.likes;
return FavouritesList.includes(imageUrl);
}
return false
}
getCurrentUser() {
const userData: {
email: string,
id: string,
_token: string,
_tokenExpirationDate: string
} = JSON.parse(localStorage.getItem('authUserData'));
const userEmail = userData.email;
const users = this.getUsers();
return users.find(user => user.email === usersEmail);
}

unable to select all checkboxes in tree using angular2-tree on init

Objective : i have a button named "feed data" so when ever i click it the data will be loaded i mean the tree with checkboxes here my requirement is when ever i click it along with data all the check boxes have to be checked on init i tried using
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
but it is not working below is my code
click(tree: TreeModel) {
this.arrayData = [];
let result: any = {};
let rs = [];
console.log(tree.selectedLeafNodeIds);
Object.keys(tree.selectedLeafNodeIds).forEach(x => {
let node: TreeNode = tree.getNodeById(x);
// console.log(node);
if (node.isSelected) {
if (node.parent.data.name) //if the node has parent
{
rs.push(node.parent.data.name + '.' + node.data.name);
if (!result[node.parent.data.name]) //If the parent is not in the object
result[node.parent.data.name] = {} //create
result[node.parent.data.name][node.data.name] = true;
}
else {
if (!result[node.data.name]) //If the node is not in the object
result[node.data.name] = {} //create
rs.push(node.data.name);
}
}
})
this.arrayData = rs;
tree.selectedLeafNodeIds = {};
}
selectAllNodes() {
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
// firstNode.setIsSelected(true);
}
onTreeLoad(){
console.log('tree');
}
feedData() {
const results = Object.keys(this.data.info).map(k => ({
name: k,
children: this.data.info[k].properties
? Object.keys(this.data.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
feedAnother() {
const results = Object.keys(this.dataa.info).map(k => ({
name: k,
children: this.dataa.info[k].properties
? Object.keys(this.dataa.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
onActivate(event) {
this.selectedDataList.push(event.node.data);
console.log(this.selectedDataList)
}
onDeactivate(event) {
const index = this.selectedDataList.indexOf(event.node.data);
this.selectedDataList.splice(index, 1);
console.log(this.selectedDataList)
}
below is my stackblitz https://stackblitz.com/edit/angular-hrbppy
Use updatedata and initialized event to update the tree view to check all checkboxes.
app.component.html
<tree-root #tree *ngIf ="nodes" [nodes]="nodes" [options]="options" [focused]="true"
(initialized)="onTreeLoad()"
(updateData)="updateData()"
(select)="onActivate($event)"
(deselect)="onDeactivate($event)">
</tree-root>
It'll initiate tree-root component only if nodes variable is available,
then in the initialized and updateData event call selectAllNodes method to select all checkboxes.
app.component.ts
updateData() {
this.selectAllNodes();
}
onTreeLoad(){
this.selectAllNodes();
}
Refer to this slackblitz for working example.
just, in your function feed data call to your function this.selectAllNodes() enclosed in a setTimeout. You can see your forked stackblitz
setTimeout(()=>{
this.selectAllNodes()
})
NOTE: I see in your code you try to control in diferents ways the items selected. I simplified using a recursive function.
In this.treeComp.treeModel.selectedLeafNodeIds we have the items that are changed, so
getAllChecked()
{
const itemsChecked=this.getData(
this.treeComp.treeModel.selectedLeafNodeIds,null)
console.log(itemsChecked);
}
getData(nodesChanged,nodes) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
//in nodesChanged we has object like {1200002:true,123132321:false...}
if (nodesChanged[node.id]) //can be not changed, and then it's null because
//it's not in object or can be changed to false
data.push({id:node.id,name:node.name})
//or data.push(node.name); //if only need the "name"
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children)]
}
);
return data
}
Updated I updated the function getData to include the "parent" of the node, but looking the code of #Raghul selvam, his function like me more than mine.
getData(nodesChanged,nodes,prefix) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
if (nodesChanged[node.id])
data.push(prefix?prefix+"."+node.name:node.name)
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children,prefix?prefix+"."+node.name:node.name)]
}
);
return data
}
And call it as
this.getData(this.treeComp.treeModel.selectedLeafNodeIds,null,"")
You could add this in your onTreeLoad function. You could add a boolean flag(treeLoaded) for tracking if the tree has loaded or not.
onTreeLoad(tree){
this.selectAllNodes();
this.treeLoaded = true;
}

Execute overrided static function from base class

I want to execute an overriden static method from the base class without being instantiated.
I want to use an MVC like pattern on an app I'm building and I've created a class named Model that connects to a database and gets the object, it has some static methods that I'm overriding such as the table name (tableName). The problem is that this method must be called from static methods.
From the base class all works like a charm, the problem is when I use other class that extends the base one.
Here's the code:
class Model {
static get tableName() {
return this.name;
}
static get primaryKey() {
return "id";
}
static get columns() {
return [];
}
static id(id) {
return new Promise((resolve, reject) => {
Model.get(Model.primaryKey, id)
.then(models => {
resolve(models[0]);
});
});
}
static get(columnName, value, compareSymbol) {
return new Promise((resolve, reject) => {
if (!compareSymbol) {
compareSymbol = "=";
}
let sql = `select * from ${this.tableName}`,
params = [];
if (typeof columnName !== "undefined") {
sql += ` where ${columnName} ${compareSymbol} ?`;
params = [columnName, value];
}
console.log(sql, params);
});
}
constructor(params) {
this.target = new.target
for (let name in params) {
if (Model.primaryKey == name) {
this[`#${name}`] = params[name];
} else {
this.set(name, params[name]);
}
}
}
set(name, value) {
if (name != this.target.primaryKey && this.target.columns.indexOf(name) > -1) {
this[`#${name}`] = value;
}
}
get(name) {
return this[`#${name}`];
}
executeSql(sql, variables) {
console.log(sql, variables);
}
update() {
let columns = this.target.columns.slice(),
values = [],
sql;
sql = `update ${this.target.tableName} set ${columns.join("=?, ")}=? where ${this.target.primaryKey} = ${this.get(this.target.primaryKey)}`;
for (let i = 0; i < columns.length; i++) {
values.push(this.get(columns[i]));
}
return this.executeSql(sql, values);
}
}
// from this line down is other different file
class Directory extends Model {
static get tableName() {
return "directories";
}
static get columns() {
return [
"name",
"path",
"recursive"
];
}
}
// shows "from Model" expected "from directories"
Directory.id(2);
// work as expected
let d1 = new Directory({
id: 1,
name: "name",
path: "path",
recursive: false
});
d1.update();
If called without being instantiated it returns "Model", is there any way to get the overriden value from the base class?

Categories