Comments (4)
Yes, if you plan to reuse an address block throughout your application, building it as a nested component is a great idea!
Since the course was focused on forms, and because the array chapter was already a bit complex ... I don't plan to make this change to the course.
Have you tackled it yet? If so, I'd be happy to post a link to your github or blog to share your solution with others.
Thanks!
from angular-reactiveforms.
Thanks for the answer.
I don't have a blog, or a github with an example of it, in fact, I didn't make it work, sรณ i'm going to show you my code and how far I got so you can see if it's in the right direction and how close I am to do this.
First I created a base class for a form component I called it BindComponent because it's used for combos and lists, with a way to select an object and bind it to my model property.
`import {Component, ViewChild, ChangeDetectorRef, Input, Output, EventEmitter, OnInit} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
import {Observable} from 'rxjs/Observable';
const noop = () => { };
export class BindComponent implements OnInit, ControlValueAccessor {
protected values: IBindValue[] = [];
public Source: (obj?:any) => Observable<IBindValue[]>;
@Input() label: string;
@Input() Children: BindComponent[] = [];
@Input() readonly: boolean = false;
private _model: any; // stored value
@Input() AutoLoad: boolean = false;
@Input() idhtml: string;
@Input() property: string = "";
@Output() init: EventEmitter<BindComponent> = new EventEmitter<BindComponent>(false);
//@Output() ModelChange: EventEmitter<any> = new EventEmitter<any>(false); // an event emitter
//Placeholders for the callbacks
private _onTouchedCallback: () => void = noop;
private _onChangeCallback: (_: any) => void = noop;
protected initialized: boolean = false;
public ngOnInit() {
this.init.emit(this);
if (this.AutoLoad) {
this.load();
}
}
public load(obj?) {
this.clearChildren();
if (this.Source) {
this.Source(obj).subscribe(x => this.values = x, e => { }, () => {
this.setSelectItem();
this.finallyLoad();
this.initialized = true;
});
}
}
public get Model(): any {
return this._model;
};
//set accessor including call the onchange callback
public set Model(v: any) {
//if (v !== this._model) { //LUIZ: fiz essa alteracao para que campos nulos atualizem os filhos temos que ficar atento para possiveis efeitos colaterais
this._model = v;
this._onChangeCallback(v);
this.onSetModel();
for (let child of this.Children) {
if (child) {
child.load();
}
}
//}
}
protected onSetModel() { }
protected finallyLoad() { }
protected setSelectItem() { }
protected clearChildren() {
for (let child of this.Children) {
if (child) {
child.clear();
child.clearChildren();
}
}
}
protected clear() {
this.values = new Array<IBindValue>();
}
//Set touched on blur
onTouched() {
this._onTouchedCallback();
}
//From ControlValueAccessor interface
writeValue(value: any) {
this._model = value;
this.onWriteValue();
}
protected onWriteValue() { }
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this._onChangeCallback = (x) => {
if (this.initialized) {
fn(x);
}
}
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this._onTouchedCallback = () => {
if (this.initialized) {
fn();
}
}
}
public DefaultMap<T>(source: T[], value: (T: T) => string, text: (T: T) => string): IBindValue[] {
return source.map<IBindValue>((e, i, a) => new BindValue(value(e), text(e), false, e));
}
public DefaultMapValue<T>(source: T[], value: (T: T) => string, text: (T: T) => string, object:(T:T)=> any ): IBindValue[] {
return source.map<IBindValue>((e, i, a) => new BindValue(value(e), text(e), false, object(e)));
}
}
export interface IBindValue {
value: string;
text: string;
selected: boolean;
model: any;
}
export class BindValue implements IBindValue {
constructor(public value: string, public text: string, public selected: boolean, public model: any) {
}
}`
Then moving on my example I created a liscomponent
`import { Component, Provider, forwardRef, ExistingProvider } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BindComponent, IBindValue } from './bind.component'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { List } from '../models/list.model'
const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ListComponent),
multi: true
};
@component({
selector: 'tst-dual-list',
templateUrl: 'app/common/components/list.component.html',
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class ListComponent extends BindComponent {
protected finallyLoad() {
var selected = this.getSelectedValue();
if (selected) {
this.selectItems(selected.map(x => x.value));
}
}
protected setSelectItem() {
var modelproperty: any[] = this.Model ? List.getArrayList(this.Model, v => v) : [];
modelproperty = modelproperty.map(x => x[this.property] ? x[this.property] : -1);
for (let item of this.values) {
var itemproperty = item.model && item.model[this.property] ? item.model[this.property] : -2;
item.selected = modelproperty.indexOf(itemproperty) >= 0;
}
}
public getSelectedValue(): IBindValue[] {
var retorno = [];
for (let v of this.values) {
if (v.selected) {
retorno.push(v);
}
}
return retorno;
}
selectItem(selectedValue: string) {
for (let value of this.values) {
if (value.value == selectedValue) {
value.selected = !value.selected;
}
}
this.emitSelected();
}
private selectItems(selectedvalue: string[]) {
for (let value of this.values) {
if (selectedvalue.indexOf(value.value) >= 0) {
value.selected = true;
}
else {
value.selected = false;
}
}
this.emitSelected();
}
protected emitSelected() {
var selected = this.getSelectedValue();
var retorno = new List<any>();
if (selected) {
retorno.$values = selected.map(x => x.model);
};
this.onTouched();
this.Model = retorno;
}
}with a vey simple html
- {{model.text}}
and it's usage is very simple
<tst-dual-list (init)="documentsInit($event)" [readonly]="readOnly" [(ngModel)]="model.Model.Documents" [property]="'Id'" [idhtml]="'documents'" [AutoLoad]="true" formControlName="Documents" [Children]="dependenciesdocumentsBindComponent"></tst-dual-list>
and this event as the init of the component
` private documentsBindComponent: BindComponent;
private dependenciesdocumentsBindComponent: BindComponent[] = [];
public documentsInit(event: BindComponent) {
this.documentsBindComponent = event;
//push to dependencies
this.documentsBindComponent.Source = () => this._bindService.documentsAutocomplete().map((values) => this.documentsBindComponent.DefaultMap(values, (e) => e.Id.toString(), (e) => e.Description));
}`
what do you think of it? I'm in the right path?
Thanks again, I'm a huge fan of you.
from angular-reactiveforms.
Thank you for the kind words!
I've spent the better part of May at conferences and/or out of the country. I have not (and most likely will not) really have time to go through all of this posted code in detail.
If you do want to set up a github account with something at least runable (even if it does not work), I will find time to go through it.
from angular-reactiveforms.
Thanks for the time spent on this topic
I created another post with the code, it works almost fine, but the form doesn't refresh the model state unless some change is made in the parent form
I zipped the file and uploaded it to the issue.
from angular-reactiveforms.
Related Issues (20)
- Cannot find control with path: angular2 HOT 1
- ng serve throws : The "@angular/compiler-cli" package was not properly installed - Error HOT 5
- Best Online Content Presenter HOT 1
- Not Working HOT 4
- You seem to not be depending on "@angular/core" and/or "rxjs". This is an error HOT 4
- Deborah's next course: ACME Products created with Angular-cli HOT 3
- generic validator and dynamic elements HOT 1
- Reactive form does not load with it's initial value HOT 2
- npm ERR! 404 Not Found HOT 3
- FormArray controls not accessable inside the FormBuilder
- npm test has two failures HOT 2
- Generic validator with AsyncValidator
- Demo Final is empty HOT 2
- Error when runing with npm start HOT 3
- How to setup Nested Form Groups in the APM updated? HOT 6
- subscription HOT 4
- Unit Tests HOT 1
- Create a Form component and an Array Component #2 HOT 1
- Cannot find control with path: angular2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from angular-reactiveforms.