How to instantiate model Angular 2 - forms

I'm creating a very basic Angular 2 Application with the purpose of CRUD products. I have a CreateProductComponent but I'm having trouble instantiating the model that I wan't to use on my view. I'm getting this error:
This is my code:
create-product.component.ts
import { Component, OnInit } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { Product } from '../product'
import { ProductService } from '../product.service'
#Component({
moduleId: module.id,
selector: 'app-create-product',
templateUrl: 'create-product.html',
styleUrls: ['create-product.css'],
})
export class CreateProductComponent {
model: new Product(1,'2',1); <- error on this line
constructor(private productService: ProductService) { }
addProduct() {
//this.productService.addProduct(this.product);
}
}
product.ts
export class Product {
constructor(
public ID: number,
public Name: string,
public Price: number
) { }
}
Ideally, I would like to create an empty product, or a product with defaul values so that when a form in the view is filled with the product data this data is passed down to the product. Does anyone know how to solve this?

In typescript terminology ':' means you are declaring a type for a variable you have defined.
'=' means you are assigning a value to your variable.
But here what you did is instead of assigning a value you are trying to declare a value (which is not correct according to syntax)
so either you can directly assign a value like
model = new Product(1,'2',1);
or else you can first declare a type for your variable and then assign a value in either of these two ways (I personally prefer this way)
model: Product = new Product(1,'2',1);
or
model: Product;
model = new Product(1,'2',1);

The issue is that you're syntax is a little off. You should be using any of the following:
This creates a property with an instantiated value of Product.
export class CreateProductComponent {
// Define model, declare type, and assign/instantiate.
model = new Product(1,'2',1);
constructor(private productService: ProductService) { }
}
Or this one, which defines the property model as a type of Product but doesn't assign it a value. Instead it's assigned in the constructor.
export class CreateProductComponent {
// Define model, declare type
model: Product;
constructor(private productService: ProductService) {
this.model = new Product(1,'2',1);
}
}
Alternatively you could even be more explicit, even though it is not required as the types can easily be inferred.
export class CreateProductComponent {
// Define model, declare type, and assign/instantiate.
model: Product = new Product(1,'2',1);
constructor(private productService: ProductService) { }
}
There is some great documentation here as well as a REPL playground which I highly suggest using.

It should be
model = new Product(1,'2',1);

You are creating an field in CreateProductComponent. Use '=' instead of ':' like
model = new Product(1,'2',1); <- error on this line

Related

ParseObjectIdPipe for MongoDB's ObjectID

I want to create a NestJs API with TypeORM and MongoDB. My entity id fields are of type ObjectID. The controller routes should validate the incoming ids before passing them to the services. I know that Nest ships with the ParseIntPipe and ParseUUIDPipe but as far as I know there is nothing I can use for MongoDBs ObjectID.
So I created my own pipe for those fields as described here https://docs.nestjs.com/pipes#transformation-use-case
import { PipeTransform, Injectable, BadRequestException } from '#nestjs/common';
import { ObjectID } from 'typeorm';
#Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, ObjectID> {
transform(value: any): ObjectID {
const validObjectId: boolean = ObjectID.isValid(value);
if (validObjectId) {
throw new BadRequestException('Invalid ObjectId');
}
const objectId: ObjectID = ObjectID.createFromHexString(value);
return objectId;
}
}
and hope this will do the trick, even for edge cases. I can use it for my route params like
#Get(':id')
public getUserById(#Param('id', ParseObjectIdPipe) id: ObjectID): Promise<User> {
return this.usersService.getUserById(id);
}
The problem I have is that some routes need a Body validation. I use the class-validator package as described here
https://docs.nestjs.com/techniques/validation
It seems that I have to create my own class-validator decorator for those ObjectID fields but that should be fine. Maybe I'll find something here on how to do it https://github.com/typestack/class-validator#custom-validation-classes. But how can I transform those fields to the type ObjectID? Can I use the custom pipe for that later on too?
Update:
I also tried to transform the value via class-transformer package. So the code for this is
import { ObjectID } from 'typeorm';
import { Type, Transform } from 'class-transformer';
import { BadRequestException } from '#nestjs/common';
export class FooDTO {
#Type(() => ObjectID)
#Transform(bar => {
if (ObjectID.isValid(bar)) {
throw new BadRequestException('Invalid ObjectId');
}
return ObjectID.createFromHexString(bar);
})
public bar: ObjectID;
}
Unfortunately the value bar is always undefined. But maybe this code might help for validation and transformation purposes...
I took your code and changed some parts. I tested it, It works fine.
import { PipeTransform, Injectable, BadRequestException } from '#nestjs/common';
import { Types } from 'mongoose';
#Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, Types.ObjectId> {
transform(value: any): Types.ObjectId {
const validObjectId = Types.ObjectId.isValid(value);
if (!validObjectId) {
throw new BadRequestException('Invalid ObjectId');
}
return Types.ObjectId.createFromHexString(value);
}
}
For the class-transformer approach this worked for me
import { IsNotEmpty } from "class-validator";
import { Type, Transform } from 'class-transformer';
import { Types } from "mongoose"
export class CreateCommentDto {
#IsNotEmpty()
#Type(() => Types.ObjectId)
#Transform(toMongoObjectId)
articleId: Types.ObjectId
with this definition for 'toMongoObjectId':
export function toMongoObjectId({ value, key }): Types.ObjectId {
if ( Types.ObjectId.isValid(value) && ( Types.ObjectId(value).toString() === value)) {
return Types.ObjectId(value);
} else {
throw new BadRequestException(`${key} is not a valid MongoId`);
}
}
You are getting undefined because you are importing from wrong lib.
you need to change this:
import { ObjectID } from 'typeorm';
for this:
import { ObjectID } from 'mongodb';
Just create on pipe of object id in code by using below code :
import { PipeTransform, Injectable, BadRequestException } from
'#nestjs/common';
import { ObjectID } from 'mongodb';
#Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, ObjectID> {
public transform(value: any): ObjectID {
try {
const transformedObjectId: ObjectID = ObjectID.createFromHexString(value);
return transformedObjectId;
} catch (error) {
throw new BadRequestException('Validation failed (ObjectId is expected)');
}
}
}
and add piple in controller method :
#Post('cat/:id')
async cat(
#Param('id', ParseObjectIdPipe) id: ObjectId,
#Res() res,
#Body() cat: catDTO[],)

Can we use queryParams in ionic4?

We have used queryParams for angular projects. can we use queryParams in ionic project?
is there any side effect or security issues?
this.router.navigate(['your-page-name-here'], params);
You can use queryParams but it is not a recommended way because you are sending values as a part of router link. This also means that you are limited to strings only and objects need to be stringified (JSON.stringify()) and parsed each time you send data.
Better option is to use Extras State:
let navigationExtras: NavigationExtras = {
state: {
userData: this.user
}
};
this.router.navigate(['my-page'], navigationExtras);
In MyPage, get data from the State by injecting Router in constructor:
this.data = this.router.getCurrentNavigation().extras.state.userData;
Using Service: Also, you can create a service that has a getter and setter that you can use to save data in it before navigating and retrieve it after completing the navigation:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class DataService {
private data = [];
constructor() { }
setData(id, data) {
this.data[id] = data;
}
getData(id) {
return this.data[id];
}
}

Mongoose & TypeScript - Property '_doc' does not exist on type 'IEventModel'

I'm learning some JavaScript backend programming from a course I'm taking. It focuses on ExpressJS, MongoDB, and GraphQL. Because I like making things more challenging for myself, I decided to also brush up on my TypeScript while I'm at it by doing all the coursework in TypeScript.
Anyway, so I'm using verison 5.5.6 of mongoose and #types/mongoose. Here is my interface for the type of the DB record:
export default interface IEvent {
_id: any;
title: string;
description: string;
price: number;
date: string | Date;
}
Then I create the Mongoose Model like this:
import { Document, Schema, model } from 'mongoose';
import IEvent from '../ts-types/Event.type';
export interface IEventModel extends IEvent, Document {}
const eventSchema: Schema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
date: {
type: Date,
required: true
}
});
export default model<IEventModel>('Event', eventSchema);
Lastly, I have written the following resolver for a GraphQL mutation:
createEvent: async (args: ICreateEventArgs): Promise<IEvent> => {
const { eventInput } = args;
const event = new EventModel({
title: eventInput.title,
description: eventInput.description,
price: +eventInput.price,
date: new Date(eventInput.date)
});
try {
const result: IEventModel = await event.save();
return { ...result._doc };
} catch (ex) {
console.log(ex); // tslint:disable-line no-console
throw ex;
}
}
My problem is that TypeScript gives me an error that "._doc" is not a property on "result". The exact error is:
error TS2339: Property '_doc' does not exist on type 'IEventModel'.
I can't figure out what I'm doing wrong. I've reviewed the documentation many times and it seems that I should have all the correct Mongoose properties here. For the time being I'm going to add the property to my own interface just to move on with the course, but I'd prefer help with identifying the correct solution here.
This might be a late answer but serves for all that come searching this.
inteface DocumentResult<T> {
_doc: T;
}
interface IEvent extends DocumentResult<IEvent> {
_id: any;
title: string;
description: string;
price: number;
date: string | Date;
}
Now when you call for (...)._doc , _doc will be of type _doc and vscode will be able to interpert your type. Just with a generic declaration. Also instead of creating an interface for holding that property you could include it inside IEvent with the type of IEvent.
This is something that I do when I always use typescript alongside mongoose,
first things first we should define interfaces for the schema and model:
export interface IDummy {
something: string;
somethingElse: string;
}
export interface DummyDocument extends IDummy, mongoose.Document {
createdAt: Date;
updatedAt: Date;
_doc?: any
}
second we should create out schema:
const DummySchema = new mongoose.Schema<DummyDocument>({
something: String,
somethingElse: String,
})
finally we are going to use export model pattern for exporting our model as a module from file:
export const DummyModel = mongoose.model<DummyDocument>
Now the problem has fixed and you are not going to see the typescript error, we have manually attached the _doc to our model with generics that the aid of generics.
interface MongoResult {
_doc: any
}
export default interface IEvent extends MongoResult {
_id: any;
title: string;
description: string;
price: number;
date: string | Date;
}
Then you still have to deal with casting the _doc back to your own IEvent...
add _doc with type any to your custom Model interface
interface IUser extends Document {
...
_doc: any
}
a full example is here https://github.com/apotox/express-mongoose-typescript-starter
The _doc field will be a circular reference. So an easy way to go about it is to simply do something like this.
It also avoids infinite circular references by omitting itself in the child record.
No interface extension is required!
export default interface IEvent {
_doc: Omit<this,'_doc'>;
}
For some reasons, the structure of the return type is not included in #types/mongoose lib. So each time you want to de-structure the return object you get an error that the variable in not definition in the interface signature of both document and your custom types. That should be some sort of bug i guess.
The solution is to return the result itself, that will automatically return the data defined in interface (IEvent) without the meta data .
...
try {
const result = await event.save();
return result;
} catch (ex) {
throw ex;
}
...

Angular 2. Set value of ControlGroup in data driven form

Let's say I have this model:
export class MyModel {
constructor(
public id: number,
public name: string
) {}
}
and this ControlGroup:
export class MyComponent {
form: ControlGroup;
model: MyModel;
constructor(builder: FormBuilder) {
this.form = this.builder({
'id' : [''],
'name' : ['']
})
}
}
To get form's data I can simply do that (if field names match):
this.model = this.form.value;
But how can I set form's value in the same manner?
something like: this.form.value = model;
Getting the following error:
Cannot set property value of #<AbstractControl> which has only a getter
Thank you!
UPD: Based on Günter Zöchbauer's suggestion below I ended up with that helper method:
setFormValues(form: ControlGroup, model: any) {
for(var key in model) {
var ctrl = (<Control>form.controls[key]);
if ( ctrl != undefined )
ctrl.updateValue(model[key]);
}
}
The ControlGroup returned from this.builder.group(...) doesn't support to set the value. To set the value you have to set it on each control individually like:
setValue() {
let value = {id: 'xxx', name: 'yyy'};
Object.keys(value).forEach((k) => {
this.form.controls[k].updateValue(value[k]);
});
}
Plunker example

Non-Singleton Services in AngularJS

AngularJS clearly states in its documentation that Services are Singletons:
AngularJS services are singletons
Counterintuitively, module.factory also returns a Singleton instance.
Given that there are plenty of use-cases for non-singleton services, what is the best way to implement the factory method to return instances of a Service, so that each time an ExampleService dependency is declared, it is satisfied by a different instance of ExampleService?
I'm not entirely sure what use case you are trying to satisfy. But it is possible to have a factory return instances of an object. You should be able to modify this to suit your needs.
var ExampleApplication = angular.module('ExampleApplication', []);
ExampleApplication.factory('InstancedService', function(){
function Instance(name, type){
this.name = name;
this.type = type;
}
return {
Instance: Instance
}
});
ExampleApplication.controller('InstanceController', function($scope, InstancedService){
var instanceA = new InstancedService.Instance('A','string'),
instanceB = new InstancedService.Instance('B','object');
console.log(angular.equals(instanceA, instanceB));
});
JsFiddle
Updated
Consider the following request for non-singleton services. In which Brian Ford notes:
The idea that all services are singletons does not stop you from
writing singleton factories that can instantiate new objects.
and his example of returning instances from factories:
myApp.factory('myService', function () {
var MyThing = function () {};
MyThing.prototype.foo = function () {};
return {
getInstance: function () {
return new MyThing();
}
};
});
I would also argue his example is superior due to the fact that you do not have to use the new keyword in your controller. It is encapsulated within the getInstance method of the service.
I don't think we should ever have a factory return a newable function as this begins to break down dependency injection and the library will behave awkwardly, especially for third parties. In short, I am not sure there are any legitimate use cases for non-singleton sevices.
A better way to accomplish the same thing is to use the factory as an API to return a collection of objects with getter and setter methods attached to them. Here is some pseudo-code showing how using that kind of service might work:
.controller( 'MainCtrl', function ( $scope, widgetService ) {
$scope.onSearchFormSubmission = function () {
widgetService.findById( $scope.searchById ).then(function ( widget ) {
// this is a returned object, complete with all the getter/setters
$scope.widget = widget;
});
};
$scope.onWidgetSave = function () {
// this method persists the widget object
$scope.widget.$save();
};
});
This is just pseudo-code for looking up a widget by ID and then being able to save changes made to the record.
Here's some pseudo-code for the service:
.factory( 'widgetService', function ( $http ) {
function Widget( json ) {
angular.extend( this, json );
}
Widget.prototype = {
$save: function () {
// TODO: strip irrelevant fields
var scrubbedObject = //...
return $http.put( '/widgets/'+this.id, scrubbedObject );
}
};
function getWidgetById ( id ) {
return $http( '/widgets/'+id ).then(function ( json ) {
return new Widget( json );
});
}
// the public widget API
return {
// ...
findById: getWidgetById
// ...
};
});
Though not included in this example, these kinds of flexible services could also easily manage state.
I don't have time right now, but if it will be helpful I can put together a simple Plunker later to demonstrate.
Another way is to copy service object with angular.extend().
app.factory('Person', function(){
return {
greet: function() { return "Hello, I'm " + this.name; },
copy: function(name) { return angular.extend({name: name}, this); }
};
});
and then, for example, in your controller
app.controller('MainCtrl', function ($scope, Person) {
michael = Person.copy('Michael');
peter = Person.copy('Peter');
michael.greet(); // Hello I'm Michael
peter.greet(); // Hello I'm Peter
});
Here is a plunk.
I know this post has already been answered but I still think there would be some legitimate scenarios that you need to have non-singleton service. Let's say there are some reusable business logic which can be shared between several controllers. In this scenario the best place to put the logic would be a service, but what if we need to keep some state in our reusable logic? Then we need non-singleton service so can be shared across different controllers in app. This is how I would implement these services:
angular.module('app', [])
.factory('nonSingletonService', function(){
var instance = function (name, type){
this.name = name;
this.type = type;
return this;
}
return instance;
})
.controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){
var instanceA = new nonSingletonService('A','string');
var instanceB = new nonSingletonService('B','object');
console.log(angular.equals(instanceA, instanceB));
}]);
Here's my example of a non singleton service, It's from a ORM im working on. In the example I show a Base Model (ModelFactory) which I want services('users','documents') to inherit and potential extend.
In my ORM ModelFactory injects other services to provide extra functionality(query,persistence,schema mapping) which is sandboxed using the module system.
In the example both user and document service have the same functionality but have their own independent scopes.
/*
A class which which we want to have multiple instances of,
it has two attrs schema, and classname
*/
var ModelFactory;
ModelFactory = function($injector) {
this.schema = {};
this.className = "";
};
Model.prototype.klass = function() {
return {
className: this.className,
schema: this.schema
};
};
Model.prototype.register = function(className, schema) {
this.className = className;
this.schema = schema;
};
angular.module('model', []).factory('ModelFactory', [
'$injector', function($injector) {
return function() {
return $injector.instantiate(ModelFactory);
};
}
]);
/*
Creating multiple instances of ModelFactory
*/
angular.module('models', []).service('userService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("User", {
name: 'String',
username: 'String',
password: 'String',
email: 'String'
});
return instance;
}
]).service('documentService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("Document", {
name: 'String',
format: 'String',
fileSize: 'String'
});
return instance;
}
]);
/*
Example Usage
*/
angular.module('controllers', []).controller('exampleController', [
'$scope', 'userService', 'documentService', function($scope, userService, documentService) {
userService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
username : 'String'
password: 'String'
email: 'String'
}
}
*/
return documentService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
format : 'String'
formatileSize: 'String'
}
}
*/
}
]);
angular only gives a singleton service/factory option.
one way around it is to have a factory service that will build a new instance for you inside your controller or other consumer instances.
the only thing that is injected is the class that creates new instances.
this is a good place to inject other dependencies or to initialize your new object to the specification of the user (adding services or config)
namespace admin.factories {
'use strict';
export interface IModelFactory {
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel;
}
class ModelFactory implements IModelFactory {
// any injection of services can happen here on the factory constructor...
// I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion.
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel {
return new Model($log, connection, collection, service);
}
}
export interface IModel {
// query(connection: string, collection: string): ng.IPromise<any>;
}
class Model implements IModel {
constructor(
private $log: ng.ILogService,
private connection: string,
private collection: string,
service: admin.services.ICollectionService) {
};
}
angular.module('admin')
.service('admin.services.ModelFactory', ModelFactory);
}
then in your consumer instance you need the factory service and call the build method on the factory to get a new instance when you need it
class CollectionController {
public model: admin.factories.IModel;
static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory'];
constructor(
private $log: ng.ILogService,
$routeParams: ICollectionParams,
private service: admin.services.ICollectionService,
factory: admin.factories.IModelFactory) {
this.connection = $routeParams.connection;
this.collection = $routeParams.collection;
this.model = factory.build(this.$log, this.connection, this.collection, this.service);
}
}
you can see it provides opperatiunity to inject some specific services that are not available in the factory step.
you can always have injection happen on the factory instance to be used by all Model instances.
Note I had to strip off some code so I might made some context errors...
if you need a code sample that works let me know.
I believe that NG2 will have the option to inject a new instance of your service in the right place in your DOM so you dont need to build your own factory implementation. will have to wait and see :)
I believe there is good reason to create a new instance of an object within a service. We should keep an open mind as well rather than just say we ought never do such a thing, but the singleton was made that way for a reason. Controllers are created and destroyed often within the lifecycle of the app, but the services must be persistent.
I can think of a use case where you have a work flow of some kind, like accepting a payment and you have multiple properties set, but must now change their payment type because the customer's credit card failed and they need to provide a different form of payment. Of course, this does have a lot to do with the way you create your app. You could reset all properties for the payment object, or you could create a new instance of an object within the service. But, you would not want a new instance of the service, nor would you want to refresh the page.
I believe a solution is providing an object within the service that you can create a new instance of and set. But, just to be clear, the single instance of the service is important because a controller may be created and destroyed many times, but the services need persistence. What you are looking for may not be a direct method within Angular, but an object pattern that you can manage inside your service.
As an example, I have a made a reset button. (This is not tested, its really just a quick idea of a use case for creating a new object within a service.
app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) {
$scope.utility = {
reset: PaymentService.payment.reset()
};
}]);
app.factory("PaymentService", ['$http', function ($http) {
var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/"
function PaymentObject(){
// this.user = new User();
/** Credit Card*/
// this.paymentMethod = "";
//...
}
var payment = {
options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"],
paymentMethod: new PaymentObject(),
getService: function(success, fail){
var request = $http({
method: "get",
url: paymentURL
}
);
return ( request.then(success, fail) );
}
//...
}
return {
payment: {
reset: function(){
payment.paymentMethod = new PaymentObject();
},
request: function(success, fail){
return payment.getService(success, fail)
}
}
}
}]);
Here's another approach to the problem that I was quite satisfied with, specifically when used in combination with Closure Compiler with advanced optimizations enabled:
var MyFactory = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
MyFactory.prototype.foo = function() {
console.log(this.arg1, this.arg2);
// You have static access to other injected services/factories.
console.log(MyFactory.OtherService1.foo());
console.log(MyFactory.OtherService2.foo());
};
MyFactory.factory = function(OtherService1, OtherService2) {
MyFactory.OtherService1_ = OtherService1;
MyFactory.OtherService2_ = OtherService2;
return MyFactory;
};
MyFactory.create = function(arg1, arg2) {
return new MyFactory(arg1, arg2);
};
// Using MyFactory.
MyCtrl = function(MyFactory) {
var instance = MyFactory.create('bar1', 'bar2');
instance.foo();
// Outputs "bar1", "bar2" to console, plus whatever static services do.
};
angular.module('app', [])
.factory('MyFactory', MyFactory)
.controller('MyCtrl', MyCtrl);