<file_sep>import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from 'firebase';
import {AngularFireAuth } from '@angular/fire/auth';
import { first } from 'rxjs/operators';


@Injectable({
  providedIn: 'root'
})
export class AuthService {
  public user:User;

  constructor(public afAuth: AngularFireAuth) { }


  async login(email:string, password:string){
    try{
      const result = await this.afAuth.auth.signInWithEmailAndPassword(
        email,
        password
        );
        return result;
      }catch (error) {
      console.log(error);
    }  
  }


  async register(email:string,password:string){
    try{
      const result = await this.afAuth.auth.createUserWithEmailAndPassword(
        email,
        password
        );
      return result;
    }catch(error){console.log(error)}
    }
    
    
  async logout(){
    try{
      await this.afAuth.auth.signOut();
    }catch(error){
      console.log(error);
    }
  }


  getCurrentUser(){
    return this.afAuth.authState.pipe(first()).toPromise();
  }
}
<file_sep>Descripción
***************************
Este es la documentación de unproyecto realizado en angular 10 

Contenido
===========================
1.  Descripción
2.  Desario 1
3.  Desafio 2
4.  Desafio 4


<file_sep>import { TestBed } from '@angular/core/testing';

import { MunicipiosColombiaService } from './municipios-colombia.service';

describe('MunicipiosColombiaService', () => {
  let service: MunicipiosColombiaService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(MunicipiosColombiaService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});
<file_sep>*********
Registro
*********

1. Descripción
    En este componente se debe mostrar todos los registros almacenados en la base de datos. 

2. Importaciones
    Para este componente de debe importar el servicio a la base de datos, el servicio para la 
    autentificación de usuarios y el servicio a una API para obtener los municipios y departamentos 
    de Colombia.
    ::

        import { RegistrosService } from 'src/app/services/registros.service';
        import { AngularFireAuth } from '@angular/fire/auth';
        import {MunicipiosColombiaService} from 'src/app/services/municipios-colombia.service'

    También se importan componentes de Angular Material para la implementación de ventana
    emergente o modal y de se importa el componente Router para un redireccionamiento al 
    concluir el registro.
    ::

        import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
        import { DialogComponent } from '../dialog/dialog.component';
        import { Router } from '@angular/router';

    
3. Desarrollo
    El constructor se inyectan todos los objetos a necesarios para el desarrollo del componente.
    ::
        private afAuth: AngularFireAuth,
        private registrosServiceF : RegistrosService,
        private municipiosService:MunicipiosColombiaService,
        private matDialog: MatDialog,
        private router:Router,   
        private fb:FormBuilder

    Para la captura de datos se emplea los formularios reactivos, para ello se declara un
    objeto de la clase FormGroup y se inicializa creando un FormControl para cada campo requerido, 
    declarando su estado y sus respectivo valida daciones.  
    ::
        this.registerForm= this.fb.group({
            photoUrl: new FormControl(''),      
            cedula: new FormControl('', [Validators.required, this.validarCedula]),
            .
            .
            .
            descripcion: new FormControl('', [Validators.required]),
            })

    En el método ngOnInit se realiza la obtención de todos los registros almacenados en la 
    base de datos con el fin de realizar validaciones con dichos datos, estos datos son 
    almacenados en un array.
    ::

        this.registrosServiceF.getRegistros().subscribe((rgSnapshot) => {
        this.listaRegistros = [];
        rgSnapshot.forEach((rgData: any) => {
            this.listaRegistros.push({
            id: rgData.payload.doc.id,
            data: rgData.payload.doc.data()
                                
            });
        })
        });

    En el mismo método se obtienen los datos de la API sobre los municipios y 
    departamentos de Colombia y se asignan en un array.
    ::
        
        this.municipiosService.getDatos().subscribe(datos =>{      
            // Se almacenan los municipios
            this.datosMunicipios=datos.map(data=>         
            data.municipio);
            // Se almacenan los departamentos
            this.datosDepartamentos=datos.map(data=>           
            data.departamento);

            // Se elimina los departamentos repetidos
            let NuevosDatosDepartamentos:string[]=[];
            for (let posicion = 0; posicion < this.datosDepartamentos.length; posicion++) {
            const element = this.datosDepartamentos[posicion];          
            if(posicion==0)
            {
                NuevosDatosDepartamentos.push(element.toString());
            }
            else{
                if(NuevosDatosDepartamentos.includes(element.toString())==false)
                {
                NuevosDatosDepartamentos.push(element.toString());
                }
            }
            }

    En el método ngOnInit también se declara un array observador para municipio y otro para departamento
    encargado del filtrado en el componente html. 
    ::
        this.filteredMunicipios = this.registerForm.controls.ciudad.valueChanges
            .pipe(
                startWith(''),
                map(value => this._filterMunicipios(value))
            );


            this.filteredDepartamentos = this.registerForm.controls.departamento.valueChanges
            .pipe(
            startWith(''),
            map(value => this._filterDepartamentos(value))
            );

    Al mismo tiempo se realiza a los métodos que devuelve un array con las filtraciones realizadas
    ::
        _filterMunicipios(value: string): string[] {
            const filterValue = value.toLowerCase();
            return this.datosMunicipios.filter(option => option.toLowerCase().includes(filterValue));
        }

        _filterDepartamentos(value: string): string[] {
            const filterValue = value.toLowerCase();
            return this.datosDepartamentos.filter(option => option.toLowerCase().includes(filterValue));
        }
   

    Cada campo del formulario tiene su respectivo control y todos son requeridos, ahora hay campos 
    especiales que requieren validaciones personalizadas empezando por el campo del numero de 
    cedula el cual no debe estar registrado con anterioridad en la base de datos.

    En la declaración del control de para el campo de cedula se añade su validador personalizado
    ::
        cedula: new FormControl('', [Validators.required, this.validarCedula]),
    Para la validación personalizada se crea el siguiente método.
    ::
        private validarCedula: ValidatorFn =(control: AbstractControl): ValidationErrors | null => {
            const cedula = control.value;
            let respuesta= null;
            if (this.ValidarExistenciaCedula(cedula)==true) {
            return{ ms: 'para otro forma de error' };
            }    
            return null;
        }
    Internamente este método llama a otro método encargado de revisar si la cedula ya existe 
    en los registros obtenidos de la base de datos.
    ::
        ValidarExistenciaCedula(cedulaIn: string): boolean {
            let existeCedula: boolean = false;
            let respuesta: boolean = true;
            // Se compara la cedula ingresada con la cedula de cada registro
            for (let i = 0; i < this.listaRegistros.length; i++) {
            const element = this.listaRegistros[i];
            const { cedula } = element.data;
            if (cedulaIn == cedula) {
                existeCedula = true;
            }      
            }
            if (existeCedula == true) {         
            respuesta = true;
            }   
            else {
            respuesta = false;
            }
            return respuesta;
        }

    Cuando hay la validación verifica la existencia de la cedula ingresada en los registros de 
    la base de datos, el formulario informa inmediatamente al usuario que hay un erro mediante 
    el uso de formularios reactivos y la sincronización entre la vista y el controlador.

    Campo de Cedula en el HTML
    ::
        <mat-form-field appearance="outline">
            <mat-label>Número de cédula</mat-label>
            <input matInput type="number" formControlName="cedula">            
            <mat-error *ngIf="registerForm.controls.cedula.errors">{{errorCedula()}}</mat-error>           
        </mat-form-field>

    Método encargado de informar el tipo de error.
    ::
        errorCedula() {
            if (this.registerForm.controls.cedula.hasError('required')) {
                return 'Ingrese un número de cédula';
            }
            if (this.registerForm.controls.cedula.hasError('ms')) {
                return 'El número de cedula ya ha sido registrado';
            }
        
        }

    
    El mismo comportamiento presenta el campo de Email. 

    Declaración del control email con su validador personalizado
    ::
        email: new FormControl('', [Validators.required, Validators.email, this.validarEmail]),

    Metodo para la validación personalizada.
    ::
        private validarEmail: ValidatorFn =(control: AbstractControl): ValidationErrors | null => {
            const email = control.value;
            let respuesta= null;
            if (this.ValidarExistenciaCorreo(email)==true) {
                return{ ms: 'para otro forma de error' };
            }   
            return null;
        } 

    Método encargado de revisar si el email ya existe en los registros obtenidos de la base de datos.
    ::
        ValidarExistenciaCorreo(correo: string): boolean {
            let existeCorreo: boolean = false;
            let respuesta: boolean = true;
            // Se compara el correo ingresado con el email de cada registro
            for (let i = 0; i < this.listaRegistros.length; i++) {
                const element = this.listaRegistros[i];
                const { email } = element.data;
                if (correo == email) {
                existeCorreo = true;
                }      
            }
            if (existeCorreo == true) {      
                respuesta = true;
            }
            else {
                respuesta = false;
            }
            return respuesta;
        }

    Campo de Email en el HTML
    ::
        <mat-form-field appearance="outline">
            <mat-label>E-mail</mat-label>
            <input matInput placeholder="<EMAIL>" formControlName="email" required>            
            <mat-error *ngIf="registerForm.controls.email.errors">{{errorEmail()}}</mat-error>
        </mat-form-field >

    Método encargado de informar el tipo de error
    ::
        errorEmail() {
            if (this.registerForm.controls.email.hasError('required')) {
                return 'Debe ingresar un email';
            }
            if (this.registerForm.controls.email.hasError('ms')) {
                return 'El email ya ha sido registrado';
            }
            return this.registerForm.controls.email.hasError('email') ? 'Email no válido' : '';
        }

    Otro de los campos con funciones especiales son los de municipio y departamento que emplean un filtro
    de autocompletado y su estructura en HTML es el siguiente aplicable para los dos
    casos cambiando su contenido.
    ::
        <mat-form-field appearance="outline">
            <mat-label>Ciudad</mat-label>
            <input type="text" aria-label="Number" matInput formControlName="ciudad" [matAutocomplete]="auto">
            <mat-autocomplete #auto="matAutocomplete">
                <mat-option *ngFor="let municipio of filteredMunicipios | async" [value]="municipio">
                    {{municipio}}
                </mat-option>
            </mat-autocomplete>
            <mat-error *ngIf="registerForm.controls.ciudad.invalid">Ingrese una ciudad</mat-error>
        </mat-form-field>

    El formulario incluía una sesión donde se debía ingresar de 1 a 3 habilidades mediante checkbox,
    para ello se declaró e inicializo un array con las habilidades preestablecida y un contador de habilidades.
    ::
        listaHabi: any = [
            { id: 1, name: 'Autoconocimiento' },
            { id: 2, name: 'Empatía' },
            { id: 3, name: 'Comunicación asertiva' },
            { id: 4, name: 'Pensamiento crítico' },
            { id: 5, name: 'Toma de decisiones' },
            { id: 6, name: 'Adaptación' },
            { id: 7, name: 'Comunicación' },
            { id: 8, name: 'Trabajo en equipo' },
            { id: 9, name: 'Capacidad de asociación' },
            { id: 10, name: 'Razonamiento' },
        ];
        nHabilidaddes:number=0;
    
    Para el control y majeo de los checkbox se creó un formGoup con un array de forms
    ::  
        checkboxForm = new FormGroup({    
            habiForm: new FormArray([], Validators.required)
        });

    El campo de los checkbox  en el HTML es el siguiente.
    :: 
        <form [formGroup]="checkboxForm">
            <table id="tbl-habilidades">
                <section class="section">
                    <ul >
                        <li *ngFor="let hab of listaHabi">
                        <   mat-checkbox class="example-margin" [value]="hab.name" (change)="onCheckboxChange($event)">{{hab.name}}</mat-checkbox>
                        </li>
                    </ul>              
                </section>
            </table>
            <p></p>           
        </form>
    
    Cada vez que un checkbox sea seleccionado o cambie su estado se ejecuta el siguiente método que 
    obtiene el array de forms y se comprueba si ha sido seleccionado y  el contador de 
    habilidades esta en el rango entre 1 y 3 se suma el formContro al array de forms y 
    se suma el contador de habilidades, si contador de habilidades llega al límite entonces
    si el checkbox seleccionado es des marcado se elimina  ese formControl del array de forms de 
    lo contrario no permitirá seleccionar más checkbox.

    Si el contador de habilidades vuelve a ser 0 por deseleccionar todas las habilidades el 
    fromControl principal es limpiado y actualizado su validador de requerido inhabilitando 
    el botón de enviar el formulario. 
    ::
        onCheckboxChange(e) {
            const habiForm: FormArray = this.checkboxForm.get('habiForm') as FormArray;                
            //si lo chuliaron agrega al array siempre que numero de habilidades sea menor 3
            if (e.checked && this.nHabilidaddes<=2) {            
                habiForm.push(new FormControl(e.source.value));        
                this.nHabilidaddes++;
                //metodo que actuliza las habilidades con el formControl principal
                this.validarHabilidades();
            } else {
            //si numero la habilidad se desmarca elimina habilidad 
            if(e.checked==false)
            {
                const index = habiForm.controls.findIndex(x => x.value === e.source.value);
                habiForm.removeAt(index);
                //metodo que actuliza las habilidades con el formControl principal
                this.validarHabilidades();
                this.nHabilidaddes--;
            }
            //si numero de habilidades esta al limite (3) no agrega nada y no permite chulear
            else{
                e.source._checked=false;
            }
            }

            //si no hay habilidades selecionadas no habilita el boton de registrarse
            if(this.nHabilidaddes==0)
            {
                //restauro el formControl principal, limpiandolo, agregando el validador y actualizandolo
                this.registerForm.controls.habilidades.setValue([]);
                this.registerForm.controls.habilidades.setValidators([Validators.required]);
                this.registerForm.controls.habilidades.updateValueAndValidity();
            }            
            //console.log(this.registerForm.controls.habilidades.value);
        }
    
    El método validarHabilidades() llamado internamente cuando un checkbox cambia su estado es 
    el encargado de obtener el valor de la habilidad y asignarlo al formControl principal de 
    habilidades del cual se tomara su valor parar registrarlo en la base de datos.
    ::        
        validarHabilidades(){

            //obtengo los valores de FormGroup
            const ob = this.checkboxForm.value;
            //obtengo el array habForm de ob ostenido de FormGroup
            const { habiForm } = ob;

            //para almacenar las habilidades
            let habilidadesIn: string = '';    

            for (let i = 0; i < habiForm.length; i++) {

                habilidadesIn += habiForm[i] + " / ";  

            }         
            //asigno las habilidaesIn al valor del formcontrol habilidaes para que lo registre en la bd  
            this.registerForm.controls.habilidades.setValue([habilidadesIn]);
        }

    Para registrar en la base de datos se emplea el siguiente método donde hace otra verificación sobre la cedula y 
    el email que es redundante dado que ya se validó, se le asigna una foto al formControl photoUrl por 
    defecto para su perfil y se procede a realizar el registro en la base de datos, también se muestra 
    un modal agradeciendo el registro y se redirige a la vista de inicio.

    Al momento hacer el registro también se crea un usuario en el sistema mediante su email, siendo este de email y 
    contraseña con el cual tendrá acceso al sistema.
    ::               
        public onRegister(form, documentId = this.documentId) {

            //verifica el resultado del metodo verificar existencia de correo
            if (this.ValidarExistenciaCorreo(this.registerForm.get('email').value)  == false && 
                this.ValidarExistenciaCedula(this.registerForm.get('cedula').value) == false ) 
            {
                // se le asigan una foto por defecto
                this.registerForm.controls.photoUrl.setValue("https://firebasestorage.googleapis.com/v0/b/teamdc-c1083.appspot.com/o/uploads%2FLogo.png?alt=media&token=<PASSWORD>");
                // se realiza el registro en la bd
                this.registrosServiceF.crearRegistro(this.registerForm.value).then(() => {

                //se presenta un modal
                const data={mensaje:'Gracias por registrarte'};
                this.openDialog(data);    
                //se redirige a la pagina home
                this.router.navigate(['/home']);
                
                }, (error) => {
                console.error(error);
                });        
                
                const result= this.afAuth.auth.createUserWithEmailAndPassword(
                this.registerForm.controls.email.value, 
                this.registerForm.controls.email.value);

            
            }
        }|











<file_sep>.. DCTEAM documentation master file, created by
   sphinx-quickstart on Tue Sep  1 15:04:31 2020.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.
   
Bienvenido a la documentación de DCTEAM!
=========================================

Contenido
====================

.. toctree::
   :maxdepth: 2
   
   descripcion
   home
   login
   mi Perfil
   registro
   registros
   video

Descripción
***************************
TEAM DC es una plataforma web responsiva de tipo dashboard, con varias secciones. 
La plataforma permite a los usuarios registrarse y acceder a todas los módulos de la plataforma. 
En la sección mi perfil se visuliza la información del usuario, permitiendo que se editen estos datos y 
agregar una foto de usuario. La seccion de registros muestra información de todos los usuarios registrados.


Framework
------------------------------
El proyecto es desarrollado en el framework Angular creado por Google.
La finalidad de Angular es facilitar el desarrollo de aplicaciones web SPA y 
además dar herramientas para trabajar con los elementos de una web de una manera 
más sencilla y óptima.

Lenguaje de programación
-------------------------------------------
El proyecto es desarrollado en el lenguaje TypeScript.
Es un lenguaje de programación libre y de código abierto desarrollado y mantenido por Microsoft.
Es un superconjunto de JavaScript, que esencialmente añade tipos estáticos y objetos basados en clases.

Estilos
-------------------------------- 
Se trabajó con el lenguaje de etiquetas HTML con el que se define el contenido de las páginas web. 
Para dar estilo se usó SCSS y la librería de estilos Angular Material.








.. sphinx-build -b html source build <file_sep>*********
Mi perfil
*********

1. Descripción
    Este componente contiene un formulario en donde se muestra la información del usuario logueado, permite mediante
    un botón acceder al mismo y editar algunos campos, ademas permite subir una imagen de perfil. 
    del proyecto.

2. Importaciones
    Para este componente de debe importar el servicio de registros y de municipios, y el fireAuth de firebase.
    ::

        import { AngularFireAuth } from '@angular/fire/auth';
        import { RegistrosService } from 'src/app/services/registros.service';
        import { MunicipiosColombiaService } from 'src/app/services/municipios-colombia.service';

    También se importa el servicio de storage de firebase, para mantener la imagen que se sube.
    ::

        import { AngularFireStorage } from '@angular/fire/storage';
        import { finalize } from 'rxjs/operators';

    Además se hacen las importaciones del formulario y sus validadores.
    :: 
        import { FormBuilder, FormGroup, Validators, FormControl, FormArray, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';

    
3. Desarrollo
    En el constructor se inyectan todos los objetos  necesarios para el desarrollo del componente.
    Para manejar el estado de la sesion
    :: 
        private afAuth: AngularFireAuth
    
    Para la conexion con la BD
    ::
        private registroService:RegistrosService

    Para el formulario
    ::
        private fb:FormBuilder,
    El servicio encargado de municipios de colombia
    ::
        private municipiosService:MunicipiosColombiaService,
    
    Para subir la imagen al storage de firebase
    ::
        private storage:AngularFireStorage
    
    Se crea el método para subir la imagen al storage de firebase, condicionando que solo se permite subir 
    imágenes png o jpg y que no superen el tamaño de un MB.
    Además de los metodos que permiten asociar la información del usario logueado y mostrarla en el perfil. 
    Cuenta con un botón de editar que habilita los campos del formulario para hacer la respectiva edición. 
    Y un botón de guardar para enviar los nuevos cambios a la base de datos. <file_sep>.. DCTEAM documentation master file, created by
   sphinx-quickstart on Tue Sep  1 15:04:31 2020.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.
   
Bienvenido a la documentación de DCTEAM!
=========================================

Descripción
***************************
Este es la documentación de unproyecto realizado en angular 10 


1.  Descripción
2.  Desario 1
3.  Desafio 2
4.  Desafio 4


.. toctree::
   :maxdepth: 2
   :caption: Contents:
      descripcion
      home
      login
      mi Perfil
      registro
      registros
      video



.. Indices and tables
==================

* :ref:`genindex`
* :ref:`home`
* :ref:`search`

.. sphinx-build -b html source build <file_sep>import { Component } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';



@Component({
  selector: 'app-sidenav',
  templateUrl: './sidenav.component.html',
  styleUrls: ['./sidenav.component.scss'],
})
export class SidenavComponent {

  //puntos de quiebre del tamaño de la pantalla
  isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
    .pipe(
      map(result => result.matches),
      shareReplay()
    );

    //estado del usuario
    public user$: Observable<any> = this.authSvc.afAuth.user;
    

  constructor(private breakpointObserver: BreakpointObserver, private authSvc: AuthService,
    private router: Router,) { }

    public isLogged: boolean= false;

//método para salir
  async onLogout(){
    try{
      await this.authSvc.logout();
      this.router.navigate(['/login']);
    }
    catch(error){console.log(error)}
  
  
  }
  //método para validar si el usuario esta logueado o no
  onCheckUser():void {
    if (this.authSvc.getCurrentUser()==null) {
      this.isLogged=false;
    } else {
      this.isLogged=true;
    }
  }

}
<file_sep>*********
Iniciar Sesión
*********

1. Descripción
    Este componente es un formulario para dar ingreso a los usuarios registrados a todas las secciones 
    del proyecto.

2. Importaciones
    Para este componente de debe importar el servicio de autenticación y de registros, y el fireAuth de firebase.
    ::

        import { AuthService } from '../../services/auth.service';
        import { RegistrosService } from 'src/app/services/registros.service';
        import { AngularFireAuth } from '@angular/fire/auth';

    También se importan componentes de Angular Material para la implementación de ventana
    emergente o modal y de se importa el componente Router para el redireccionamiento
    ::

        import { MatDialogConfig, MatDialog } from '@angular/material/dialog';
        import { DialogComponent } from '../dialog/dialog.component';
        import { Router } from '@angular/router';

    Además se hacen las importaciones del formulario y sus validadores.
    :: 
        import { FormControl, FormGroup, Validators, } from '@angular/forms';

    
3. Desarrollo
    El constructor se inyectan todos los objetos a necesarios para el desarrollo del componente.
    ::
       private authSvc:AuthService, private router: Router, 
       private registroService:RegistrosService,
       private matDialog: MatDialog,
       public afAuth: AngularFireAuth
    
    Para el inicio de sesión se verifica que el correo ya se encuentre registrado en la base de datos. 
    ::

       this.ValidarExistenciaCorreo(this.loginForm.controls.email.value)==true

    Al realizar esta verificación se muestra un modal si el usuario no está en la base de datos.
    Si el usuario esta registrado, permite ingresar al sistema, redirigiendolo a la página de inicio.   <file_sep>*********
Registros
*********

1. Descripción
    En este componente se debe realizar el registro de cada nuevo usuario, donde el usuario debe ingresar una 
    serie de datos necesaria para su registro.  

2. Importaciones
    Para este componente de debe importar MatTableDataSource para la tabulación de los datos.
    ::
        import { MatTableDataSource } from '@angular/material/table';.

    También se importa el servicio con el cual se establece la conexión con la base de datos.
    ::
        import { RegistrosService } from 'src/app/services/registros.service';.
    
3. Desarrollo
    Se declara y se inicializa un array con los nombres de las columnas a mostrar.
    :: 
        displayedColumns: string[] = ['nombres',...,'descripcion'];.

    Se declara una un objeto de la clase que importamos.
    ::
        dataSource = new MatTableDataSource();.

    En el constructor se inyecta el servicio.
    ::
        private registroService:RegistrosService

    En el método ngOnInit se obtienen todos los datos de la base de datos y se los asigna al objeto dataSource.
    ::
        this.registroService.getAllUser().subscribe(res => 
        this.dataSource.data=res);.

<file_sep>//RegistrosI la I de interfaces
export interface RegistrosI{
 
 photoUrl?: string;   
 nombres: string;
 apellidos: string;
 cedula: number;
 email: string;
 fechaNacimiento: string;
 direccion: string;
 ciudad: string;
 departamento: string;
 pais:string;
 codigoPostal:string;
 profesion: string;
 habilidades: string;
 descripcion:string;

}

export interface ContactosI{
 
     
    nombresCompleto: string;  
    email: string;
    motivo: string;
    mensaje: string;    
   
   }



<file_sep>export const environment = {
  production: true,
  
  firebaseConfig: {
    apiKey: "<KEY>",
    authDomain: "teamdc-c1083.firebaseapp.com",
    databaseURL: "https://teamdc-c1083.firebaseio.com",
    projectId: "teamdc-c1083",
    storageBucket: "teamdc-c1083.appspot.com",
    messagingSenderId: "487516474484",
    appId: "1:487516474484:web:3514ec6d1e078126195521"
  }


};
<file_sep>import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';

// servicio para el manejo de la bd de contactos
import {ContactosService} from 'src/app/services/contactos.service'
// para el manejo de enviar email del nuevo contacto
import 'src/assets/smtp.js';
declare let Email: any;

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.scss']
})
export class ContactComponent implements OnInit {

  //para el formGroup
  contactForm: FormGroup;

  listaContactos;

  // para la validacion del email
  emailPattern = "^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$";

  // para el selecionar el motivo
  motivos= [
    {value: 'Contratos' },
    {value: 'Proyectos'},
    {value: 'Cobranzas'}
  ];

  motivoSeleccionado:string;

  constructor(
    //para el formulario
    private fb:FormBuilder,
    //para el servicio
    private contactosService:ContactosService,
  ) {

    this.contactForm= this.fb.group({

      nombreCompleto: new FormControl('', [Validators.required]),
      email: new FormControl('', [Validators.required, Validators.required, Validators.pattern(this.emailPattern), /*this.validarEmail*/]),
      motivo: new FormControl('', [Validators.required]),
      mensaje: new FormControl('', [Validators.required]),

    });

    

   }

  ngOnInit(): void {

    //limpiar campos
    this.contactForm.setValue({

      nombreCompleto: '', 
      email: '', 
      motivo: '',
      mensaje: ''

    });
    this.getRegistros();
  }

  getRegistros(){

    this.contactosService.getContactos().subscribe((rgSnapshot) => {
      this.listaContactos = [];
      rgSnapshot.forEach((rgData: any) => {
        this.listaContactos.push({
          id: rgData.payload.doc.id,
          data: rgData.payload.doc.data()

        });
      })

      console.log(this.listaContactos);
      

    });

    
  }

  //metodo para informar errores en el campo de email
  errorEmail() {
    if (this.contactForm.controls.email.hasError('required')) {
      return 'Debe ingresar un email';
    }
    if (this.contactForm.controls.email.hasError('ms')) {
      return 'El email ya ha sido registrado';
    }
    return this.contactForm.controls.email.hasError('pattern') ? 'Email no válido' : '';
  }

  //metodo para registrar un nuevo contacto
  oncreate(form){
    //contraseña <PASSWORD>

    this.contactosService.crearContacto(this.contactForm.value).then(() => {

      //toco importar en el archivo angular.json ln 34 y 99
      //"scripts": ["src/assets/smtp.js"]    
      //tomado de https://medium.com/javascript-in-plain-english/send-emails-without-a-server-side-code-with-angular-e<PASSWORD>     
      Email.send({
      Host : 'smtp.elasticemail.com',
      Username : '<EMAIL>',
      Password : '<PASSWORD>',
      To : '<EMAIL>',//<EMAIL>
      From : '<EMAIL>',
      Subject : 'Dc Team Nuevo contacto',
      Body : `
            <h1> Nuevo registro de Contacto</h1>
            <p></p>
            <h3> Nombre: ${this.contactForm.controls.nombreCompleto.value}</h3>
            <h3> Correo: ${this.contactForm.controls.email.value}</h3>
            <h3> Motivo: ${this.contactForm.controls.motivo.value}</h3>
            <h3> Mensaje</h3>
            <p>${this.contactForm.controls.mensaje.value}</p> 
      
      `
      }).then( message => {
        alert(message); 
       } );


      this.contactForm.setValue({

        nombreCompleto: '', 
        email: '', 
        motivo: '',
        mensaje: ''
  
      });
      
    }, (error) => {
      console.error(error);
    }); 
  }

  

}
<file_sep>import { Component, OnInit } from '@angular/core';
import { RegistrosI } from '../../models/registros.interface';
import { MatTableDataSource } from '@angular/material/table';

//paso 1. Importar service
import { RegistrosService } from 'src/app/services/registros.service';



@Component({
  selector: 'app-records',
  templateUrl: './records.component.html',
  styleUrls: ['./records.component.scss']
})
export class RecordsComponent implements OnInit {

  //nombre de las columnas de la tabla
  displayedColumns: string[] = ['nombres', 'apellidos', 'cedula','email','fechaNacimiento',
  'direccion','ciudad','departamento','pais','codigoPostal','profesion','habilidades',
  'descripcion'];

   dataSource = new MatTableDataSource();


 
//paso 2. Inyectar service
  constructor(private registroService:RegistrosService) { }

  ngOnInit(): void {
    this.registroService.getAllUser().subscribe(res => 
      this.dataSource.data=res);
  }
  
  //1.Importar Service
  //2.Inyectar Service
  //3. Utilizar
}
<file_sep>Página de Inicio
============================

Descripción
----------------------------
En esta sección se muestra información de los desarrolladores. 

Importaciones
----------------------------
Para este componente se debe importar el módulo de Angular Material denominado MatCard para las tarjetas de cada 
integrante del equipo.

import { MatCardModule } from '@angular/material/card';

Desarrollo
----------------------------
El MatCard permite agregar una imagen y un texto en este caso los datos del desarrollador.
<file_sep>*********
Página de inicio
*********

1. Descripción
    En este componente se debe mostrar los desarrolladores del proyecto 

2. Importaciones
    Para este componente no se requieren importaciones pues trabaja mas la parte front. Es una visualización de
    tarjetas.
   
3. Desarrollo
    Las card encapsulan los textos y las imagenes para dar una mejor vista de la página. 
<file_sep>import { Component, OnInit } from '@angular/core';
import { FormControl, Validators, FormGroup, FormArray, AbstractControl, FormBuilder, ValidatorFn, ValidationErrors } from '@angular/forms';

//Importar servicio encargada de ls bd
import { RegistrosService } from 'src/app/services/registros.service';
//para el loguin
import { AngularFireAuth } from '@angular/fire/auth';
//Importar servici encargada de los municipios de colombia
import {MunicipiosColombiaService} from 'src/app/services/municipios-colombia.service'
//para el manejo del modal
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { DialogComponent } from '../dialog/dialog.component';

import {map, startWith} from 'rxjs/operators';
import {Observable} from 'rxjs';
import { Router } from '@angular/router';



@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.scss'],
  
})

export class RegisterComponent implements OnInit {

  //donde se van a almacenar los registros de la bd
  public listaRegistros=[];
  
  
  //donde se van a almacenar los datos de la api
  datosMunicipios:string[]=[];
  datosDepartamentos:string[]=[];
  

  //respuesta del Dialog
  resDialog:boolean=false;
    
  //para el autocompletar 
  filteredMunicipios: Observable<string[]>;
  filteredDepartamentos: Observable<string[]>;


  //donde se va almacenar el id de un registro
  public documentId = null;

  //Si el valor es 1, la aplicación estará en modo creación si es 2 se habilita el modo edicion
  public currentStatus = 1;
  //para el formulario
  registerForm: FormGroup;

  //inyectar el servicio rgistros servis encargada de la bd 
  constructor( private registrosServiceF : RegistrosService,
    /// servicio encargado de municipios de colombia
     private municipiosService:MunicipiosColombiaService,
     //inyecto el modal o ventana emergente
     private matDialog: MatDialog,
     //para navegacion
     private router:Router,
     
     // para loguin
     private afAuth: AngularFireAuth,
     //para el formbuilder
     private fb:FormBuilder


    ) { 

      this.registerForm= this.fb.group({
      photoUrl: new FormControl(''),
      nombres: new FormControl('', [Validators.required]),
      apellidos: new FormControl('', [Validators.required]),
      cedula: new FormControl('', [Validators.required, this.validarCedula]),
      email: new FormControl('', [Validators.required, Validators.email, this.validarEmail]),
      fechaNacimiento: new FormControl('', [Validators.required]),
      direccion: new FormControl('', [Validators.required]),
      ciudad: new FormControl('', [Validators.required]),
      departamento: new FormControl('', [Validators.required]),
      pais: new FormControl('', [Validators.required]),
      codigoPostal: new FormControl('', [Validators.required]),
      profesion: new FormControl('', [Validators.required]),
      habilidades: new FormControl('', [Validators.required]),
      descripcion: new FormControl('', [Validators.required]),

      })

    //asigna los valores al formulario como vacios
    this.registerForm.setValue({

      photoUrl:'',
      nombres: '',
      apellidos: '',
      cedula: null,
      email: '',
      fechaNacimiento: '',
      direccion: '',
      ciudad: '',
      departamento: '',
      pais: '',
      codigoPostal: '',
      profesion: '',
      habilidades: '',
      descripcion: '',
    });

  }


  ngOnInit() {
    //obtiene todos los registros de la bd
    this.registrosServiceF.getRegistros().subscribe((rgSnapshot) => {
      this.listaRegistros = [];
      rgSnapshot.forEach((rgData: any) => {
        this.listaRegistros.push({
          id: rgData.payload.doc.id,
          data: rgData.payload.doc.data()
                             
        });
      })
    });

       
    //obtengo los datos del service de departamentos y municipios
    this.municipiosService.getDatos().subscribe(datos =>{
      
      //almaceno todos los municipios
      this.datosMunicipios=datos.map(data=> 
        // del map retorna algo
        data.municipio);

        this.datosDepartamentos=datos.map(data=> 
          // del map retorna algo
          data.departamento);


        //elimino los departamentos repetidos
        let NuevosDatosDepartamentos:string[]=[];

        for (let posicion = 0; posicion < this.datosDepartamentos.length; posicion++) {
          const element = this.datosDepartamentos[posicion];
          
          if(posicion==0)
          {
            NuevosDatosDepartamentos.push(element.toString());
          }
          else{
            if(NuevosDatosDepartamentos.includes(element.toString())==false)
            {
              NuevosDatosDepartamentos.push(element.toString());
            }
          }
        }
           
        this.datosDepartamentos=NuevosDatosDepartamentos; 
    });

    // para el autocompletado  y es empleado el html
    this.filteredMunicipios = this.registerForm.controls.ciudad.valueChanges
      .pipe(
        startWith(''),
        map(value => this._filterMunicipios(value))
      );


    this.filteredDepartamentos = this.registerForm.controls.departamento.valueChanges
    .pipe(
      startWith(''),
      map(value => this._filterDepartamentos(value))
    );
    
  }


  //para requerimientos personalizados
  //para el correo

  private validarEmail: ValidatorFn =(control: AbstractControl): ValidationErrors | null => {
    const email = control.value;
    let respuesta= null;
    if (this.ValidarExistenciaCorreo(email)==true) {
      return{ ms: 'para otro forma de error' };
    }
    
    return null;
  }
    

  //metodo para informar errores en el campo de email
  errorEmail() {
    if (this.registerForm.controls.email.hasError('required')) {
      return 'Debe ingresar un email';
    }
    if (this.registerForm.controls.email.hasError('ms')) {
      return 'El email ya ha sido registrado';
    }
    return this.registerForm.controls.email.hasError('email') ? 'Email no válido' : '';
  }

  
  //para la cedula

  private validarCedula: ValidatorFn =(control: AbstractControl): ValidationErrors | null => {
    const cedula = control.value;
    let respuesta= null;
    if (this.ValidarExistenciaCedula(cedula)==true) {
      return{ ms: 'para otro forma de error' };
    }
    
    return null;
  }
      
  
  //metodo para informar errores en el campo de cedula
  errorCedula() {
    if (this.registerForm.controls.cedula.hasError('required')) {
      return 'Ingrese un número de cédula';
    }
    if (this.registerForm.controls.cedula.hasError('ms')) {
      return 'El número de cedula ya ha sido registrado';
    }
    
  }         

//retorna un array con la filtracion para los autocompletados
  _filterMunicipios(value: string): string[] {
    const filterValue = value.toLowerCase();

    return this.datosMunicipios.filter(option => option.toLowerCase().includes(filterValue));
  }

  
  _filterDepartamentos(value: string): string[] {
    const filterValue = value.toLowerCase();

    return this.datosDepartamentos.filter(option => option.toLowerCase().includes(filterValue));
  }


 //Valida la existencia del correoen la bd, retorna un boolean 
  ValidarExistenciaCorreo(correo: string): boolean {
    let existeCorreo: boolean = false;
    let respuesta: boolean = true;


    //Obtengo los correos en un array
    for (let i = 0; i < this.listaRegistros.length; i++) {
      const element = this.listaRegistros[i];
      //obtengo el email
      const { email } = element.data;
      //comparo que si existe
      if (correo == email) {
        existeCorreo = true;
      }
      
    }
    
    if (existeCorreo == true) {

      //otra forma de informar empleando el modal
      //const data={ titulo:'Advertencia', mensaje:'El correo ingresado ya está registrado'};
      //this.openDialog(data);
      respuesta = true;

    }
    else {
      respuesta = false;
    }

    return respuesta;

  }

 //Valida la existencia del la C.C en la bd, retorna un boolean
  ValidarExistenciaCedula(cedulaIn: string): boolean {

    let existeCedula: boolean = false;
    let respuesta: boolean = true;


    //Obtengo los correos en un array
    for (let i = 0; i < this.listaRegistros.length; i++) {
      const element = this.listaRegistros[i];
    //obtengo la cedula
      const { cedula } = element.data;
      //comparo que si existe
      if (cedulaIn == cedula) {
        existeCedula = true;
      }

      
    }
    if (existeCedula == true) {
      //otra forma de informar empleando el modal
      //const data={ titulo:'Advertencia', mensaje:'La cedula ingresada ya está registrada'};
      //this.openDialog(data);
      respuesta = true;
    }   
    else {
      respuesta = false;
    }

    return respuesta;

  }
  
  // para el checkbox empleando *ngFor en el html
  listaHabi: any = [
    { id: 1, name: 'Autoconocimiento' },
    { id: 2, name: 'Empatía' },
    { id: 3, name: 'Comunicación asertiva' },
    { id: 4, name: 'Pensamiento crítico' },
    { id: 5, name: 'Toma de decisiones' },
    { id: 6, name: 'Adaptación' },
    { id: 7, name: 'Comunicación' },
    { id: 8, name: 'Trabajo en equipo' },
    { id: 9, name: 'Capacidad de asociación' },
    { id: 10, name: 'Razonamiento' },
  ];
  //Conteno de habilidades
  nHabilidaddes:number=0;

  //crea un formgoroup para los checkBox
  checkboxForm = new FormGroup({
      //un array de Form
    habiForm: new FormArray([], Validators.required)

  });


  onCheckboxChange(e) {
    const habiForm: FormArray = this.checkboxForm.get('habiForm') as FormArray;
        
    //si lo chuliaron agrega al array siempre que numero de habilidades sea menor 3
    if (e.checked && this.nHabilidaddes<=2) {
      
      habiForm.push(new FormControl(e.source.value));        
      this.nHabilidaddes++;
      //metodo que actuliza las habilidades con el formControl principal
      this.validarHabilidades();
    } else {

      //si numero la habilidad se desmarca elimina habilidad 
      if(e.checked==false)
      {
        const index = habiForm.controls.findIndex(x => x.value === e.source.value);
        habiForm.removeAt(index);
        //metodo que actuliza las habilidades con el formControl principal
        this.validarHabilidades();
        this.nHabilidaddes--;
      }
      //si numero de habilidades esta al limite (3) no agrega nada y no permite chulear
      else{
        e.source._checked=false;
      }
    }

    //si no hay habilidades selecionadas no habilita el boton de registrarse
    if(this.nHabilidaddes==0)
    {
      //restauro el formControl principal, limpiandolo, agregando el validador y actualizandolo
      this.registerForm.controls.habilidades.setValue([]);
      this.registerForm.controls.habilidades.setValidators([Validators.required]);
      this.registerForm.controls.habilidades.updateValueAndValidity();
   }
    
   //console.log(this.registerForm.controls.habilidades.value);

  }

  // para asignar las habilidades al Formgroup principal
  validarHabilidades(){
    //obtengo los valores de FormGroup
    const ob = this.checkboxForm.value;

    //obtengo el array habForm de ob ostenido de FormGroup
    const { habiForm } = ob;

    //para almacenar las habilidades
    let habilidadesIn: string = '';    

    for (let i = 0; i < habiForm.length; i++) {

      habilidadesIn += habiForm[i] + " / "; // \n 

    }         
    //asigno las habilidaesIn al valor del formcontrol habilidaes para que lo registre en la bd  
    this.registerForm.controls.habilidades.setValue([habilidadesIn]);

  }


   //para hacer un nuevo registro en la bd
   public onRegister(form, documentId = this.documentId) {
            
    //verifica el resultado del metodo verificar existencia de correo
    if (this.ValidarExistenciaCorreo(this.registerForm.get('email').value)  == false && 
        this.ValidarExistenciaCedula(this.registerForm.get('cedula').value) == false ) 
    {

      //console.log(`Status: ${this.currentStatus}`);
      //si es 1 es para la creacion de un nuevo registro
      if (this.currentStatus == 1) {

        
        // se le asigan una foto por defecto
        this.registerForm.controls.photoUrl.setValue("https://firebasestorage.googleapis.com/v0/b/teamdc-c1083.appspot.com/o/uploads%2FLogo.png?alt=media&token=4<KEY>");

        this.registrosServiceF.crearRegistro(this.registerForm.value).then(() => {

          //si se aprobo el registro
          const data={mensaje:'Gracias por registrarte'};
          this.openDialog(data);    
          //console.log("respuesta del dialogo:"+this.resDialog);
          this.router.navigate(['/home']);
          

          //limpia el formulario
          this.registerForm.setValue({
            photoUrl:'',
            nombres: '',
            apellidos: '',
            cedula: null,
            email: '',
            fechaNacimiento: '',
            direccion: '',
            ciudad: '',
            departamento: '',
            pais: '',
            codigoPostal: '',
            profesion: '',
            habilidades: '',
            descripcion: '',

          });
        }, (error) => {
          console.error(error);
        });        
      }

    } else {
      
    }

    // se crea un usuario en la autentificacion de firebase con su email
    const result= this.afAuth.auth.createUserWithEmailAndPassword(
      this.registerForm.controls.email.value, 
      this.registerForm.controls.email.value);


    //parte de codigo no aplicado, es para editar basado en una ejemplo seguido para el crud
    if (this.currentStatus != 1) {

      //modo edicion => no funciona porque falta formulario de edicion
      this.registrosServiceF.updateRegistro(documentId, this.registerForm.value).then(() => {
        this.currentStatus = 1;
        //limpia el formulario
        this.registerForm.setValue({

          nombres: '',
          apellidos: '',
          cedula: null,
          email: '',
          fechaNacimiento: '',
          direccion: '',
          ciudad: '',
          departamento: '',
          pais: '',
          codigoPostal: '',
          profesion: '',
          habilidades: '',
          descripcion: '',

        });
        console.log('Documento editado exitósamente');
      },
        (error) => {
          console.log(error);
        });
    }


  }

  
    //abrir dialogo
    openDialog(data:any) {
      const dialogConfig = new MatDialogConfig();
      dialogConfig.data = data;
      //dialogConfig.data = { titulo:'Estado de registro', mensaje:'Exitoso'};
      let dialogRef = this.matDialog.open(DialogComponent, dialogConfig)
      dialogRef.afterClosed().subscribe(value => {
       
        this.resDialog=value;
        //console.log(`Dialog sent: ${value}`); 
      });;
    }
    








  //funciones del crud que no se aplican pero que se adaptaron de un ejemplo seguido para el crud

  //para la edicion de un registro aun no funciona porque no hay fromulario de edicion
  public editarRegistro(documentId) {
    let editSubscribe = this.registrosServiceF.getRegistro(documentId).subscribe((registro) => {
      this.currentStatus = 2;
      this.documentId = documentId;
      this.registerForm.setValue({

        id: documentId,
        nombre: registro.payload.data()['nombre'],
        apellidos: registro.payload.data()['apellidos'],
        cedula: registro.payload.data()['cedula'],
        email: registro.payload.data()['email'],
        fechaNacimiento: registro.payload.data()['fechaNacimiento'],
        direccion: registro.payload.data()['direccion'],
        ciudad: registro.payload.data()['ciudad'],
        departamento: registro.payload.data()['departamento'],
        pais: registro.payload.data()['pais'],
        codigoPostal: registro.payload.data()['codigoPostal'],
        profesion: registro.payload.data()['profesion'],
        habilidades: registro.payload.data()['habilidades'],
        descripcion: registro.payload.data()['descripcion'],
      });
      editSubscribe.unsubscribe();
    });
  }

  
  //elimina el registro de la bd
  public eliminarRegistro(documentId) {
    this.registrosServiceF.eliminarRegistro(documentId).then(() => {
      console.log('Documento eliminado!');
    }, (error) => {
      console.error(error);
    });
  }


}


<file_sep>import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

//Inportacion del modulo encargado de Material
import{MaterialModule} from './material/material.module';

import { SidenavComponent } from './components/sidenav/sidenav.component';

//Importacion para trabajar con el formulario
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

//Inportacion del servicio encargado de los registros
import {RegistrosService} from './services/registros.service';

//importaciones para el manejo de FireBase
import { environment } from '../environments/environment';
import{ AngularFireModule } from '@angular/fire';
import{ AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireAuthModule} from '@angular/fire/auth';


//Con este modulo no necesitas usar fetch ni ajax ni nada para llamadas a apis
import { HttpClientModule } from "@angular/common/http";

//importacion para el manejo de responsive
import { FlexLayoutModule} from '@angular/flex-layout';
import { LayoutModule } from '@angular/cdk/layout';

import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { DialogComponent } from './components/dialog/dialog.component';
import { YouTubePlayerModule }  from  '@angular/youtube-player' ;
import { AngularFireStorageModule } from '@angular/fire/storage';
import { FooterComponent } from './components/footer/footer.component';
import { AuthService } from './services/auth.service';
import { ContactComponent } from './components/contact/contact.component';



   


@NgModule({
  declarations: [
    AppComponent,
    SidenavComponent,
    DialogComponent,
    FooterComponent,
    ContactComponent,
  
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,

    //Importaciones de FireBase
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFirestoreModule,
    AngularFireAuthModule, 

    //para llamadas http
    HttpClientModule,

    //Modulo encargado de Material
    MaterialModule,

    //Encargado del formulario
    FormsModule,
    ReactiveFormsModule,

    //encargado del modo responsive
    FlexLayoutModule,

    LayoutModule,

    MatToolbarModule,

    MatButtonModule,

    MatSidenavModule,

    MatIconModule,

    MatListModule,

    //para embeber videos de youtube
    YouTubePlayerModule,

    //para subir la imagen al storage
    AngularFireStorageModule,

   

  ],
  providers: [
    //Servicio de firabese para BD
    RegistrosService,
    // servicio para la autentificacion
    AuthService,
    //servicio de api
    //MunicipiosColombiaService

  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
<file_sep>import { Injectable } from '@angular/core';

//importaciones para gestionar el servicio a la BD
import{AngularFirestore, AngularFirestoreCollection} from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';



//importamos la interface
import {RegistrosI} from 'src/app/models/registros.interface';

//ID para la interface
export interface RegistrosID extends RegistrosI{id:string;}


@Injectable({
  providedIn: 'root'
})
export class RegistrosService {


  //Crear propiedad
  private registrosCollection: AngularFirestoreCollection<RegistrosI>;

  //propiedad para guardar todos los usuarios
  registros: Observable<RegistrosID[]>;

  //inyectamos el servicio a la bd
  constructor(private readonly firestore:AngularFirestore) { 

    
    //nombre de la coleccion va en parentesis
    this.registrosCollection = firestore.collection<RegistrosI>('registros');
    this.registros = this.registrosCollection.snapshotChanges().pipe(
      map(actions => actions.map(a =>{
        const data = a.payload.doc.data() as RegistrosI;
        const id = a.payload.doc.id;
        return{ id, ...data};
      }))
    );
  }

 

  getAllUser(){
    //return todos los usuarios
    return this.registros;
  }

  //--------------------------------------------
  // conecion y manejo de base de datos tomado de:
  // https://medium.com/angular-chile/angular-6-y-firestore-b7f270adcc96
  //--------------------------------------------
 //metodos

 //Recibe un objeto de la interface
  crearRegistro(registro: RegistrosI)
  {
    return this.firestore.collection('registros').add(registro);
    
  }


  
   //Recibe el id para un obtener un unico registro
  getRegistro(registroId: string) {
    return this.firestore.collection('registros').doc(registroId).snapshotChanges();
  }

  //obtiene todos los registros
  getRegistros()
  {
    return this.firestore.collection('registros').snapshotChanges();
  }

  //actualiza un registro
  public updateRegistro(documentId: string, registro: RegistrosI) {
    return this.firestore.collection('registros').doc(documentId).set(registro);
  }
  //elimina un registro
  eliminarRegistro(registroID: string){

    return this.firestore.doc('registros/' + registroID).delete();;
  }

}






<file_sep>import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactComponent } from './components/contact/contact.component';

//redireccion a la pagina de register
const routes: Routes = [
  { path: '', 
    redirectTo: '/home',
    pathMatch: 'full' },
 
  { path: 'home', loadChildren: () => import('./components/home/home.module').then(m => m.HomeModule) },
  { path: 'register', loadChildren: () => import('./components/register/register.module').then(m => m.RegisterModule) },
  { path: 'records', loadChildren: () => import('./components/records/records.module').then(m => m.RecordsModule) },
  { path: 'video', loadChildren: () => import('./components/video/video.module').then(m => m.VideoModule) },
  { path: 'login', loadChildren: () => import('./components/login/login.module').then(m => m.LoginModule) },
  { path: 'profile', loadChildren: () => import('./components/profile/profile.module').then(m => m.ProfileModule) },
  {path:'contact', component:ContactComponent},
  { path: 'team', loadChildren: () => import('./components/team/team.module').then(m => m.TeamModule) },  
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
<file_sep>Descripción
***************************
Este es la documentación de un proyecto realizado en angular 10.

El proyecto es desarrollado con el fin de poner en práctica los conocimientos
adquiridos en el transcurso de la carrera de Ingeniería de Sistemas en la UNAB.
Además de aprender nuevos lenguajes y herramientas que son útiles en el desarrollo
de una plataforma web. Esta página web es un dashboard que permite realizar un registro 
y guardar los datos del usuario, para luego ingresar al sistema y poder acceder a todos 
los módulos de la plataforma.La asignación de actividades para el desarrollo de la 
página web se definió mediante desafíos asignados por el supervisor.

Contenido
===========================


Desafío 1
----------------------------
Tiene un formulario responsivo con los campos definidos por el supervisor, en donde se usan 
campos de tipo texto, númerico, de fecha, de selección y de chequeo, alojando la información recolectada
en una base de datos.

Desafío 2
------------------------------
Sobre el formulario desarrollado, se incluyen validaciones y alertas de campos erróneos e inválidos.
Se traen datos de municipios y departamentos de Colombia de un WebService.
Tiene un estilo estipulado por el supervisor, incluyendo aviso de un registro exitoso.
Se tienen las secciones de Home, Registro, Inicio de sesión, Registros y video.
En el Home se agregan fotos del equipo desarrollador.
Registros permite visualizar los datos de los usuarios registrados.
La sección de video muestra un video del equipo DC.

Desafío 3
-----------------------------------
Se restringue el acceso.
Los usuarios que no estan logueados tienen accesso a la página principal, al registro, a inicio de sesión y al video.
Los usuarios que han iniciado sesión estan habilidatos para navegar por todas las secciones.

Desafío 4
--------------------------------
Se acomoda un logo correspondiente al equipo DC en la esquina superior izquierda, y en modo responsivo en el centro del dispositivo.
Tiene una sección de mi perfil en donde se muestra toda la información del usuario logueado y se permite la edición 
de los datos y añadir una foto de usuario tipo avatar.



<file_sep>*********
Registro
*********

1. Descripción
    En este componente se debe mostrar todos los registros almacenados en la base de datos. 

2. Importaciones
    Para este componente de debe importar el servicio a la base de datos, el servicio para la 
    autentificación de usuarios y el servicio a una api para obtener los municipios y departamentos 
    de Colombia.
    ::

        import { RegistrosService } from 'src/app/services/registros.service';
        import { AngularFireAuth } from '@angular/fire/auth';
        import {MunicipiosColombiaService} from 'src/app/services/municipios-colombia.service'.

    También se importan componentes de Angular Material para la implementación de ventana
    emergente o modal y de se importa el componente Router para un redireccionamiento al 
    concluir el registro.
    ::

        import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
        import { DialogComponent } from '../dialog/dialog.component';
        import { Router } from '@angular/router';.

    
3. Desarrollo
    El constructor se inyectan todos los objetos a necesarios para el desarrollo del componente.
    ::
        private afAuth: AngularFireAuth,
        private registrosServiceF : RegistrosService,
        private municipiosService:MunicipiosColombiaService,
        private matDialog: MatDialog,
        private router:Router,   
        private fb:FormBuilder.

    Para la captura de datos se emplea los formularios reactivos, para ello se declara un
    objeto de la clase FormGroup y se inicializa creando un FormControl para cada campo requerido, 
    declarando su estado y sus respectivo valida daciones.  
    ::
        this.registerForm= this.fb.group({
            photoUrl: new FormControl(''),      
            cedula: new FormControl('', [Validators.required, this.validarCedula]),
            .
            .
            .
            descripcion: new FormControl('', [Validators.required]),
            }).

    En el método ngOnInit se realiza la obtención de todos los registros almacenados en la 
    base de datos con el fin de realizar validaciones con dichos datos, estos datos son 
    almacenados en un array.
    ::

        this.registrosServiceF.getRegistros().subscribe((rgSnapshot) => {
        this.listaRegistros = [];
        rgSnapshot.forEach((rgData: any) => {
            this.listaRegistros.push({
            id: rgData.payload.doc.id,
            data: rgData.payload.doc.data()
                                
            });
        })
        });
    sacaaaaaaaaaaaaaaaaaaaa




<file_sep>import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators, EmailValidator } from '@angular/forms';
import { Router } from '@angular/router';
import { auth } from 'firebase';
import { AuthService } from '../../services/auth.service';
import { RegistrosService } from 'src/app/services/registros.service';
import { MatDialogConfig, MatDialog } from '@angular/material/dialog';
import { DialogComponent } from '../dialog/dialog.component';
import { AngularFireAuth } from '@angular/fire/auth';



@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
 
  loginForm= new FormGroup({
    
    email: new FormControl('',[Validators.required, Validators.email]),

  })
  resDialog: any;

  
  constructor(private authSvc:AuthService, private router: Router, 
    private registroService:RegistrosService,
    private matDialog: MatDialog,
    public afAuth: AngularFireAuth) { }

  
  listaRegistros;

  
     
  ngOnInit(): void {
    this.registroService.getRegistros().subscribe((rgSnapshot) => {
      this.listaRegistros = [];
      rgSnapshot.forEach((rgData: any) => {
        this.listaRegistros.push({
          id: rgData.payload.doc.id,
          data: rgData.payload.doc.data()

        });
      })
    });
  }
  
    
   

 async onLogin(){
    console.log("funciona"+ this.loginForm.controls.email.value);
    
    if(this.ValidarExistenciaCorreo(this.loginForm.controls.email.value)==true)
    {
      const user= await this.authSvc.login(this.loginForm.controls.email.value,
        this.loginForm.controls.email.value);
     if (user) {
       //redirect to homePage
       this.router.navigate(['/home'])
     }
     
       
       }
    else{
      //window.alert('correo no registrado');
      const data={ titulo:'Advertencia', mensaje:'El correo no esta registrado en la BD'};
      this.openDialog(data);
      
    }


  }
  get email(){ return this.loginForm.get('email');}
  get password(){ return this.loginForm.get('password');}


 //Valida la existencia del correo en la bd, retorna un boolean

 ValidarExistenciaCorreo(correo: string): boolean {
   console.log("validacion"+correo)
  let existeCorreo: boolean = false;
  let respuesta: boolean = true;


  //Obtengo los correos en un array
  for (let i = 0; i < this.listaRegistros.length; i++) {
    const element = this.listaRegistros[i];

    const { email } = element.data;
    if (correo == email) {
      existeCorreo = true;
    }

    
  }


  if (existeCorreo == true) {

 
    respuesta = true;

  }
  else {
    respuesta = false;
  }

  return respuesta;

}

openDialog(data:any) {
  const dialogConfig = new MatDialogConfig();
  dialogConfig.data = data;
  //dialogConfig.data = { titulo:'Estado de registro', mensaje:'Exitoso'};
  let dialogRef = this.matDialog.open(DialogComponent, dialogConfig)
  dialogRef.afterClosed().subscribe(value => {
    this.resDialog=value;
    console.log(`Dialog sent: ${value}`); 
  });;
}
}
<file_sep>import { Component, OnInit } from '@angular/core';

//para el loguin
import { AngularFireAuth } from '@angular/fire/auth';
import { RegistrosService } from 'src/app/services/registros.service';


import { FormBuilder, FormGroup, Validators, FormControl, FormArray, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { DatePipe } from '@angular/common';
import { Observable } from 'rxjs';
import { MunicipiosColombiaService } from 'src/app/services/municipios-colombia.service';
import { startWith, map } from 'rxjs/operators';


//para recuperar la imagen que se subio
import { AngularFireStorage } from '@angular/fire/storage';
import { finalize } from 'rxjs/operators';




@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.scss']
})
export class ProfileComponent implements OnInit {

  //almacena el usuario logueado
  private UserEmail:string;
  // lista de registros
  public listaRegistros=[];
  //registro a modificar
  registroUsuario
  //para el formGroup
  editForm: FormGroup;
  checkboxForm :FormGroup;
  //cambiar estados de los botones
  btnEditarDisabled=false;
  btnGuardarDisabled=true;

  
  //donde se va almacenar el id de un registro
  public documentId = null;

  datosMunicipios:string[]=[];
  datosDepartamentos:string[]=[];
  
  filteredMunicipios: Observable<string[]>;
  filteredDepartamentos: Observable<string[]>;

  //para la fecha de nacimiento
  datePipe = new DatePipe('en-US');

  breakpoint: number;
  
  constructor(
    // para manejar el estado de la sesion
    private afAuth: AngularFireAuth,
    // para la conexion con la BD
    private registroService:RegistrosService,
    //para el formulario
    private fb:FormBuilder,
    /// servicio encargado de municipios de colombia
    private municipiosService:MunicipiosColombiaService,
    //para subir la imagen al storage de firebase
    private storage:AngularFireStorage
    
  ) {

     //crea un formgoroup para los checkBox
    this.checkboxForm= this.fb.group({
      //un array de Form
      habiForm: new FormArray([], Validators.required)});
  
    this.editForm= this.fb.group({

      photoUrl: new FormControl(''),
      nombres: new FormControl('', [Validators.required]),
      apellidos: new FormControl('', [Validators.required]),
      cedula: new FormControl('', [Validators.required, this.validarCedula]),
      email: new FormControl('', [Validators.required, Validators.email, /*this.validarEmail*/]),
      fechaNacimiento: new FormControl('', [Validators.required]),
      direccion: new FormControl('', [Validators.required]),
      ciudad: new FormControl('', [Validators.required]),
      departamento: new FormControl('', [Validators.required]),
      pais: new FormControl('', [Validators.required]),
      codigoPostal: new FormControl('', [Validators.required]),
      profesion: new FormControl('', [Validators.required]),
      habilidades: new FormControl('', [Validators.required]),
      descripcion: new FormControl('', [Validators.required]),

      })
      

  }


  //para la imagen basado en https://dev.to/fayvik/uploading-an-image-to-firebase-cloud-storage-with-angular-2aeh
  uploadPercent: Observable<number>;
  downloadURL: Observable<string>;
  urlImage;
  

  ngOnInit(): void {
    this.breakpoint = (window.innerWidth <= 400) ? 1 : 6;




    this.getDatosMuniYDeap();
         

    this.UserEmail=this.afAuth.auth.currentUser.email;


    this.editForm.setValue({
      photoUrl:'',
      nombres: '',
      apellidos: '',
      cedula: null,
      email: '',
      fechaNacimiento: '',
      direccion: '',
      ciudad: '',
      departamento: '',
      pais: '',
      codigoPostal: '',
      profesion: '',
      habilidades: '',
      descripcion: '',

    });
    //desactivo el formulario
    this.editForm.disable();
    this.getRegistros(); 

  }
  onResize(event) {
    this.breakpoint = (event.target.innerWidth <= 400) ? 1 : 6;
  }


  //metodo para subir la imagen al storage
  onUpload(e){
    //console.log('subir', e);
    //generar un id unico para la imagen   
    const id = Math.random().toString(36).substring(2);
    //aqui se tiene el archivo
    const file = e.target.files[0];
    
    const filePath = `uploads/profile_${id}`;
    //ruta del fichero
    const ref = this.storage.ref(filePath);
    //se realiza la subida del fichero
    const task = this.storage.upload(filePath,file);

    this.uploadPercent = task.percentageChanges();

     //obtiene el tamaño de la imagen y lo pasa a MB
     const Filesize = e.target.files[0].size/1024/1024;
     if (Filesize>1) {
       alert('El archivo excede el tamaño permitido de 1MB')
     }else{
       //para recuperar la url
    task.snapshotChanges()
    .pipe(
      finalize(() => {
        this.downloadURL = ref.getDownloadURL();
        this.downloadURL.subscribe(url => {
          if (url) {
            this.urlImage = url;
            this.editForm.enable();
            this.editForm.controls.photoUrl.setValue([this.urlImage]);            
            this.editForm.controls.fechaNacimiento.setValue(this.registroUsuario.data.fechaNacimiento);
            this.onUpdate(this.editForm.value);

          }
          console.log(this.urlImage);
        });
      })
    ).subscribe();

     }
    

   
}



    

    
 
  
  //metodo para informar errores en el campo de cedula
  errorCedula() {
    if (this.editForm.controls.cedula.hasError('required')) {
      return 'Ingrese un número de cédula';
    }
    if (this.editForm.controls.cedula.hasError('ms')) {
      return 'El número de cedula ya ha sido registrado';
    }
    
  }

  
  //para la cedula
  private validarCedula: ValidatorFn =(control: AbstractControl): ValidationErrors | null => {
    const cedula = control.value;
    let respuesta= null;
    if (this.ValidarExistenciaCedula(cedula)==true) {
      return{ ms: 'para otro forma de error' };
    }
    
    return null;
  }



  getRegistros(){

    this.registroService.getRegistros().subscribe((rgSnapshot) => {
      this.listaRegistros = [];
      rgSnapshot.forEach((rgData: any) => {
        this.listaRegistros.push({
          id: rgData.payload.doc.id,
          data: rgData.payload.doc.data()

        });
      })

      //console.log(this.listaRegistros);
      this.setDatosFormulario();

    });

    
  }

  setDatosFormulario(){
    
    for (let index = 0; index < this.listaRegistros.length; index++) {
      const element = this.listaRegistros[index];    
    
      const { email } = element.data;

      if(email==this.UserEmail)
      {        
        this.registroUsuario=element
               
        break;
      }      
      
    };

    //console.log( this.registroUsuario.id );
    //console.log('sdsadadas');
    //console.log( this.documentId );
      //para el id del documento a actualizar
      this.documentId = this.registroUsuario.id;
      this.urlImage=this.registroUsuario.data.photoUrl;
      let editSubscribe = this.registroService.getRegistro(this.documentId).subscribe((registro) => {
       
      
     
        let myFormattedDate = this.datePipe.transform(registro.payload.data()['fechaNacimiento'].seconds*1000, 'dd/MM/yyyy');
       
       
       
        
        

        this.editForm.setValue({  
          
          photoUrl:registro.payload.data()['photoUrl'],
          nombres: registro.payload.data()['nombres'],
          apellidos: registro.payload.data()['apellidos'],
          cedula: registro.payload.data()['cedula'],
          email: registro.payload.data()['email'],
          fechaNacimiento: /*registro.payload.data()['fechaNacimiento'],*/myFormattedDate,
          direccion: registro.payload.data()['direccion'],
          ciudad: registro.payload.data()['ciudad'],
          departamento: registro.payload.data()['departamento'],
          pais: registro.payload.data()['pais'],
          codigoPostal: registro.payload.data()['codigoPostal'],
          profesion: registro.payload.data()['profesion'],
          habilidades: registro.payload.data()['habilidades'],
          descripcion: registro.payload.data()['descripcion'],
        });
        editSubscribe.unsubscribe();
      });
       


  }

  //boton de editar
  onEditar(){
    this.editForm.enable();
    //para no cambiar el correo
    this.editForm.get('email').disable();
    this.btnEditarDisabled=true;
    this.editForm.controls.habilidades.setValue([]);
    this.editForm.controls.habilidades.setValidators([Validators.required]);
    this.editForm.controls.habilidades.updateValueAndValidity();
    this.editForm.controls.fechaNacimiento.setValue([]);
    this.editForm.controls.fechaNacimiento.setValidators([Validators.required]);
    this.editForm.controls.fechaNacimiento.updateValueAndValidity();
    
    this.nHabilidaddes=0;
      const habiForm: FormArray = this.checkboxForm.get('habiForm') as FormArray;
        let i=2;
        while (habiForm.controls.length!=0) {
          habiForm.removeAt(i);
          i--;
        }

    //this.checkboxForm.controls.habiForm
    console.log(this.checkboxForm.controls.habiForm);
  }

  onUpdate(form){

    this.editForm.get('email').enable();
    this.editForm.controls.email.setValue(this.UserEmail);
    //verifica el resultado del metodo verificar existencia de correo 
    if (this.ValidarExistenciaCorreo(this.editForm.get('email').value)  == false && 
        this.ValidarExistenciaCedula(this.editForm.get('cedula').value) == false ) 
    {
      
   
      

      this.registroService.updateRegistro(this.documentId, this.editForm.value).then(() => {
      
        this.editForm.disable();
        this.btnEditarDisabled=false;
        this.btnGuardarDisabled=true;

        //para actualizar el correo y contraseña pero esta desabilitado
        //this.afAuth.auth.currentUser.updateEmail(this.editForm.controls.email.value);
        //this.afAuth.auth.currentUser.updatePassword(this.editForm.controls.email.value);

      console.log('Documento editado exitósamente');
      }, (error) => {
        console.log(error);
      });

    }else{

      console.log(" no es valido para el registro");
      

    }


    
  }
        
  

  //Valida la existencia del correoen la bd, retorna un boolean
  
  ValidarExistenciaCorreo(correo: string): boolean {
    let existeCorreo: boolean = false;
    let respuesta: boolean = true;


    //Obtengo los correos en un array
    for (let i = 0; i < this.listaRegistros.length; i++) {
      const element = this.listaRegistros[i];

      const { email } = element.data;

      //excluyo el documento que se va a editar
      if(element.id!= this.registroUsuario.id)
      {
        if (correo == email) {
          existeCorreo = true;
        }
      }

      
      
    }


    if (existeCorreo == true) {

      console.log('El correo ingresado ya está registrado');
      //const data={ titulo:'Advertencia', mensaje:'El correo ingresado ya está registrado'};
      //this.openDialog(data);
      respuesta = true;

    }
    else {
      respuesta = false;
    }

    return respuesta;

  }

  //Valida la existencia del la C.C en la bd, retorna un boolean
  ValidarExistenciaCedula(cedulaIn: string): boolean {

    let existeCedula: boolean = false;
    let respuesta: boolean = true;

    
    //Obtengo los correos en un array
    for (let i = 0; i < this.listaRegistros.length; i++) {
      const element = this.listaRegistros[i];

      const { cedula } = element.data;

      //excluyo el documento que se va a editar
      if(element.id!= this.registroUsuario.id)
      {
        if (cedulaIn == cedula) {
          existeCedula = true;
        }
      }
    

      
    }
    if (existeCedula == true) {

      console.log('La cedula ingresada ya está registrado');
      //const data={ titulo:'Advertencia', mensaje:'La cedula ingresada ya está registrada'};
      //this.openDialog(data);
      respuesta = true;
    }   
    else {
      respuesta = false;
    }

    return respuesta;

  }



  // para el checkbox empleando *ngFor en el html

  listaHabi: any = [
    { id: 1, name: 'Autoconocimiento' },
    { id: 2, name: 'Empatía' },
    { id: 3, name: 'Comunicación asertiva' },
    { id: 4, name: 'Pensamiento crítico' },
    { id: 5, name: 'Toma de decisiones' },
    { id: 6, name: 'Adaptación' },
    { id: 7, name: 'Comunicación' },
    { id: 8, name: 'Trabajo en equipo' },
    { id: 9, name: 'Capacidad de asociación' },
    { id: 10, name: 'Razonamiento' },
  ];
  //Conteno de habilidades
  nHabilidaddes:number=0;

 

  onCheckboxChange(e) {
    const habiForm: FormArray = this.checkboxForm.get('habiForm') as FormArray;
        
    //si lo chuliaron hagrega al array siempre que numero de habilidades menor 4
    if (e.checked && this.nHabilidaddes<=2) {
      
      habiForm.push(new FormControl(e.source.value));        
      this.nHabilidaddes++;
      this.validarHabilidades();
    } else {

      //si numero la habilidad se desmarca elimina habilidad 
      if(e.checked==false)
      {
        const index = habiForm.controls.findIndex(x => x.value === e.source.value);
        habiForm.removeAt(index);
        this.validarHabilidades();
        this.nHabilidaddes--;
      }
      //si numero de habilidades esta al limite (3) no agrega nada y no permite chulear
      else{
        e.source._checked=false;
      }
    }

    //si no hay habilidades selecionadas no habilita el boton
    if(this.nHabilidaddes==0)
    {
      this.editForm.controls.habilidades.setValue([]);
      this.editForm.controls.habilidades.setValidators([Validators.required]);
      this.editForm.controls.habilidades.updateValueAndValidity();
  }
    
  //console.log(this.editForm.controls.habilidades.value);

  }

  // para asignar las habilidades al Formgroup principal
  validarHabilidades(){
    //obtengo los valores de FormGroup
    const ob = this.checkboxForm.value;

    //obtengo el array habForm de ob ostenido de FormGroup
    const { habiForm } = ob;

    //para almacenar las habilidades
    let habilidadesIn: string = '';    

    for (let i = 0; i < habiForm.length; i++) {

      habilidadesIn += habiForm[i] + " / "; // \n 

    }         
    //asigno las habilidaesIn al valor del formcontrol habilidaes para que lo registre en la bd  
    this.editForm.controls.habilidades.setValue([habilidadesIn]);

  }



  
  _filterMunicipios(value: string): string[] {
    const filterValue = value.toLowerCase();

    return this.datosMunicipios.filter(option => option.toLowerCase().includes(filterValue));
  }

  
  _filterDepartamentos(value: string): string[] {
    const filterValue = value.toLowerCase();

    return this.datosDepartamentos.filter(option => option.toLowerCase().includes(filterValue));
  }

  getDatosMuniYDeap(){

    //obtengo los datos del service de departamentos y municipios
    this.municipiosService.getDatos().subscribe(datos =>{
      
      //almaceno todos los municipios
      this.datosMunicipios=datos.map(data=> 
        // del map retorna algo
        data.municipio);

        this.datosDepartamentos=datos.map(data=> 
          // del map retorna algo
          data.departamento);


        //elimino los departamentos repetidos
        let NuevosDatosDepartamentos:string[]=[];

        for (let posicion = 0; posicion < this.datosDepartamentos.length; posicion++) {
          const element = this.datosDepartamentos[posicion];
          
          if(posicion==0)
          {
            NuevosDatosDepartamentos.push(element.toString());
          }
          else{
            if(NuevosDatosDepartamentos.includes(element.toString())==false)
            {
              NuevosDatosDepartamentos.push(element.toString());
            }
          }
        }
           
        this.datosDepartamentos=NuevosDatosDepartamentos; 
    });

    

  

  
    this.filteredMunicipios = this.editForm.controls.ciudad.valueChanges
      .pipe(
        startWith(''),
        map(value => this._filterMunicipios(value))
      );


    this.filteredDepartamentos = this.editForm.controls.departamento.valueChanges
    .pipe(
      startWith(''),
      map(value => this._filterDepartamentos(value))
    );


  }

}
<file_sep>import { Injectable } from '@angular/core';

//Para manejo de llamadas a apis con este modulo no necesitas usar fetch ni ajax
import { HttpClient } from "@angular/common/http";
import { Observable } from 'rxjs/internal/Observable';

@Injectable({
  providedIn: 'root'
})
export class MunicipiosColombiaService {

  //inyecto el httpClient
  constructor( private http: HttpClient) { 

  }

  //metodos
getDatos():Observable<any>{

  return this.http.get("https://www.datos.gov.co/resource/xdk5-pm3f.json");

}


}
<file_sep>import { Injectable } from '@angular/core';

//importaciones para gestionar el servicio a la BD
import{AngularFirestore, AngularFirestoreCollection} from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { map} from 'rxjs/operators';



//importamos la interface
import {ContactosI} from 'src/app/models/registros.interface';

//ID para la interface
export interface ContactosID extends ContactosI{id:string;}

@Injectable({
  providedIn: 'root'
})
export class ContactosService {

  //Crear propiedad
  private contactosCollection: AngularFirestoreCollection<ContactosI>;

  //propiedad para guardar todos los usuarios
  contactos: Observable<ContactosID[]>;

  //inyectamos el servicio a la bd
  constructor(private readonly firestore:AngularFirestore) { 

    
    //nombre de la coleccion va en parentesis
    this.contactosCollection = firestore.collection<ContactosI>('contactos');
    this.contactos = this.contactosCollection.snapshotChanges().pipe(
      map(actions => actions.map(a =>{
        const data = a.payload.doc.data() as ContactosI;
        const id = a.payload.doc.id;
        return{ id, ...data};
      }))
    );
  }

 

  getAllUser(){
    //return todos los contactos
    return this.contactos;
  }

  //--------------------------------------------
  // conecion y manejo de base de datos tomado de:
  // https://medium.com/angular-chile/angular-6-y-firestore-b7f270adcc96
  //--------------------------------------------
 //metodos

 //Recibe un objeto de la interface
  crearContacto(contacto: ContactosI)
  {
    return this.firestore.collection('contactos').add(contacto);
    
  }


  
   //Recibe el id para un obtener un unico contacto
  getContacto(contactoId: string) {
    return this.firestore.collection('contactos').doc(contactoId).snapshotChanges();
  }

  //obtiene todos los contactos
  getContactos()
  {
    return this.firestore.collection('contactos').snapshotChanges();
  }

  //actualiza un contactos
  public updateContacto(documentId: string, contacto: ContactosI) {
    return this.firestore.collection('contactos').doc(documentId).set(contacto);
  }
  //elimina un contacto
  eliminarContacto(contactoID: string){

    return this.firestore.doc('contactos/' + contactoID).delete();;
  }

}






