I have to import my URL link (actionsConfig) to my component.
In HTML I used to use "router link", how does it work in TypeScript?
actionsConfig.ts :
export const ACTIONS = [
{
label: 'Delete',
actionType: 'DELETE',
},
{
label: 'Edit',
actionType: 'GO_TO',
getUrl: row => '/detail/' + row.id,
},
];
component.ts :
actionFunc(action, user: User) {
if (action.actionType === 'DELETE') {
if (confirm('Are you sure you want to delete this item?') === true) {
/*delete ok*/
}
}
if (action === 'GO_TO') {
const url = action.getUrl(user);
/* GO TO URL (routerlink?) */
}
}
UPDATE
This one worked for me.
constructor(private router: Router) {
}
/.../
if (action.actionType === 'GO_TO') {
console.log('test');
const url = action.getUrl(user);
this.router.navigateByUrl(url).then();
}
Related
I need to validate that my user selects at least one viewport. I simply added the validator.required for the input but for the checkbox in an array I am not sure how to validate it.
public createForm(): FormGroup {
return this.formBuilder.group({
urlInput: ['', [ Validators.required, Validators.pattern(URL_VALIDATOR) ]],
children: [false, []],
viewport: this.formBuilder.array(this.viewports.map(x => false))
});
}
I used this to create the checkbox:
public ngOnInit(): void {
this.urlForm = this.createForm();
const checkboxControl = this.urlForm.controls.viewport as FormArray;
You could create a custom validator for your form:
Validator:
export function minimumNeededSelected(
controlArrayName: string,
quantityNeededSelected: number
) {
return (formGroup: FormGroup) => {
const controlArray = formGroup.controls[controlArrayName];
let quantitySelected = 0;
(controlArray as FormArray).controls.forEach(control => {
if (control.value) {
quantitySelected++;
}
});
if (quantitySelected < quantityNeededSelected) {
controlArray.setErrors({ minimumNeededSelected: true });
} else {
controlArray.setErrors(null);
}
};
}
How to use:
public createForm(): FormGroup {
return this.formBuilder.group({
urlInput: ['', [ Validators.required, Validators.pattern(URL_VALIDATOR) ]],
children: [false, []],
viewport: this.formBuilder.array(this.viewports.map(x => false))
},
{
validators: [minimumNeededSelected('viewport', 1)]
});
}
This way your formArray will get an error of 'minimumNeededSelected' if there is not enough items selected.
You can create a function that returns if a viewport is checked or not.
hasCheckedItem(): boolean {
const viewports = this.urlForm.get('viewport') as FormArray;
if (viewports) {
viewports.controls.forEach(control => {
if (control.value) {
return true;
}
});
}
return false;
}
You can then perform this check before a form submit or next action taken where this needs to be true.
I created a todo list by using react. I get some problem that I want to create checkbox but my checkbox it does not work and I cannot solve :( I don't know what's wrong with that.
I set the data for each task and then I need to change the completed of some task, but it cannot click and change the completed task
This is my code
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todoData,
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if(todo.id === id) {
todo.completed = !todo.completed
// console.log(todo.completed)
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItem = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange={this.handleChange}/>)
return (
<div>
<h1 className="header">My Todo Lists</h1>
{todoItem}
</div>
)
}
}
function TodoItem(props) {
let textItem = props.item.completed === true ?
<del>{props.item.text}</del> : props.item.text
return (
<div className="list">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p className="item">{textItem}</p>
</div>
)
}
And this is my data
const todoData = [
{
id: 1,
text: "Practice coding",
completed: false
},
{
id: 2,
text: "Grocery shopping",
completed: true
},
{
id: 3,
text: "Wash the dishes",
completed: true
},
{
id: 4,
text: "Take out the trash",
completed: false
},
{
id: 5,
text: "Teach my brother homework",
completed: false
}
]
Thank you for helping :)
Looks like on your handleChange you are mutating the existing state on your map transformation. you must return a new state instead.
Replace your handleChange with the following code:
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
return {
...todo,
completed: todo.id === id ? !todo.completed : todo.completed
};
});
return {
todos: updatedTodos
};
});
}
Right now In my Edit page a user can delete section and subsection but I'm thinking about adding confirmation dialog so that a user will not accidentally delete either of them.
I'm not really sure how I can pass the data between confirmation dialog component and Edit Page component.
The project is not gonna run on on Stackblitz but I have uploaded those two component in here https://stackblitz.com/edit/angular-ivy-ztepf6?file=Edit Component/EditComponent.ts
so I will be really appreciated if anybody can check it out and able to help me. thanks
Edit Component.TS
this is how I open Comfirmation Dialog
openSection() {
let dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: {
isSection: true, isSubsection: false
},
}
);
}
openSubsection(){
this.dialog.open(ConfirmDialogComponent, {
data: {
isSubsection: true, isSection: false
},
});
}
//This is how I'm deleting right now without confirmation Dialog
delete(sec) {
if (this.isSection) {
this.HelpService.deleteHelpSection(sec.id).subscribe(() => {
const index = this.mappedSections.findIndex((value) => value.id == sec.id);
this.mappedSections = this.mappedSections.filter(section => section.id != sec.id)
if (~index) {
this.HelpService.deleteHelpSubsection(sec.id).subscribe(() => {
this.mappedSections = this.mappedSections.filter(section => section.id != sec.id);
})
}
})
} if (this.isSubsection) {
this.HelpService.deleteHelpSubsection(sec.id).subscribe(() => {
const index = this.mappedSections.findIndex((value) => value.id == sec.parentId);
if (~index) {
this.mappedSections[index].subSections = this.mappedSections[index].subSections.filter((subsection) => subsection.id != sec.id)
}
})
}
}
subscribe for the result from the modal.
openSection() {
let dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: {
isSection: true, isSubsection: false
},
});
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed', result);
if(result){
this.delete(sec)
}
});
}
from the modal component pass the data
<button mat-raised-button (click)="closeDialog()">Close</button>
constructor(
public dialogRef: MatDialogRef<ConfirmDialogComponent>
) {}
closeDialog() {
this.dialogRef.close(true);
}
check this working sample
https://stackblitz.com/edit/angular-pd7vt6?file=src/app/dialog-elements-example.ts
I have to modify the following code with an implementation of an Array of actions (bottom page).
I saw lots of websites by I wasn't able to find something than can be used for my code.
I will have to change my html , my tableService, my component.ts and oviously my actionConfiguration.
At the moment this is my HTML:
<div class="container">
<table class="table">
<tr>
<th *ngFor="let col of columns" (click)="sortTable(col)">{{col}}</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users | paginate: {itemsPerPage: 5,
currentPage: page,
totalItems: users.length } ; let i = index">
<td *ngFor="let col of columns">{{user[col]}}</td>
<td>
<button [ngClass]="getClassCondition(act)" *ngFor="let act of actions" (click)="actionFunc(act,i)">{{act}}</button>
</td>
</tr>
</table>
</div>
<div>
<pagination-controls (pageChange)="page = $event"></pagination-controls>
</div>
This is my component.ts:
#Component({
selector: 'app-dynamic-table',
templateUrl: './dynamic-table.component.html',
styleUrls: ['./dynamic-table.component.css']
})
export class DynamicTableComponent implements OnInit {
#Input()
users = [];
#Input()
columns: string[];
#Input()
actions: string[];
#Input()
class;
direction = false;
page: any;
constructor() {
}
sortTable(param) {
/*done*/
}
actionFunc(i, index) {
if (i === 'deleteUser') {
if (confirm('Are you sure you want to delete this item?') === true) {
this.users.splice(index, 1);
}
}
if (i === 'editUser') {
/*...*/
}
}
getClassCondition(act) {
return act === 'deleteUser' ? this.class = 'btn btn-danger' : 'btn btn-primary' ;
}
ngOnInit(): void {
}
}
This is my tableService.ts
import { USERS } from './mock-data';
#Injectable()
export class TableService {
constructor() { }
static getUsers(): Observable<any[]> {
return Observable.of(USERS).delay(100);
}
static getColumns(): string[] {
return ['id', 'firstName', 'lastName', 'age'];
}
static getActions(): string[] {
return ['deleteUser', 'editUser'];
}
}
Here's the new Task, I have to create an Array of Actions so I will be able to use it in different components but I have no idea how to do it.
I have to start from something like this, it's just an example (not complete because I don't know what to insert exactly):
actionConfig.ts
export const ACTIONS = [
{
label: 'Remove',
actionType: 'deleteUser',
},
{
label: 'Edit',
actionType: 'editUser',
},
];
A sample of Enum and a table to show data on iterating on them:
StackBlitz
You also might want to read typescript-enums-explained
Basically, the TypeScript enums are compiled to something as shown below for reverse lookup. Thats why I have added the foreach loop in constructor and created another list.
export enum Fruits {
APPLE = 'Apple',
MANGO = 'Mango',
BANANA = 'Banana',
}
is compiled to
var Fruit;
(function (Fruit) {
Fruit[Fruit["APPLE"] = 'Apple'] = "APPLE";
Fruit[Fruit["MANGO"] = 'Mango'] = "MANGO";
Fruit[Fruit["BANANA"] = 'Banana'] = "BANANA";
})(Fruit || (Fruit = {}));
UPDATE
HTML
<button [ngClass]="getClassCondition(act.actionType)" *ngFor="let act of actions"
(click)="actionFunc(act, user)">{{act.label}}</button>
COMPONENTS.TS
actionFunc(action, element: any) {
if (action.actionType === 'DELETE') {
if (confirm('Are you sure you want to delete this item?') === true) {
/*...*/
}
}
if (action.actionType === 'GO_TO') {
/*...*/
}
}
actionsConfig.ts
export const ACTIONS = [
{
label: 'Delete',
actionType: 'DELETE',
deleteApi: 'api/USERS'
},
{
label: 'Edit',
actionType: 'GO_TO',
getUrl: row => '/detail/' + row.id,
},
];
I am creating a dynamic form from a configurable json in Angular 5. Everything is working fine,but i am not able to add a custom validator for a particular field.I am getting an error like
TypeError: v is not a function
Json
{
"key": "age",
"label": "Age",
"required": false,
"order": 4,
"controlType": "textbox",
"validations": ['required', 'minlength'],
"custom":['rangeValidator'],//custom validator function name
"type": "email"
}
Component to make dynamic form controls:
toFormGroup(questions) {
let group: any = {};
questions.forEach(question => {
group[question.key] = new FormControl(question.value || '', this.getValidators(question)
);
});
return new FormGroup(group);
}
getValidators(question) {
let vals = [];
question.validations.forEach((v) => {
if (v == 'required') {
vals.push(Validators.required);
}
if (v == 'minlength') {
vals.push(Validators.minLength(4))
}
});
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(va);
});
}
return vals;
}
Main Component file:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, AbstractControl ,FormControl} from '#angular/forms';
function rangeValidator(c: FormControl) {
if (c.value !== undefined && (isNaN(c.value) || c.value > 1 || c.value < 10)) {
return { range: true };
}
return null;
}
#Component({
selector: 'app-question',
templateUrl: './dynamic-form-question.component.html',
styleUrls: ['./dynamic-form-question.component.css']
})
export class DynamicFormQuestionComponent implements OnInit {
#Input() question;
#Input() form: FormGroup;
get isValid() { return this.form.controls[this.question.key].valid; }
constructor() { }
ngOnInit() {
console.log("My form", this.form.value)
}
}
Stackblitz Url
Any ideas,Please help
there
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(va);
});
}
you want to add your custom validators, but in fact you just add to the validators array the string "rangeValidator". So yes v is not a function :)
You should should declare you range validators as a static function of your customs validators like that :
export class CustomValidators {
static rangeValidator(c: FormControl) {
if (c.value !== undefined && (isNaN(c.value) || c.value > 1 || c.value < 10)) {
return { range: true };
}
return null;
}
then import it in and use it like that :
getValidators(question) {
....
if (question.custom || question.custom.length > 0) {
question.custom.forEach((va) => {
vals.push(CustomValidators[va]);
});
}
return vals;
}
see the forked stackblitz
NB : this fork only resolve the current matter. You global example form validation still doesnt work