Coder Social home page Coder Social logo

Comments (4)

DeborahK avatar DeborahK commented on May 18, 2024 1

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.

luizfbicalho avatar luizfbicalho commented on May 18, 2024

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.

DeborahK avatar DeborahK commented on May 18, 2024

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.

luizfbicalho avatar luizfbicalho commented on May 18, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.