I am having difficulties implementing NgForm to handle form data. I currently have my a working solution simply implementing two-way binding like so:
post_create_component.dart
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'package:angular_components/material_button/material_button.dart';
import 'package:angular_components/material_icon/material_icon.dart';
import 'package:angular_components/material_input/material_input.dart';
import '../../models/post.dart';
#Component(
selector: 'app-post-create',
styleUrls: [
'package:angular_components/css/mdc_web/card/mdc-card.scss.css',
'post_create_component.css'
],
templateUrl: 'post_create_component.html',
directives: [
formDirectives,
MaterialButtonComponent,
MaterialIconComponent,
materialInputDirectives,
])
class PostCreateComponent {
String enteredTitle = '';
String enteredContent = '';
final _postCreated = new StreamController<Post>();
void onAddPost() {
Post post = Post(this.enteredTitle, this.enteredContent);
_postCreated.add(post);
}
#Output()
Stream<Post> get postCreated => _postCreated.stream;
}
post_create_component.html
<section>
<h2>Post Comments</h2>
<div class="mdc-card demo-size">
<material-input label="Title" floatingLabel [(ngModel)]="enteredTitle" class="wide">
</material-input>
<material-input label="Content" floatingLabel [(ngModel)]="enteredContent" class="wide">
</material-input>
<div class="mdc-card__actions">
<div class="mdc-card__action-buttons">
<material-button raised (click)="onAddPost()">Save Post</material-button>
</div>
<div class="mdc-card__action-icons">
<material-button icon>
<material-icon icon="favorite_border"></material-icon>
</material-button>
<material-button icon>
<material-icon icon="share"></material-icon>
</material-button>
<material-button icon>
<material-icon icon="more_vert"></material-icon>
</material-button>
</div>
</div>
</div>
</section>
I have refactored my code to look like this:
post_create_component.dart
import 'dart:async';
import 'dart:html';
import 'package:angular/angular.dart';
import 'package:angular_forms/angular_forms.dart';
import 'package:angular_components/material_button/material_button.dart';
import 'package:angular_components/material_icon/material_icon.dart';
import 'package:angular_components/material_input/material_input.dart';
import '../../models/post.dart';
#Component(
selector: 'app-post-create',
styleUrls: [
'package:angular_components/css/mdc_web/card/mdc-card.scss.css',
'post_create_component.css'
],
templateUrl: 'post_create_component.html',
directives: [
formDirectives,
MaterialButtonComponent,
MaterialIconComponent,
materialInputDirectives,
NgForm,
])
class PostCreateComponent {
String enteredTitle = '';
String enteredContent = '';
final _postCreated = new StreamController<Post>();
void onAddPost(NgForm form) {
window.console.log(form.value);
Post post = Post(form.value.title, form.value.content);
_postCreated.add(post);
}
#Output()
Stream<Post> get postCreated => _postCreated.stream;
}
post_create_component.html
<section class="data-entry">
<h2>Post Comments</h2>
<div class="mdc-card demo-size">
<form #postForm="ngForm">
<material-input label="Title" floatingLabel class="wide" ngControl="title" required name="title" ngModel>
</material-input>
<material-input label="Content" floatingLabel class="wide" ngControl="content" required name="content"
ngModel>
</material-input>
<div class="mdc-card__actions">
<div class="mdc-card__action-buttons">
<material-button raised type="submit" (trigger)="onAddPost(postForm)">Save Post</material-button>
</div>
<div class="mdc-card__action-icons">
<material-button icon>
<material-icon icon="favorite_border"></material-icon>
</material-button>
<material-button icon>
<material-icon icon="share"></material-icon>
</material-button>
<material-button icon>
<material-icon icon="more_vert"></material-icon>
</material-button>
</div>
</div>
</form>
</div>
</section>
When I submit my form, I get the following error:
Console log error in chrome dev tools.
Clearly I am not accessing the Identity Map correctly. How do I go about accessing the values? Any help is appreciated.
UPDATE
After many hours of searching, the solution was extremely easy:
void onAddPost(NgForm form) {
Post post = Post(form.value["title"], form.value["content"])
_postCreated.add(post);
}
I will leave the question in case anyone searches something similar in the future.
Related
I am trying to console the result from react draft wysiwyg and I am getting editorState.getCurrentContent is not a function. I am not sure where did I go wrong. Many thanks in advance and greatly appreciate any helps. Thanks
import React,{useState} from 'react'
import { render } from 'react-dom';
import { Editor } from 'react-draft-wysiwyg';
import {EditorState} from "draft-js";
import { stateToHTML } from "draft-js-export-html";
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
const AddBlog= () => {
const [editorState, setEditorState] = useState(
() => EditorState.createEmpty(),
);
const handleChange = (editorState) =>{
const contentState = stateToHTML(editorState.getCurrentContent())
// JSON.stringify(convertToRaw(editorState.getCurrentContent()))
console.log(contentState)
}
return (
<div className="container-fluid">
<div className="card-wrapper-tutorial">
<div className="card">
<div className="card-body">
<h4 className="card-title">New Code Snippet</h4>
<form autoComplete="off">
<div className="form-group">
<label htmlFor="title">Title</label>
<input id="title" type="text" className="form-control" placeholder="title" name="title" disabled = {disabled} onChange={handleChange} />
</div>
<div className="form-group">
<label htmlFor="body">Body</label>
<Editor
defaultEditorState={editorState}
onEditorStateChange={setEditorState}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
onChange={handleChange}
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block">
{loading && <i className="fa fa-refresh fa-spin"></i>}
Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
export default AddBlog
You have the handleChange in two places which they don't belong
on the input's onChange, the callback for onChange would not
be an editorState but and input event e.
on the Editor
component, the callback for onChange would not be an editorState
also but a RawDraftContentState which is the string html.
You probably meant to place it in the onEditorStateChange for Editor:
<Editor
defaultEditorState={editorState}
onEditorStateChange={editorState => {
setEditorState(editorState);
handleChange(editorState);
}}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
/>
or leaving the code as is and modifying the handleChange to expect RawDraftContentState
const handleChange = (rawDraftContentState) =>{
// no need for convertToRaw or stateToHtml anymore
console.log(rawDraftContentState)
}
I was tried to using angular material drag and drop and I was looping through cdkDropList container. I'm not able to drag the items from Catalogue to (to do )list. If I'm not using any loop then it's working fine.
Here is my stackblitz link
I tried without loop that is working fine
<div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[done]" [id]="item"
class="column-list" (cdkDropListDropped)="drop($event)"
<div class="column-box" [ngClass]="[(item.Name=='R') ?'Red':'',(item.Name=='G') ?'Green':'',(item.Name=='B') ?
'Blue':'',(item.Name=='Y') ?'Yellow':'',(item.Name=='O') ?'Orange':'']" *ngFor="let item of items3" cdkDrag cdkDragLockAxis="y">
{{item.Name}}</div>
</div>
but if I'm adding this *ngFor it's not working
<div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[done]" [id]="item"
class="column-list" (cdkDropListDropped)="drop($event)" *ngFor="let item of signalContainer">
<div class="column-box" [ngClass]="[(item.Name=='R') ?'Red':'',(item.Name=='G') ?'Green':'',(item.Name=='B') ?
'Blue':'',(item.Name=='Y') ?'Yellow':'',(item.Name=='O') ?'Orange':'']" *ngFor="let item of items3" cdkDrag cdkDragLockAxis="y">
{{item.Name}}</div>
</div>
COMPONENT.TS
import {Component} from '#angular/core';
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '#angular/cdk/drag-drop';
#Component({
selector: 'cdk-drag-drop-connected-sorting-example',
templateUrl: 'cdk-drag-drop-connected-sorting-example.html',
styleUrls: ['cdk-drag-drop-connected-sorting-example.css'],
})
export class CdkDragDropConnectedSortingExample {
initialSkeleton = new Array(5);
done = [
'A','B','C','D','E'
];
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer === event.container) {
debugger;
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
event.container.data[event.currentIndex] = event.previousContainer.data[event.previousIndex];
}
}
}
HTML
<div class="example-container">
<h2>To do</h2>
<div cdkDropList #todoList="cdkDropList" [cdkDropListData]="initialSkeleton" [cdkDropListConnectedTo]="[doneList]" class="example-list"
(cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let item of initialSkeleton" cdkDrag>
<div class="child" >{{item }}</div>
</div>
</div>
</div>
<div class="example-container">
<h2>Done</h2>
<div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList]" class="example-list"
(cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let item of done" cdkDrag>{{item}}</div>
</div>
</div>
I've modified the code now you will be able to drag and drop items in the for loop. Hope it answers your question.
I am working with angular6 CdkDragDrop module.
I tried to create a custom directive "appRenderControl" that
would listen to CdkDragDrop event, when an item is dropped into
the container.
However, the directive is not being listened at the
moment.
Below is my code, can anyone please point out what is going wrong.
<div id='main'>
<div [style.background]="'blue'" id='components'>
<mat-card>
<mat-card-title>Components</mat-card-title>
<mat-card-content >
<div id="controls" cdkDropList #inactiveList="cdkDropList"
[cdkDropListConnectedTo]="[activeList]">
<div class="control-button" *ngFor="let item of controls" cdkDrag >
<div class="custom-drag-drop" *cdkDragPlaceholder>{{item}} </div>
{{item}}
</div>
</div>
</mat-card-content>
</mat-card>
</div>
<div [style.background]="'white'" id='preview' cdkDropList
#activeList='cdkDropList' appRenderControl>
</div>
</div>
</div>
</div>
Directive.ts
import { Directive, HostListener} from '#angular/core';
import {CdkDragDrop} from '#angular/cdk/drag-drop';
#Directive({
selector: '[appRenderControl]'
})
export class RenderControlDirective {
constructor() {
}
//This is not being listened
#HostListener('CdkDragDrop', ['$event'])
onItemDropped(event: CdkDragDrop<string[]>): void {
console.log('directive being called')
}
}
I'm newbie in Angular 2 and trying to write a simple ng-form following by official tutorial.
If I'm using simple array from tutorial, it works fine:
powers = ['Really Smart', 'Super Flexible',
'Super Hot', 'Weather Changer'];
But when I'm changing it on my custom array from http
public departments = [];
constructor(http: Http) {
http.get('/api/departments')
.map((res: Response) => res.json())
.subscribe((departments: Array<Object>) => this.departments = departments);
}
I'm getting an error:
error_handler.js:51 Error: Uncaught (in promise): Error: Error in ./AddClientComponent class AddClientComponent - inline template:41:12 caused by: Cannot find a differ supporting object '[object Object]' of type 'departments'. NgFor only supports binding to Iterables such as Arrays.
So where is my mistake and what am I missing? thanks in advance.
AddClientComponent
import 'rxjs/add/operator/map';
import {Component} from '#angular/core';
import {Http, Response} from '#angular/http';
import { DepartmentsComponent } from '../departments/departments.component';
#Component({
selector: 'app-add-client',
templateUrl: './add-client.component.html',
styleUrls: ['./add-client.component.css']
})
export class AddClientComponent {
public departments = [];
public firstName = '';
public lastName = '';
public id = null;
constructor(http: Http) {
http.get('/api/departments')
.map((res: Response) => res.json())
.subscribe((departments: Array<Object>) => this.departments = departments);
}
model = new Employee(
this.id,
this.firstName,
this.lastName,
this.departments
);
submitted = false;
onSubmit() { this.submitted = true; }
active = true;
}
export class Employee {
constructor(
public id: number,
public firstName: string,
public lastName: string,
public departments: any
) { }
}
html
<div class="container">
<div [hidden]="submitted">
<h1>Employee Form</h1>
<form *ngIf="active" (ngSubmit)="onSubmit()" #employeeForm="ngForm">
<div class="form-group">
<label for="firstName">First Name</label>
<input type="text" class="form-control" id="firstName"
required
[(ngModel)]="model.firstName"
name="firstName"
#firstName="ngModel" >
<div [hidden]="firstName.valid || firstName.pristine"
class="alert alert-danger">
First Name is required
</div>
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input type="text" class="form-control" id="lastName"
required
[(ngModel)]="model.lastName"
name="lastName"
#lastName="ngModel" >
<div [hidden]="lastName.valid || lastName.pristine"
class="alert alert-danger">
Last Name is required
</div>
</div>
<div class="form-group">
<label for="departments">Department</label>
<select class="form-control" id="departments"
required
[(ngModel)]="model.departments"
name="departments"
#departments="ngModel" >
<option
*ngFor="let department of departments"
[value]="department">{{department.name}}
</option>
</select>
<div [hidden]="departments.valid || departments.pristine"
class="alert alert-danger">
Department is required
</div>
</div>
<button type="submit"
class="btn btn-default"
[disabled]="!employeeForm.form.valid">Submit
</button>
<!--<button type="button"-->
<!--class="btn btn-default"-->
<!--(click)="newHero()">New Hero-->
<!--</button>-->
</form>
</div>
<div [hidden]="!submitted">
<h2>You submitted the following:</h2>
<div class="row">
<div class="col-xs-3">First Name</div>
<div class="col-xs-9 pull-left">{{ model.firstName }}</div>
</div>
<div class="row">
<div class="col-xs-3">Last Name</div>
<div class="col-xs-9 pull-left">{{ model.lastName }}</div>
</div>
<div class="row">
<div class="col-xs-3">Department</div>
<div class="col-xs-9 pull-left">{{ model.departments }}</div>
</div>
<br>
<button class="btn btn-default" (click)="submitted=false">Edit</button>
</div>
</div>
Use a different name for
#departments="ngModel"
I think it overloads the departments property of the class used in *ngFor
Try changing type
public departments: Array<any> = [];
constructor(http: Http) {
http.get('/api/departments')
.map((res: Response) => res.json())
.subscribe((departments: Array<any>) => this.departments = departments);
}
Look at my plunkr:
http://plnkr.co/edit/hB34VjxP98uz1iAYS7Dw?p=preview
Name is included in myform.form, but Name1 of component inner is not. How do I include Name1 in myform?
<div class="container">
<div [hidden]="submitted">
<h1>Hero Form</h1>
<form #heroForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" required
[(ngModel)]="modelname"
name="name" #name="ngModel" >
<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
Name is required
</div>
<inner></inner>
</div>
</form>
{{heroForm.form.value | json}}
</div>
Template of inner.component:
<label for="name">Name1</label>
<input type="text" class="form-control" required
[(ngModel)]="modelname1"
name="name1" #name1="ngModel" >
<div [hidden]="name1.valid || name1.pristine" class="alert alert-danger">
Name1 is required
</div>
See this issue:
https://github.com/angular/angular/issues/9600
I have fixed your code to accomplish your end goal. Now both Name and Name1 are included in the form and values are coming up on the display for you
I have created a fork from your plnkr and fixed your code to support your use case. Please take a look : http://plnkr.co/edit/UHkwJ9?p=preview
inner.component.html is changed as :
<label for="name">Name1</label>
<input type="text" class="form-control" required
[(ngModel)]="modelname1" [formModel]="form" [registerModel]="name2"
name="name2" #name2="ngModel" >
<div [hidden]="name2.valid || name2.pristine" class="alert alert-danger">
Name1 is required
</div>
hero-form.component.ts is changed as :
<inner [form]="heroForm"></inner>
New Directive is added which will register a new control in the existing form reference :
import { Directive, ElementRef, Input, OnInit } from '#angular/core';
import { NgModel, NgForm } from '#angular/forms';
#Directive({
selector: '[formModel]'
})
export class FormModelDirective implements OnInit {
private el: HTMLInputElement;
#Input('formModel') public form: NgForm;
#Input('registerModel') public model: NgModel;
constructor(el: ElementRef) {
this.el = el.nativeElement;
}
ngOnInit() {
if (this.form && this.model) {
this.form.form.addControl(this.model.name, this.model.control);
}
}
}
Output Image :
Output of the plnkr code
Reference : plnkr.co/edit/GG2TVHHHGbAzoOP5mIRr?p=preview