Angular 12 FormControls in nested formGroup inside of two FormArrays - angular12

I am trying to build dynamic form that has such structure:
Form
-|FormGroup
--|FormArray
---|FormGroup
----|FormControl
----|FormArray
-----|FormGroup
------|FormControl <-- Problem that I cannot bind this form control to input
------|FomrControl <-- Problem that I cannot bind this form control to input
On two specified controls I get error
Cannot find control with path: 'items -> 0 -> properties -> 1'
I just guess that it can be because something wrong with let rowItem of items.controls[i].value.properties; let j=index
Tried a lot of different sceanrious without success. For example:
let rowItem of items.controls[i].properties; let j=index
give
Property 'properties' does not exist on type 'AbstractControl'.
StackBlitz
On StackBlitz it is also not working correct.
I will be grateful for advices on how to connect those FormControl's to inputs.
Html code:
<form [formGroup]="myForm">
<div formArrayName="items">
<div *ngFor="let item of items.controls; let i=index">
<div [formGroupName]="i">
<input formControlName="word" matInput>
<div formArrayName="properties">
<div *ngFor="let rowItem of items.controls[i].value.properties; let j=index">
<div [formGroupName]="j">
<input formControlName="property" matInput>
<input formControlName="value" matInput>
</div>
</div>
<br>
</div>
<button (click)="addWordProperty(items.controls[i].value.properties)">Add property</button>
<br>
</div>
</div>
<button (click)="addWord()">Add word</button>
</div>
</form>
{{ myForm.value | json }}
Type Script Code:
import { Component} from '#angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup } from '#angular/forms';
#Component({
selector: 'app-infinitive',
templateUrl: './infinitive.component.html',
styleUrls: ['./infinitive.component.css']
})
export class InfinitiveComponent {
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
items: this.fb.array([
this.fb.group({
word: this.fb.control(''),
properties: this.fb.array([
this.fb.group({
property: this.fb.control(''),
value: this.fb.control('')
})
]),
})
])
})
}
addWordProperty(control: any) {
var items = control as FormArray;
items.push(this.fb.control(''));
}
addWord() {
var array = this.myForm.get('items') as FormArray;
array.push(this.fb.group({
word: this.fb.control(''),
properties: this.fb.array([
this.fb.group({
property: this.fb.control(''),
value: this.fb.control('')
})
]),
}));
}
get items() {
return this.myForm.get('items') as FormArray;
}
}

<form [formGroup]="myForm">
<div formArrayName="items">
<div *ngFor="let item of items.controls; let i=index">
<div [formGroupName]="i">
<input formControlName="word" matInput>
<div formArrayName="properties">
<div *ngFor="let rowItem of items.controls[i].controls['properties'].controls; let j=index">
<div [formGroupName]="j">
<input formControlName="property" matInput>
<input formControlName="value" matInput>
</div>
</div>
<br>
</div>
<button (click)="addWordProperty(items.controls[i].value.properties)">Add property</button>
<br>
</div>
</div>
<button (click)="addWord()">Add word</button>
</div>
</form>

Related

vuedraggable custom style for each individual item

I need to have each vuedraggable item to have different styling in the wrapper tag (for example based on the element's index) like so:
<div class="wrapper">
<div class="grid_item" style="grid-row: 1">
I am Item 1
</div>
<div class="grid_item" style="grid-row: 2">
I am Item 2
</div>
<div class="grid_item" style="grid-row: 3">
I am Item 3
</div>
</div>
I know this is a very simple example that doesn't really need the index but suppose a more complex scenario where it is necessary to have access to the index in the draggable component (not its child template).
Suppose the current component looks like this:
<template>
<div class="wrapper">
<draggable
:list="items"
class="grid_item"
item-key="name">
<template #item="{ element }">
I am {{ element.name }}
</template>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {
items: [
{
name: 'Item 1'
},
{
name: 'Item 2'
},
{
name: 'Item 3'
},
],
}
},
methods: {
rowForItem(index) {
return `grid-row: ${index + 1}`;
}
},
}
</script>
<style>
.wrapper {
display: grid;
}
.grid_item {
background-color: gray;
}
</style>
How can I make use of the rowForItem method here?

How to obtain data in child component in Vue.js 3?

I have a Date component that contains a calendar and time. I can select a date and time from this. I have another component called DatePopup that shows a calendar and time as a popup. This component contains OK and Cancel buttons. If a user selects OK, I want to get the date reactive variable in child component, i.e., Date.
How can I achieve this structure so that I can obtain the data from parent component?
Note that I use Quasar.
Date.vue
<template>
<div class="q-pa-md q-gutter-sm">
<q-badge color="yellow-illerarasi-film-teskilati" text-color="black">
Deadline: {{ date.full }}
</q-badge>
</div>
<div class="q-gutter-md row items-start">
<q-date v-model="date.full" mask="YYYY-MM-DD HH:mm" color="yellow-illerarasi-film-teskilati" text-color="black"/>
<q-time v-model="date.full" mask="YYYY-MM-DD HH:mm" color="yellow-illerarasi-film-teskilati" text-color="black"/>
</div>
<p> {{ date }}</p>
</template>
<script>
import {reactive} from "vue";
export default {
name: "Date",
setup() {
let date = reactive({
full: "2022-01-01 00:00",
})
return {
date,
}
}
}
</script>
<style scoped>
</style>
DatePopup.vue
<template>
<div class="q-pa-md q-gutter-sm">
<q-dialog v-model="show">
<q-card style="width: 700px; max-width: 80vw;">
<q-card-section>
<div class="text-h6">Choose a Deadline</div>
</q-card-section>
<q-card-section class="q-pt-none">
<Date/>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" text-color="black" v-close-popup/>
<q-btn label="OK" color="yellow-illerarasi-film-teskilati" text-color="black" v-close-popup/>
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script>
import Date from "./Date.vue";
import {reactive} from "vue";
export default {
name: "DatePopup",
components: {Date},
props: {
showPopup: {
type: Boolean,
default: true
}
},
setup(props) {
return reactive({
show: true
})
}
}
</script>
<style scoped>
</style>

ReactJS - how to style the parent?

I would like to put style on the "Forms_formline__3DRaL" div (seen below)
This div comes from a component; and it seems like this style is applied to the input , and not its parent div.
How can I, though reactjs, define the style "display: none" for the parent div?
Here is my code:
<MyInput
style={{
display:
type == 1
? 'none'
: 'block',
}}
id="name"
type="text"
label={t('elevation_loss')}
value="name"
/>
Here is the result in my chrome source:
<div class="Forms_formline__3DRaL">
<label for="name">Name *</label>
<input type="text" id="name" value="" style="display: none;">
</div>
You can try doing something like this
import { CSSProperties, FC } from 'react';
export const MyInput: FC<{
styleOverrides: {
container: CSSProperties;
};
id: string;
type: string;
label: string;
}> = ({ styleOverrides }) => {
return (
<>
<div className="Forms_formline__3DRaL" style={styleOverrides.container}>
...
</div>
</>
);
};

How to Display the Template instead of the Code of the Template?

The problem is that I get a display like bunch of codes(look to the image) in the Card and my purpose is to display the template not the code of the template
PS: if I get that code storing in DB and put it manual in the card it works.
Please any ideas or help?.
Display
App.component.ts
import { Component, ViewChild} from '#angular/core';
import { EmailEditorComponent } from 'angular-email-editor';
import { SenderService } from "../app/sender.service";
import sample from './sample.json';
import { TemplateCard } from "src/app/template";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public templates:TemplateCard[];
public model: any;
options = {
};
gridColumns = 3;
toggleGridColumns() {
this.gridColumns = this.gridColumns === 3 ? 4 : 3;
}
constructor(public http:SenderService) { }
ngOnInit() {
}
#ViewChild('editor')
private emailEditor: EmailEditorComponent = new EmailEditorComponent;
GetDataTemplate(){
this.model=this.http.getEmail("http://localhost:3000/savetemplate").subscribe( data => {
this.templates=data;
console.log(this.templates); // this is where the problem appears
},
err => console.error(err),
() => console.log('template completed')
);
}
editorLoaded() {
this.emailEditor.editor.loadDesign(sample
);
}
//App.component.html
<div class="content" >
<div fxLayout="" fxLayoutGap="10px grid" >
<div [fxFlex]="(100/gridColumns) + '%'" fxFlex.xs="100%" fxFlex.sm="33%" >
<mat-card class="mat-elevation-z4 info" >
<mat-card-content *ngFor="let item of templates" >
<!-- partie Html will be set here to fit -->
{{item.templatebody}}
</mat-card-content>
</mat-card>
</div>
<mat-card class="mat-elevation-z4 info">
<mat-card-content >
<!-- partie Html will be set here to fit -->
</mat-card-content>
</mat-card>
<mat-card class="mat-elevation-z4 info">
<mat-card-content >
<!-- partie Html will be set here to fit -->
</mat-card-content>
</mat-card>
</div>
</div>
Amine, you can use [innerHTML] for this purpose. You just need to change this line:
<mat-card class="mat-elevation-z4 info" >
<mat-card-content *ngFor="let item of templates" >
<!-- partie Html will be set here to fit -->
<div [innerHTML]="item.templatebody">
</div>
</mat-card-content>
</mat-card>
If Angular shows to you an error saying that this action needs to be sanitized: You can follow this tutorial: Medium

How to catch click event with addEventListener

I am having a modal with button inside. Unfortunatly, it's not properly working. During the build I got an error : TypeError: Cannot read property 'addEventListener' of null
Below is the code:
import React from "react";
import { Modal } from "react-bootstrap";
import '../../assets/styles/Login.css';
class LoginRegisterModal extends React.Component {
constructor(props, context) {
super(props);
this.state = {show: false};
}
componentDidMount(){
const signUpButton = document.getElementById('signUp');
const signInButton = document.getElementById('signIn');
const container = document.getElementById('container');
signUpButton.addEventListener('click', () => {
container.classList.add('right-panel-active');
});
signInButton.addEventListener('click', () => {
container.classList.remove('right-panel-active');
});
}
....
render() {
const styleModal = {
marginTop: "15%",
marginLeft: "30%",
padding: 0,
width:770,
height:480,
backgroundColor:"#ffffffff",
borderRadius:21.5,
}
return (
<Modal show={this.state.show} style={styleModal} >
<div class="container" id="container">
<div>
.....
</div>
<div class="overlay-container">
<div class="overlay">
<div class="overlay-panel overlay-left">
<h1>Sign in.</h1>
<p>
Nice to see you again.Login and continue the journey.
</p>
<button class="ghost" id="signIn">Sign In</button>
</div>
<div class="overlay-panel overlay-right">
<h1>Hey, new friend!</h1>
<p>New to the Village? Sign up and start your journey</p>
<button class="ghost" id="signUp">Sign Up</button>
</div>
</div>
</div>
</div>
</Modal>
);
}
}
export default LoginRegisterModal;
I have tried adding a if condition before the addListener but it's just fixing the error but not working.
Also I have tried to replace by onClick instead but it's not working the code
signUpButton = () => {
container.classList.add('right-panel-active');
}
but container is not known..
Any idea?
Thanks
Your code should be inside component did mount method componentDidMount(). That's because when you look for a element with id "signUp" it doesn't exist yet. I don't encourage you to do what you are doing. A better approach would be <button onClick={myMethod}>

Resources