Extend Vuetify components in TypeScript - javascript

I'm trying to extend a vuetify component with some default props set in TypeScript. In JavaScript it worked perfectly but I can't figure out how to extend the components in TS. Here is the code for the Component in JS:
import { VTextField } from 'vuetify/lib'
export default {
name: "my-text-field",
extends: VTextField,
props: {
"hide-details": {
type: Boolean,
default: true
},
outlined: {
type: Boolean,
default: true
},
dense: {
type: Boolean,
default: true
},
"single-line": {
type: Boolean,
default: true
},
color: {
type: String,
default: "secondary"
}
}
}

The proper way to do that add in the tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"types": ["vuetify"]
}
}
Now just extend the needful component.
<script lang="ts">
import { VTextField } from 'vuetify/lib'
import { Component, Prop } from 'vue-property-decorator'
#Component({})
export default class TextField extends VTextField {
#Prop({default: 'auto'}) private hideDetails!: boolean|string;
#Prop({default: true}) private outlined!: boolean;
#Prop({default: true}) private dense!: boolean
#Prop({default: true}) private singleLine!: boolean;
#Prop({default: 'secondary'}) private color!: string
}
</script>

I've found a solution by peeking into the VTextArea component of Vuetify. Here's my solution:
import Vue from 'vue'
//#ts-ignore
import VTextField from 'vuetify/lib/components/VTextField/VTextField'
// Create Base Mixins and Define Custom Properties
const base = Vue.extend({ mixins: [VTextField] })
export default base.extend({
name: "my-text-field",
props: {
hideDetails: {
type: Boolean,
default: true
},
outlined: {
type: Boolean,
default: true
},
dense: {
type: Boolean,
default: true
},
singleLine: {
type: Boolean,
default: true
},
color: {
type: String,
default: "secondary"
}
}
})

hope this works for you:
import {Vue, Component, Prop} from 'vue-property-decorator';
import { VIcon, VTextField} from 'vuetify/lib'
interface Item {
text: string;
complete: boolean;
}
#Component({
name: 'TodoItem',
components: {
'v-icon': VIcon,
'v-text-field': VTextField
}
})
export default class TodoItem extends Vue {
#Prop(Object) public item!: Item;
#Prop(Number) public index!: number;
#Prop(Number) public editingId!: number;
public editingContent = 'nihao';
public edit() {
this.$emit('on-edit', this.index)
}
public save() {
alert('k');
}
protected render() {
return (
<li>
{this.editingId === this.index ?
(<div>
{/* tslint:disable-next-line:max-line-length */}
<v-text-field v-model={this.editingContent} append-icon={'mdi-close'} placeholder={this.item.text} on-click:append={this.save}/>
{/*<v-text-field><v-icon color={'red'} slot={'append'}>mdi-close</v-icon></v-text-field>*/}
</div>)
: (<div>
<span>{this.item.text}</span>
<v-icon x-small={true} nativeOn-click={this.edit}>mdi-pencil</v-icon>
</div>)
}
</li>
);
}
}

Related

Create Jest unit test for Passport ApiKey Strategy in NestJS

Let say I have a file named api-key.strategy.ts
import { PassportStrategy } from '#nestjs/passport';
import { Injectable } from '#nestjs/common';
import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
import { ConfigService } from '#nestjs/config';
#Injectable()
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) {
constructor(private readonly configService: ConfigService) {
super(
{
header: 'x-api-key',
prefix: ''
},
true,
(apiKey: string, done: any) => {
if (apiKey === 'MY_API_KEY') return done(true);
else return done(false);
}
);
}
}
Based on above codes, how to create a Jest unit test?
Thanks

How to fix NestJS #InjectModel() dependency error?

rememberLink.scheme.ts
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document, Types } from 'mongoose';
import { User } from 'src/users/schemas/users.schema';
export type RememberLinkDocument = RememberLink & Document;
#Schema({versionKey: false, timestamps: true})
export class RememberLink {
#Prop({ type: String, required: true })
code: string;
#Prop({ type: Types.ObjectId, ref: User.name, required: true })
user: User;
}
export const RememberLinkSchema = SchemaFactory.createForClass(RememberLink);
remember-password.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { RememberPasswordController } from './remember-password.controller';
import { RememberPasswordService } from './remember-password.service';
import { RememberLink, RememberLinkSchema } from './schemas/rememberLink.schema';
#Module({
imports: [
MongooseModule.forFeature([{
name: RememberLink.name,
schema: RememberLinkSchema
}])
],
controllers: [RememberPasswordController],
providers: [RememberPasswordService],
exports: [RememberPasswordService]
})
export class RememberPasswordModule {}
remember-password.service.ts
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import { UserDto } from 'src/users/dto/user.dto';
import { User } from 'src/users/schemas/users.schema';
import { RememberLinkDto } from './dto/rememberLink.dto';
import { RememberLink, RememberLinkDocument } from './schemas/rememberLink.schema';
#Injectable()
export class RememberPasswordService {
constructor( #InjectModel(RememberLink.name) private readonly rememberLinkModel: Model<RememberLinkDocument> ) {}
async getUserByRememberCode(code: string): Promise<UserDto> {
return await this.rememberLinkModel.findOne({code}).populate(User.name).lean();
}
}
Error:
Nest can't resolve dependencies of the RememberPasswordService (?).
Please make sure that the argument RememberLinkModel at index [0] is
available in the RememberPasswordService context.
What I would do is export the MongooseModule as well, Its just that the dependency injection knows that there will be Separate module for that model somewhere in the App
#Module({
imports: [
MongooseModule.forFeature([{
name: RememberLink.name,
schema: RememberLinkSchema
}])
],
controllers: [RememberPasswordController],
providers: [RememberPasswordService],
exports: [MongooseModule,RememberPasswordService] // <-- MongooseModule added here
})
export class RememberPasswordModule {}
Fixed. It was import "RememberPasswordService" instead of "RememberPasswordModule" in another module

Type 'QueryProps' has no property and no string index signature

I am using react-apollo#1.4.2 and TypeScript#2.3.4 and I have received this as an error of my below code:
export default graphql(PROFILE_QUERY, {
options: {
fetchPolicy: 'cache-and-network',
},
props: ({ data: { loading, currentUser } }) => ({
loading,
currentUser,
}),
})(User);
And the component is declared like this:
export interface IUserProps {
loading: boolean;
currentUser: {
login: string;
};
}
class User extends React.Component<IUserProps, any> {

Angular 2 : ViewChild is undefined on parent

I have following error :
Error: Uncaught (in promise): TypeError: Cannot set property 'test_id' of undefined
TypeError: Cannot set property 'test_id' of undefined
The errror is trigger on this line:
console.log('after view init testList', this.productList.test_id);
I saw a lot of posts but all of them seems outdated and most of them are saying that I have to use a function ngAfterViewInit which I did.
I have a clic action that triggers updateTestId and I want to pass this id to my child view ProductListComponent.
Here is my parent component :
import { Component,ViewChild} from '#angular/core';
import {Test,TestData} from './testService';
import { LocalDataSource } from 'ng2-smart-table';
import {ProductListComponent} from '../../components/product/list/productList.component';
#Component({
selector: 'test-list',
templateUrl: './testList.html',
styleUrls: ['./testList.scss']
})
export class TestListComponent{
//tests: Test[];
tests: any[];
selected_test : number;
#ViewChild(ProductListComponent)
private productList: ProductListComponent;
constructor(protected service: TestData){
this.service.getData().then((data) => {
this.tests = data.tests;
this.source.load(data);
});
}
settings = {
editable : false,
actions: {
add:false,
edit:false,
delete:false
},
columns: {
id: {
title: 'ID',
type: 'number'
},
nb_cartons: {
title: 'Cartons',
type: 'number'
},
nb_items: {
title: 'Items',
type: 'number'
},
nb_pallets: {
title: 'Pallets',
type: 'number'
},
};
//source : Test[];
source: LocalDataSource = new LocalDataSource();
public updateTestId(value:any):void {
this.selected_test = value.data.id;
console.log('rowSelect', this.selected_test );
//console.log(this.productList.test_id)
}
}
And here is my child component :
import { Component,Input,Output, OnChanges, SimpleChanges } from '#angular/core';
import { LocalDataSource } from 'ng2-smart-table';
import {Test} from '../../test/testService';
#Component({
selector: 'product-list',
templateUrl: './productList.html',
styleUrls: ['./productList.scss']
})
export class ProductListComponent implements OnChanges{
#Input() test_id : number = null;
settings = {
editable : false,
actions: {
add:false,
edit:false,
delete:false
},
columns: {
id: {
title: 'ID',
type: 'number'
},
sku: {
title: 'SKU',
type: 'string'
},
reference: {
title: 'Reference',
type: 'string'
},
size: {
title: 'Size',
type: 'string'
},
},
edit: {
editButtonContent: '<i class="ion-edit"></i>',
saveButtonContent: '<i class="ion-checkmark"></i>',
cancelButtonContent: '<i class="ion-close"></i>',
}
};
source: LocalDataSource = new LocalDataSource();
constructor() {
}
ngOnChanges(changes: SimpleChanges) {
console.log(changes['test_id'],changes['test_id'].currentValue);
console.log('change in child',changes.test_id);
}
/*
#Input()
set _test_id(id: number) {
this._test_id = id;
}
get test_id(): number { return this._test_id; }
*/
}
i use the child selector like this :
<test-list></test-list>
<product-list #productList [test_id]="selected_test"></product-list>
Add a template reference variable name to the product-list in the template
<product-list #productList [test_id]="selected_test"></product-list>
And reference it by that in the component
#ViewChild('productList')
private productList: ProductListComponent;
EDIT:
In this case Vivek Doshi is correct in his answer that you don't need it since you are passing data to the child via #Input. But still - if you want to use ViewChild, this is a solution :)
If you want to pass data from parent to child you can directly pass
<product-list [test_id]="selected_test"></product-list>
That's it , nothing more.
There is no need to access it via #ViewChild
To detect the changed value over the time from product-list component
ngOnChanges(changes: SimpleChanges) {
console.log(changes['test_id'].currentValue);
}
Example :
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnChanges {
#Input()
prop: number;
ngOnChanges(changes: SimpleChanges) {
// changes.prop contains the old and the new value...
}
}
Things to note :
You need to implements OnChanges on child component.
For more detail please read this doc

How can I effectively reset a state using #ngrx/store?

I seem to have gotten stuck on this matter for the last couple of days.
We're working on an Angular 2 application, and I need to create a wizard for users to fill out a form.
I've successfully managed to make the data flow through each step of the wizard, and save it in order to freely move back and forth. However, I can't seem to be able to reset it once the form is submitted.
I should add that each component is behind a wall. Maybe a better solution would be a singleton service injected directly at the AppModule. But I can't seem to make it work.
Here's my code so far:
Step 1
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { EventOption } from '../../../events/shared/event-option.model';
import { Store } from '#ngrx/store';
import { NewEventService } from '../shared/new-event.service';
import { Event } from '../../../events/shared/event.model';
import { FriendService } from '../../../friends/shared/friend.service';
#Component({
selector: 'app-upload-images',
templateUrl: './upload-images.component.html',
styleUrls: ['../../../events/new-event/new-event.component.css']
})
export class UploadImagesComponent implements OnInit {
form: FormGroup;
private event;
private images = [];
constructor(
private _store: Store<any>,
private formBuilder: FormBuilder,
private router: Router,
private newEventService: NewEventService,
private friendService: FriendService
) {
_store.select('newEvent').subscribe(newEvent => {
this.event = newEvent;
})
}
ngOnInit() {
this.initForm(this.event);
if (this.event.counter === 0) {
let friends = this.friendService.getFriends('58aaf6304fabf427e0acc08d');
for (let friend in friends) {
this.event.userIds.push(friends[friend]['id']);
}
}
}
initForm(event: Event) {
this.images.push({ imageUrl: 'test0', voteCount: 0 });
this.images.push({ imageUrl: 'test1', voteCount: 0 });
this.images.push({ imageUrl: 'test2', voteCount: 0 });
this.images.push({ imageUrl: 'test3', voteCount: 0 });
this.form = this.formBuilder.group({
firstImage: [this.event.length > 0 ? this.event.eventOption[0].imageUrl : null],
secondImage: [this.event.length > 0 ? this.event.eventOption[1].imageUrl : null],
thirdImage: [this.event.length > 0 ? this.event.eventOption[2].imageUrl : null],
fourthImage: [this.event.length > 0 ? this.event.eventOption[3].imageUrl : null],
})
}
next() {
this.event.eventOptions = this.images;
this.newEventService.updateEvent(this.event);
this.router.navigate(['events/new-event/choose-friends']);
}
}
Step 2
import { Component, OnInit, Input } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { EventOption } from '../../../events/shared/event-option.model';
import { Store } from '#ngrx/store';
import { Event } from '../../shared/event.model';
import { NewEventService } from '../shared/new-event.service';
import { FriendService } from '../../../friends/shared/friend.service';
import { SearchPipe } from '../../../core/search.pipe';
#Component({
selector: 'app-choose-friends',
templateUrl: './choose-friends.component.html',
styleUrls: ['../../../events/new-event/new-event.component.css', './choose-friends.component.css']
})
export class ChooseFriendsComponent implements OnInit {
private searchTerm = '';
private event;
private friends = [];
private friendsError = false;
constructor(
private _store: Store<any>,
private formBuilder: FormBuilder,
private router: Router,
private newEventService: NewEventService,
private friendService: FriendService
) {
_store.select('newEvent').subscribe(newEvent => {
this.event = newEvent;
})
}
ngOnInit() {
this.friends = this.friendService.getFriends('58aaf6304fabf427e0acc08d');
}
selectedFriend(friendId: string) {
return this.friendService.selectedFriend(friendId, this.event.userIds);
}
toggleFriend(friendId: string) {
return this.friendService.toggleFriend(friendId, this.event.userIds);
}
toggleAllFriends() {
return this.friendService.toggleAllFriends(this.friends, this.event.userIds);
}
submit() {
if (this.event.userIds.length > 0) {
this.newEventService.resetEvent();
this.router.navigate(['events/vote-events']);
} else {
this.friendsError = true;
}
}
back() {
this.newEventService.updateEvent(this.event);
this.router.navigate(['events/new-event/upload-images']);
}
}
Event Service
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Store, Action } from '#ngrx/store';
import { Event } from '../../../events/shared/event.model';
import { EventOption } from '../../../events/shared/event-option.model';
import { newEvent, newEventModel } from './new-event.reducer';
import 'rxjs/add/operator/take';
import 'rxjs/add/operator/find';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class NewEventService {
public newEvent$: Observable<newEventModel>;
constructor(private store: Store<newEventModel>) {
this.newEvent$ = this.store.select('newEvent');
}
getEvent(event) {
return this.store.dispatch({
type: 'GET_EVENT',
payload: event
})
}
updateEvent(event) {
return this.store.dispatch({
type: 'UPDATE_EVENT',
payload: event
})
}
resetEvent() {
return this.store.dispatch({
type: 'RESET_EVENT',
})
}
}
Event Reducer
import { EventOption } from '../../shared/event-option.model';
import { EventType } from '../../shared/event-type.model';
import { ActionReducer, Action } from '#ngrx/store';
import { Event } from '../../shared/event.model';
import { FriendService } from '../../../friends/shared/friend.service';
export interface newEventModel {
eventOptions: EventOption[];
eventTypeId: number,
duration: number,
comment: string,
privacyId: number,
isGlobal: boolean,
id: string,
userIds: string[],
counter: number
}
let blankState: newEventModel = {
eventOptions: [],
eventTypeId: null,
duration: 1440,
comment: '',
privacyId: 0,
isGlobal: false,
id: '',
userIds: [],
counter: 0
}
let initialState: newEventModel = {
eventOptions: [],
eventTypeId: null,
duration: 1440,
comment: '',
privacyId: 0,
isGlobal: false,
id: '',
userIds: [],
counter: 0
}
export const newEvent: ActionReducer<newEventModel> = (state: newEventModel = initialState, action: Action) => {
// return new state
switch (action.type) {
case 'GET_EVENT':
return state;
case 'UPDATE_EVENT':
action.payload.counter = action.payload.counter + 1;
return action.payload;
case 'RESET_EVENT':
return Object.assign({}, state, {
eventOptions: [],
eventTypeId: null,
duration: 1440,
comment: '',
privacyId: 0,
isGlobal: false,
id: '',
userIds: [],
counter: 0
});
default:
return state;
}
}
I could provide a working plunkr if needed, but I need to create it first.
TLDR: How can I reset the state on #ngrx/store?
Thanks for any help provided!
Noy Levi had the right thinking in her answer to this question, which assigns initialState back into state, however, there is a way to assign initialState for each reducer automatically.
The key concept to understand is that if the value of 'state' passed into a reducer is 'undefined' (not 'null', it needs to be 'undefined') then the reducer will automatically assign into 'state' the initialState provided to the reducer when it was created. Because of this default behavior, you can create a 'metareducer' that recognizes an action, say 'logout', and then passes a state of 'undefined' into all the subsequent reducers called.
This behavior is described well in this article about redux, this article about NgRx, and also in this answer about NgRx.
The relevant code would look like this:
export function logoutClearState(reducer) {
return function (state, action) {
if (action.type === ActionTypes.LOGOUT) {
state = undefined;
}
return reducer(state, action);
};
}
#NgModule({
imports: [
StoreModule.forRoot(reducers, { metaReducers: [logoutClearState] }),
],
declarations: [],
providers: [],
})
You can reset the state to initialState in your reducer by using Object.assign to copy all properties of initialState to a new object.
export const newEvent: ActionReducer<newEventModel> = (state: newEventModel = initialState, action: Action) => {
// return new state
switch (action.type) {
// ...
case 'RESET_EVENT':
return Object.assign({}, initialState);
// ...
}
}
A note on the reducer
The reducer should be a pure function, so should not modify the arguments. Your UPDATE_EVENT requires a little tweak:
case 'UPDATE_EVENT':
let counter = { counter: action.payload.counter + 1 };
return Object.assign({}, action.payload, counter);
The pattern to follow is Object.assign({}, source1, source2, ...) where source1, source2 etc contain properties to be assigned. Properties in source1 are overwritten by duplicate properties in source2 etc.
there is much easier way, you just need to set the initialState instead of state:
const reducer = createReducer(initialState,
on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
...initialState
})),
I'm assuming your RESET_EVENT is suppose to return a fresh object. Though you are filling in the object with your state data and another object:
case 'RESET_EVENT':
return Object.assign({}, state, {
eventOptions: [],
eventTypeId: null,
duration: 1440,
comment: '',
privacyId: 0,
isGlobal: false,
id: '',
userIds: [],
counter: 0
});
The syntax for Object.assign is Object.assign(target, ...sources) and your providing two items as sources: state and the object containing eventOptions, eventTypeId, etc.
Instead you'll want to return Object.assign({}, initialState);
sorry, I took a day off in order to study for some exams. I ended up "solving" it by doing the following:
....
case 'RESET_EVENT':
action.payload.eventOptions = blankState.eventOptions;
action.payload.eventTypeId = blankState.eventTypeId;
action.payload.duration = blankState.duration;
action.payload.comment = blankState.comment;
action.payload.privacyId = blankState.privacyId;
....
return action.payload;
....
It might not be the prettiest or best solution, but at least it works. Thanks for all the help #iblamefish and everyone.

Categories

Resources