update member of LitElement for the refreshing of the rendering? - frontend

My extended LitElement class has variable members, the values of which could be changed at any time. The rendering depends on several of them. I'd like know, which update() member function should be overwritten for the purpose of refreshing the rendering updon every update of any modifications to the varialbe members.

You don't need to, LitElement manages that for you, and queues up multiple property updates to become one DOM update/reflow.
These are called reactive properties.
You can set them with a properties meta property getter:
static get properties() {
return {
mode: {type: String},
data: {attribute: false},
};
}
Or in Typescript with decorators:
#property({type: String})
mode: string;
​
#property({attribute: false})
data = {};
If these properties are internal you can use #state instead of #property.
Changing the value of any of these LitElement properties will cause render() to be called again. If you set multiple ones render() won't fire until you finish or await something.

Related

How to get the types of a prisma model and place them in an array in Nestjs?

I have a Nestjs app using Prisma. For one given model, I want to send via controller the model attributes with its types, so that I can generate a form in the front end. What is the best way to do it?
What I have:
A prisma model and its corresponding DTO class in Nestjs:
// create-job-offer.dto.ts
import { IsOptional, IsNumber } from 'class-validator';
export class CreateJobOfferDto {
#IsNumber()
#IsOptional()
mentorId: number;
#IsNumber()
#IsOptional()
companyId: number;
}
I want to be able to send to my FE something like:
[{key: 'mentorId', type: 'number'}, {key :'companyId', type: 'number'}]
So that I can create a form, and for every input have the correct type. For example, if type is boolean, I'd generate a checkbox.
TypeScipt type is not an object. You can not treat typescript types as objects - ie can't iterate through them etc.
The standard way to share types is to use a #types package. TSLang website has a good explanation on how that works.
https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html
However, at my company, we will only use #types packages for packages that get published to npm because it takes too long to create one.
We will have a types.ts file for each module. Say, a custom-form.types.ts file, which may have the following code in it.
export interface MentorForm {
mentorId: number;
companyId: number | undefined;
mentorName: string;
...
}
And then we will share this file with the frontend development team. Because of how typescript works, you can not iterate through the keys of the interface directly. You can use a transformer to get the keys of an interface in an array.
const KeysOfMentorForm = keys<MentorForm>();
If you want to generate your forms in the front end programmatically, the easiest option would probably be to do something like this.
let mentorA : MentorForm;
let keyAndTypeArray : {key : string, type: string}[] = [];
for(let key of KeysOfMentorForm){
let type : string = (typeOf mentorA[key]).toString();
keyAndTypeArray.push({key : key, type : type})
}
Then you can iterate through the keyAndTypeArray to generate the form.

Models ref each other error: circular dependencies problem

admin.model.ts
import mongoose, { Schema, Document } from 'mongoose';
import UserRole, { IUserRole } from './user-role.model';
export interface IAdmin extends Document {
role: IUserRole;
}
let adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: UserRole
}
});
export default mongoose.model<IAdmin>('Admin', adminSchema);
user-role.model.ts
import { Schema, Document, model } from 'mongoose';
export interface IUserRole extends Document{
updated_by: IAdmin|string;
}
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: Admin
}
})
export default model<IUserRole>('UserRole', userRoleSchema);
MongooseError: Invalid ref at path "updated_by". Got undefined
at validateRef (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/helpers/populate/validateRef.js:17:9)
at Schema.path (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:655:5)
at Schema.add (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:535:14)
at require (internal/modules/cjs/helpers.js:88:18)
[ERROR] 22:25:54 MongooseError: Invalid ref at path "updated_by". Got undefined
Here is my two model how can I solve this type of problem and how to deal with circular dependencies?
You have to put the ref values for UserRole and Admin in '' like this:
const adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: 'UserRole' // Name of the model you are referencing
}
});
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: 'Admin' // <- and here
}
})
While the above answer by #gabriel-lupu is correct, in newer versions it seems another fix is recommended.
Fix:
adding quotes: ref: 'UserRole' // Recommended first fix
adding an arrow function: ref: () => UserRole
Typegoose documentation says the following:
Options ref and type can both also be defined without () =>, but is generally recommended to be used with.
If () => is not used, there can be problems when the class (/ variable) is defined after the decorator that requires it. Section in documentation for reference.
Because of the order classes are loaded and reordered at runtime, this might result in some references being null / undefined / not existing. This is why Typegoose provides the following:
class Nested {
#prop()
public someNestedProperty: string;
}
// Recommended first fix:
class Main {
#prop({ ref: () => Nested }) // since 7.1 arrow functions can be used to
defer getting the type
public nested: Ref<Nested>;
}
// Not recommended workaround (hardcoding model name):
class Main {
#prop({ ref: 'Nested' }) // since 7.0 it is recommended to use "console.log(getName(Class))" to get the generated name once and hardcode it like shown here
public nested: Ref<Nested>;
}
Section of documentation with the above example
Explanation:
The MongooseError: Invalid ref at path "updated_by". Got undefined error seems to be due to a circular dependency.
IAdmin has a role property that references IUserRole.
IUserRole has an updated_by property that references IAdmin.
When admin.model.ts loads it imports user-role.model.ts
user-role.model.ts needs IAdmin for the updated_by property from admin.model.ts
admin.model.ts hasn't finished loading yet resulting in updated_by which references it being undefined.
Relevant explanation from Mozilla about how modules deal with circular dependencies.
In a cyclic dependency, you end up having a loop in the graph.
Usually, this is a long loop. But to explain the problem, I’m going to
use a contrived example with a short loop.
A complex module graph with a 4 module cycle on the left. A simple 2
module cycle on the right.
Let’s look at how this would work with CommonJS modules. First, the
main module would execute up to the require statement. Then it would
go to load the counter module.
A commonJS module, with a variable being exported from main.js after a
require statement to counter.js, which depends on that import
The counter module would then try to access message from the export
object. But since this hasn’t been evaluated in the main module yet,
this will return undefined. The JS engine will allocate space in
memory for the local variable and set the value to undefined.
The message variable will be initialized and added to memory. But
since there’s no connection between the two, it will stay undefined in
the required module.
If the export were handled using live bindings, the counter module would see the correct value eventually. By the time the timeout runs, main.js’s evaluation would have completed and filled in the value.
how module imports work
Both? fixes seem to create a live binding.
Closures can close over imported values as well, which are regarded as live bindings, because when the original value changes, the imported one changes accordingly.
// closureCreator.js
import { x } from "./myModule.js";
export const getX = () => x; // Close over an imported live binding

Before save actions - dynamodb-data-mapper

I'm currently struggling with dynamodb-data-mapper and dynamodb-data-mapper-annotation.
I have a class corresponding to a table and some of the attributes must be recomputed every time we save an object.
I've been able to use marshall and unmarshall for the updatedAt attribute as it's completly independant, but when I need an interaction between several of them I cannot find any successful workaround.
I would like this be something totally internal and a method that I would have to call (better to avoid mistakes later).
Can you see any solution ?
#table('my-class-table')
export class MyClass {
#autoGeneratedHashKey()
readonly id: string;
#attribute({ defaultProvider: () => new Date() })
readonly createdAt: Date;
#attribute({
type: 'Custom',
defaultProvider: () => new Date(),
marshall: (): AttributeValue => ({ S: new Date().toISOString() }),
unmarshall: (persistedValue: AttributeValue): Date => new Date(persistedValue.S!),
})
readonly updatedAt: Date = new Date();
#attribute()
alerts: VehicleAlert[];
// This attribute depends on the alerts attribute
#attribute()
status: string;
}
I'm not sure of your exact use-case (and maybe you already figured it out), but I solved a similar problem by using a getter. For your updatedAt example:
class MyClass {
get updatedAt() {
return new Date().toISOString();
}
}
The data mapper will access the property every time you save the document, and it will update the record in the database with the value it gets from the getter.

Apply a typescript class to a member of a mongoose document

I have a mongoose schema in typescript and I am already using an interface for properties of the document itself. I want to create a class so when I find a document I can immediately call methods. I also want the document to contain members that are instances of other classes and call the methods those members. Is this possible? Here is what I have so far:
models/user.ts:
import mongoose from '../lib/mongoose';
const Schema = mongoose.Schema;
interface IUser extends mongoose.Document {
username: string;
email: string;
hash: string;
};
var userSchema = new Schema({
username: String,
email: String,
hash: String
});
export default mongoose.model<IUser>('User', userSchema);
I don't actually need to do this for my user model but it is the shortest so I thought it would be a good example. Let's say I wanted a method to check if the user's email contains their username.
Here is a simple user class:
class User {
constructor(public username: string, public email: string, public hash: string) {
}
emailUsernameCheck(): boolean {
return this.email.includes(this.username);
}
}
I want to be able to make a call like this:
import User from '../models/user';
User.findOne({username: 'foo'}).emailUsernameCheck();
Let's also say I wanted to give each user a square with a height and width, and the square class has an area method.
Here is a sample square class:
class square {
constructor(public height: number, public width: number) {
}
area(): number {
return this.height * this.width;
}
}
I also want to be able to make a call like this:
import User from '../models/user';
User.findOne({username: 'foo'}).square.area();
I know I can make the classes separately, give them constructors that take objects, and construct the classes that way. I will do this if it is the only way but it means importing the class separately and adds an extra step to every database lookup. It also opens up room for error since the classes are maintained separately from the schema. I would love to handle this all in my model. Is there something I can add to models/user.ts so I can do things like the examples above?
I think is not possible to use a separate Class in mongoose.
You can extend the Document Model schema using instance and statics methods
Or define a Custom Schema Type for your fields using separate classes (you can find a full example in Mongoose advanced custom schema object type).
Btw you can copy methods from an external object and prototype to Schema.* and Schema.methods.*, but the final object won't share the same prototype as the source class (instanceof).

Mongoose 'static' methods vs. 'instance' methods

I believe this question is similar to this one but the terminology is different. From the Mongoose 4 documentation:
We may also define our own custom document instance methods too.
// define a schema
var animalSchema = new Schema({ name: String, type: String });
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
return this.model('Animal').find({ type: this.type }, cb);
}
Now all of our animal instances have a findSimilarTypes method available to it.
And then:
Adding static methods to a Model is simple as well. Continuing with our animalSchema:
// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
return this.find({ name: new RegExp(name, 'i') }, cb);
}
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
console.log(animals);
});
It seems with static methods each of the animal instances would have the findByName method available to it as well. What are the statics and methods objects in a Schema? What is the difference and why would I use one over the other?
statics are the methods defined on the Model. methods are defined on the document (instance).
You might use a static method like Animal.findByName:
const fido = await Animal.findByName('fido');
// fido => { name: 'fido', type: 'dog' }
And you might use an instance method like fido.findSimilarTypes:
const dogs = await fido.findSimilarTypes();
// dogs => [ {name:'fido',type:'dog} , {name:'sheeba',type:'dog'} ]
But you wouldn't do Animals.findSimilarTypes() because Animals is a model, it has no "type". findSimilarTypes needs a this.type which wouldn't exist in Animals model, only a document instance would contain that property, as defined in the model.
Similarly you wouldn't¹ do fido.findByName because findByName would need to search through all documents and fido is just a document.
¹Well, technically you can, because instance does have access to the collection (this.constructor or this.model('Animal')) but it wouldn't make sense (at least in this case) to have an instance method that doesn't use any properties from the instance. (thanks to #AaronDufour for pointing this out)
Database logic should be encapsulated within the data model. Mongoose provides 2 ways of doing this, methods and statics. Methods adds an instance method to documents whereas Statics adds static “class” methods to the Models itself.The static keyword defines a static method for a model. Static methods aren't called on instances of the model. Instead, they're called on the model itself. These are often utility functions, such as functions to create or clone objects. like example below:
const bookSchema = mongoose.Schema({
title: {
type : String,
required : [true, 'Book name required']
},
publisher : {
type : String,
required : [true, 'Publisher name required']
},
thumbnail : {
type : String
}
type : {
type : String
},
hasAward : {
type : Boolean
}
});
//method
bookSchema.methods.findByType = function (callback) {
return this.model('Book').find({ type: this.type }, callback);
};
// statics
bookSchema.statics.findBooksWithAward = function (callback) {
Book.find({ hasAward: true }, callback);
};
const Book = mongoose.model('Book', bookSchema);
export default Book;
for more info: https://osmangoni.info/posts/separating-methods-schema-statics-mongoose/
Well to me it doesn't mean add anythings by adding Mongoose in front of 'static' or even in front 'instance' keyword.
What I believe meaning and purpose of static is same everywhere, even it's also true for an alien language or some sort of driver which represents Model for building the block like another object-oriented programming. The same also goes for instance.
From Wikipedia:
A method in object-oriented programming (OOP) is a procedure associated with a message and an object. An object consists of data and behavior. The data and behavior comprise an interface, which specifies how the object may be utilized by any of various consumers[1] of the object.
Data is represented as properties of the object and behaviors are represented as methods of the object. For example, a Window object could have methods such as open and close, while its state (whether it is opened or closed at any given point in time) would be a property.
Static methods are meant to be relevant to all the instances of a class rather than to any specific instance. They are similar to static variables in that sense. An example would be a static method to sum the values of all the variables of every instance of a class. For example, if there were a Product class it might have a static method to compute the average price of all products.
Math.max(double a, double b)
This static method has no owning object and does not run on an instance. It receives all information from its arguments.[7]
A static method can be invoked even if no instances of the class exist yet. Static methods are called "static" because they are resolved at compile time based on the class they are called on and not dynamically as in the case with instance methods, which are resolved polymorphically based on the runtime type of the object.
https://en.wikipedia.org/wiki/Method_(computer_programming)
As everybody said, use methods when we want to operate on a single document, and we use statics when we want to operate on entire collection. But why?
Let's say, we have a schema:
var AnimalSchema = new Schema({
name: String,
type: String
});
now as mentioned in the docs, if you need to check the similar types of a particular document in the db
you can do:
AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
return this.model('Animal').find({ type: this.type }, cb);
};
Now, this here refers to the document itself. So, what that means is, this document
doesn't knows which model it belongs to, because methods has nothing to do with the model defaultly, It's only for that particular document obj.
But we can make it work with the model, using this.model('anyModelName').
Now why did we used methods in the case of finding similar types of animals?
For finding similar types of animals we must have an animal obj for which we'll find similar types of.
That animal obj we have let's say:
const Lion = await new Animal({name: Lion, type: "dangerous"});
Next, when we find similar types we need the Lion obj first, we must have it.
So simply, whenever we need the help of a particular obj/document for doing something, We'll use methods.
Now here by chance we also need whole model to search slimier types,
although it is not available directly in methods (remember this.modelName will return undefined). we can get it by setting this.model() to our preferred model, in this case Animal.
That's all for methods.
Now, statics has the whole model at its disposal.
1)Let's say you want to calculate the total price (assume the model has a price field) of all Animals you'll use statics [for that you don't need any particular Animal obj, so we won't use method]
2)You want to find the animals which have yellow skin (assume the model has a skin field), you'll use statics. [ for that we don't need any particular Animal obj, so we won't use method]
eg:
AnimalSchema.statics.findYellowSkin = function findSimilarType (cb) {
return this.find({ skin: "yellow" }, cb);
};
Remember, In methods this refers to the model so, this.modelName will return Animal (in our case).
but just like methods, here also we can (but we don't need to) set the model using.
AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
return this.model('Animal').find({ skin: "yellow" }, cb); //just like in methods
};
so as you can see both of statics and methods are very similar.
Whenever you have a document and you need something to do with that,
use methods. Whenever you need to do something with the whole
model/collection, use statics.
Static methods apply to the entire model on which they are defined whereas instance methods apply only to a specific document within the collection.
this in the context of a static method returns the entire model whereas this in the context of an instance method returns the document.
Lets say for example:
const pokemon = new mongoose.Schema({})
pokemon.statics.getAllWithType = function(type){
// Query the entire model and look for pokemon
// with the same type
// this.find({type : type})
}
pokemon.methods.sayName = function(){
// Say the name of a specific pokemon
// console.log(this.name)
}
const pokemonModel = mongoose.model('schema', pokemon)
const squirtle = new pokemonModel({name : "squirtle"})
// Get all water type pokemon from the model [static method]
pokemonModel.getAllWithType("water")
// Make squirtle say its name [instance method]
squirtle.sayName()
Use .statics for static methods.
Use .methods for instance methods.
//instance method
bookSchema.methods.findByType = function (callback) {
return this.model('Book').find({ type: this.type }, callback);
};
// static method
bookSchema.statics.findBooksWithAward = function (callback) {
Book.find({ hasAward: true }, callback);
};
statics are pretty much the same as methods but allow for defining functions that exist directly on your Model.
statics belongs to the Model and methods belongs to an Instance

Resources