Usar un componente en varios módulos en Angular

Como ya debes haber averiguado si estas aquí, los componentes en tu aplicación de Angular sólo puede declararse dentro de un único módulo. Esto supone un problema debido a que en ocasiones queremos reutilizar un componente en diferentes partes de la aplicación (por ejemplo un datepicker o un Pipe).

Para resolver el problema lo más cómodo es declarar un módulo «shared» dentro del cual crearemos los componentes que queramos compartir en el resto de la aplicación.

Un ejemplo seria:

@NgModule({
  declarations: [ SharedComponent ],
  exports: [ SharedComponent ]
})
export class SharedModule {}

Después sólo necesitaremos importar el módulo allí donde queramos usarlo.

@NgModule({
   imports: [ SharedModule ]
})
export class ModuloQueUsaTuSharedComponent {}

Si tienes un grupo de Pipes que quieres usar en toda tu aplicación puede ser muy útil definir un módulo que los contenga a todos. Pero por supuesto, si piensas crear un componente para usarlo en diferentes aplicaciones debes definir un módulo que contenga tu componente de manera que puedas usarlo únicamente importándolo.

Ahora ya sabes cómo definir tus componentes dentro de un módulo como todo un profesional!

.woff2 Failed to load resource 404 NOT FOUND

Me he encontrado un error «común» en diferentes aplicaciones con Angular al colgarlas en servidores IIS. No es un fallo muy importante, pero puede sacar de quicio solucionarlo, ya que cuando debugas en local, no aparece este error.

Para solucionar este error, simplemente debes definir la extensión .woff2 con el mimeType «application/font-woff2» en el IIS.

Para hacer esto simplemente acudimos a la configuración mime del sitio web.

Alternativamente (y mi preferida) puedes establecer esta configuración en el webconfig, de esta manera no necesitarás tocar configuraciones del servidor (perfecto si no tienes acceso a la administración de IIS) y podrás mover la web de un lugar a otro con la seguridad de que funciona.

<system.webServer>
  <staticContent>
    <remove fileExtension=".woff2" />
    <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
  </staticContent>
</system.webServer>

Espero que te sea útil !

Lazy Loading en Angular

¿Para qué necesito Lazy Loading?

La mayoría de veces que creamos un proyecto en Angular (o al menos en mi caso) nos interesa que la aplicación cargue de una sola vez, de manera que sacrificando un pequeño período de tiempo al inicio, conseguimos que la velocidad y la fluidez sea mucho mas alta durante el resto de la navegación.

Para aplicaciones web usadas dentro de una red local, esta opción es más que perfecta, pero ¿qué podemos hacer si nuestra aplicación crece mucho y sobrecarga el navegador? ¿y si un tipo de usuario concreto sólo debe acceder a una parte muy pequeña de la aplicación?

Este problema lo solucionamos usando Lazy Loading, que básicamente significa que la aplicación web no se cargará de una sola vez al arranque, si no que se irá cargando a demanda según la necesidad del usuario.

Empezando: las rutas

Para empezar a trabajar con Lazy Loading lo primero es entender como carga las rutas Angular.

Si ya tenemos un proyecto, vamos a abrir nuestro fichero de rutas y si no es así puedes crear un nuevo proyecto especificando  el parámetro –routing para que te genere automáticamente un fichero de rutas:

ng new --routing

En mi caso, mi fichero es el siguiente:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { InicioComponent } from './inicio/inicio/inicio.component';
import { GalleryShowComponent } from './gallery-show/gallery-show/gallery-show.component';
import { AboutComponent } from './about/about/about.component';
import { GallerySelectorComponent } from './gallery-selector/gallery-selector/gallery-selector.component';

const routes: Routes = [
{ path: 'informatica', loadChildren: './inicio/inicio.module#InicioModule' },
{ path: 'gallery/view', loadChildren: './gallery-show/gallery-show.module#GalleryShowModule' },
{ path: 'about', loadChildren: './about/about.module#AboutModule' },
{ path: 'gallery', loadChildren: './gallery-selector/gallery-selector.module#GallerySelectorModule' },
{ path: '**', redirectTo: '/informatica'}
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

Aquí lo que estamos viendo es que nuestro módulo de rutas define unas rutas concretas para las diferentes páginas que tendremos, pero en lugar de referenciar una ruta a un componente, lo estamos referenciando a un módulo. Vamos a fijarnos en la galaeria (por ejemplo):

{ path: 'gallery', loadChildren: './gallery-selector/gallery-selector.module#GallerySelectorModule' },

Tenemos por un lado el path, donde especificamos la url y en loadChildren escribiremos la ruta al archivo del módulo que contiene nuestros componentes para esa página. La sintaxis especifica después de la ruta el nombre del módulo (separado por una ‘#’.

LoadChildren lo que nos indica es que el módulo tendrá sus propias rutas definidas, por lo que no las cargamos, simplemente cuando las necesitemos ya las buscará.

Fíjate que cargamos ya los componentes que forman parte de los módulos que vamos a usar.

Hasta aquí fácil no?

Creando nuestros módulos

Ahora tendremos que definir nuestros módulos y escribir en ellos las rutas que cargaremos. Para seguir con el ejemplo, os pondré el contenido del módulo gallery-selector (es el que esta funcionando en esta misma web 😀 )

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GallerySelectorComponent } from './gallery-selector/gallery-selector.component';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: '', component: GallerySelectorComponent},
];


@NgModule({
  imports: [
    CommonModule, RouterModule.forChild(routes)
  ],
  declarations: [GallerySelectorComponent]
})
export class GallerySelectorModule { }

En este fichero debemos fijarnos sobre todo en cuatro cosas:

  • Importamos los componentes que vamos a usar y forman parte del módulo
  • Definimos una variable routes con las rutas definitivas
  • Importamos Routes y RouterModule
  • Añadimos en imports «RouterModule.forChild(routes)» para que las rutas estén disponibles cuando el archivo de rutas maestro lo consulte.

 

Comandos útiles:

//Generar un módulo nuevo
ng generate module mi-modulo

//Cenerar un componente dentro de un módulo
ng generate component mi-modulo/mi-componente

Probando la configuración

Llegados a este punto nuestra aplicación ya empezará a generar el Lazy loading, pero y si queremos comprobar que realmente funciona?

Sólo tenemos que usar las herramientas de desarrollador de Chrome y fijarnos en las Request que se generan:

Como puedes ver se genera una serie de peticione sque cargan los JS inciales y sólo al visitar un álbum se genera la petición de gallery-show.module.chunk.js

Esto mismo puedes comprobarlo en el apartado de galeria de la web y verlo por ti mismo! (Recuerda que una vez compilado angular renombra los módulos con un hash!)

Como curiosidad, comentar que como convenio se suele poner un + en cada carpeta que contiene un módulo cargado por Lazy Loading. De esta manera tendremos contentos hasta a los más puritanos!

Conectar a Oracle con PHP

Hace un par de semanas estuve haciendo unas pruebas consultando una base de datos de Oracle y se me pasó por la cabeza la brillante idea de usar Symfony (un framework de php) para montar las consultas.

Mi sorpresa llegó cuando vi que la instalación por defecto de php no trae un driver para oracle y que al activarlo, apache no arranca.

Después de googlear un poco, todo el mundo recomienda usar el instant client de oracle (que viene a ser como un cliente ligero y portable de oracle) y todos te dicen: «descomprímelo en «X» directorio y añádelo al PATH, con eso te funcionará». Todo mentiras, y si estas aquí probablemente hayas llegado a la misma conclusión.

En primer lugar te diré que yo he optado por usar un XAMMP en mi entorno de desarrollo, por lo que mi manual se basará en configurar XAMMP + PHP + ORACLE, así que si no quieres usar XAMMP, puedes seguir el manual, pero probablemente tengas que instalar manualmente el plugin de oracle para php/apache (hay bastantes manuales por ahí que lo explican).

Dicho esto, empecemos!

1 - Descarga todos los archivos necesarios

*Fíjate que la versión del instant client es la 12.1 (no descargues la 12.2 ya que no te va a funcionar !!!!

2 - Descomprime el Instant Client 12.1

En primer lugar, crea la siguiente ruta y descomprime dentro las tres descargas del instant client 12.1  «C:\php-sdk\oracle\x86\instantclient_12_1» par que queden así:

3 - Instala XAMMP

Esta paso es muy simple, siguiente siguiente y aceptar!
Una vez lo tengas instalado, crea en la raiz de htdocs un documento «phpinfo.php» que contenga en siguiente código:

 <?php phpinfo(); ?>

Ahora accederemos con el navegador a http://localhost/phpinfo.php y nos cargará una pagina con toda la información de php. Aqui buscaremos el apartado «Configure Command»

Si te fijas, en este apartado aparece la ruta que hemos creado antes (si, no me la he sacado de la manga, la creé porque el comando que arranca apache especifica esta ruta en concreto. Podría ser que en versiones posteriores de XAMMP la ruta cambiara, si es así, modifica el directorio para que se adapte.

4 - Crea las variables de entorno

Para que el sistema sepa donde se encuentra el instantclient, tenemos que decírselo en las variables de entorno.

Para ello modificaremos el PATH añadiéndole al inicio la ruta en la que hemos puesto el instant client

Y crearemos una variable nueva que se llame «ORACLE_HOME» con la ruta.

5 - Editamos php.ini

Buscaremos la línea en la que se encuentra el plugin OCI8 de oracle y le quitaremos el comentario (;) del inicio de la línea

Después de esto, deberemos reiniciar, ya que normalmente las variables del PATH no se refrescan de manera correcta.

6 - Arrancamos apache

Si todo ha ido bien y no nos hemos saltado ningún paso, apache arrancará correctamente y podremos conectar a Oracle sin problemas!

Aquí os dejo un código para verificar que funciona:

 <?php 
$conn = oci_connect('user', 'pass', 'ip:1521/service_name');
$query = "sql_string";
$stid = oci_parse($conn, $query);
oci_execute($stid, OCI_DEFAULT);

echo "<table>";
while ($row = oci_fetch_array($stid, OCI_ASSOC)) {
echo "<tr>";
foreach ($row as $item) {
echo "<td> " . $item . " </td>";
} echo "</tr>"; }
echo "</table>";

oci_free_statement($stid);
oci_close($conn); ?>

Si os encontrarais con un error del tipo «Global variable undefined» o «Constant undefined» o similar, es que vuestro sistema no encuentra el instant client, revisa la configuración o renicia si no lo has hecho!

7 - Caracteres españoles

Después de conseguir acceder a oracle, me encontré con que el cliente me retornaba valores «?» en lugar de los caracteres españoles como la «ñ» o los acentos.

Esto es debido a que el instant client parece estar preparado para funcionar a la inglesa (vaya, como todo en la informática). Así que si necesitas acceder a cadenas con texto en castellano, deberás especificarle el NLS_LANG.

Para ello, vamos a tirar de nuevo de las variables del sistema, y al igual que hemos definido el ORACLE_HOME, crearemos una que se llame «NLS_LANG» y le daremos como valor «SPANISH_SPAIN.WE8ISO8859P1»

Con esto ya tendríamos el problema resuelto, pero en muchos foros recomiendan añadir la siguiente línea en el httpd.conf de apache justo al final:

 SetEnv NLS_LANG "SPANISH_SPAIN.WE8ISO8859P1"

En mi caso no fue necesario, pero no está demás comentarlo por si a ti no te funciona. Después de esto, como hemos modificado variables del sistema, reiniciamos de nuevo y a funcionar!

Debes tener en cuenta que cuando desees presentar cualquier texto, no vendrá como UTF8, por lo que deberás pasarle la función utf8_encode() para que lo printee correctamente por pantalla.

Si tienes una array, y quieres convertirla entera, puedes usar la siguiente función

 

public static function array_utf8_encode($dat)
{
if (is_string($dat))
return utf8_encode($dat);
if (!is_array($dat))
return $dat;
$ret = array();
foreach ($dat as $i => $d)
$ret[$i] = self::array_utf8_encode($d);

return $ret;
}

Bonus track

Si has estado atento en la intro, he comentado que usaba symfony para conectar a oracle. Si simplemente quieres lanzar consultas contra oracle, con esto te funcionaría, pero si llevas idea de usar Doctrine (el ORM de symfony) hay unos cuantos pasos que deberás hacer para que funcione correctamente. En el siguiente post, explicaré como se hace ! 

 

Sistema de Monitorización

Hace algún tiempo me encontré un problema a la hora de monitorizar ciertos servidores que se encontraban en centros remotos, así como servicios web que debían estar disponibles. Existen muchas herramientas como Nagios y similares que permiten este tipo de monitorizaciones, pero en muchos casos no querremos montar un servidor sólo para monitorizar dos o tres IP’s y una web.

Como las pequeñas herramientas que encontré por internet no me convencieron demasiado, decidí programar una con funciones de notificación por e-mail, comprobación de ping y consulta web para detectar errores (en caso de que el servidor este online pero, por ejemplo, el servicio web este dando Error 500)

Las funcionalidades de que dispone el programa son:

  • Comprobación de ping
    • Registro de timeouts
    • Tiempo timeout(en ms) configurable
    • Notificación por mail (en la caída y en la recuperación del host)
    • Dos niveles de error (En rojo sin notificación pero con registro, y notificación por mail)
    • Registro de timeouts en fichero
  • Comprobación web
    • Comprobación de estado
    • Búsqueda de cadenas dentro del HTML retornado
    • Notificación por mail
    • Dos niveles de error (En rojo sin notificación pero con registro, y notificación por mail)
    • Registro de timeouts en fichero
  • Posibilidad de mantener la ventana sobre el resto de ventanas (On top)
  • Transparencia al perder el foco
  • Lectura del registro desde la aplicación
  • Archivos de configuración ini

Las configuraciones son las siguientes:
#ips.ini
DESCRIPCION;192.168.XXX.XXX;mail@from.com;mail@to.com;servidorsmtp.com
BDCENTRAL;192.168.1.1;alertas@gmail.com;alexlatorre@me.com;smtp.gmail.com

#webs.ini
WEBCENTRAL;http://miservidor/login.html;mail@from.com;alexlatorre@me.com;smtp.gmail.com;palabras-a-buscar-separadas-por-guiones

 

El sistema de envío de mails no admite servidores con autenticación, por lo que debes ususarlo con un servidor smtp propio o uno anónimo.
Actualmente no tengo previsto modificar el programa, pero si hubiera una buena sugerencia no tengo problema en ponerme a ello!

 

Descargar “PingPong!”

PingPong.zip – Descargado 904 veces – 50,69 KB

Symfony y date.timezone php.ini

PHP (como ya habrás visto) es bastante ****** con los timezone. Para ser exactos, yo me encontré un problema debido a que al instalar Symfony, se queja con un warning de que el default timezone no esta setteado.

El error es el siguiente:

Warning: date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in...

Después de darle muchas vueltas, me encontré que la causa es que el php.ini que usa la herramienta de linea de comandos de php es diferente del que usa el interprete de apache.

En mi caso al ejecutar:
MacBook:htdocs alatorre$ php composer.phar create-project symfony/framework-standard-edition testProject
funcionaba correctamente, pero al ejecutar comandos de cache:clear y similares, lanzaba el warning.

Para comprobar en linea de comandos que timezone estas usando y que php.ini estas usando, puedes ejecutar los siguientes comandos:

php -i | grep "timezone"
php -i | grep php.ini

Y para resolver el problema es tan sencillo como copiar el php.ini que usas con apache en la ruta que te indica el comando anterior (habitualmente /etc/php.ini) o realizar una copia del default cambiándole el nombre a php.ini y setteando el valor correcto (en mi caso Europe/Madrid) en la linea date.timezone:

date.timezone = Europe/Madrid

** Ojo! Si la linea esta comentada (; al inicio de la línea), descomentala!

Con esto tendrás un bonito colorido verde cuando ejecutes tus comandos desde Terminal! 😀

Conexión Puerto Serie TTY desde Mac OS X

Si has llegado hasta aquí es porque te has encontrado con el mismo problema que yo hace un par de meses.  Vamos, que estás usando un Macbook para configurar un switch cisco, una cabina NetApp … o en definitiva para conectar por medio de un puerto serie con un dispositivo que lo requiera.

Los portátiles de Apple hace mucho que no tienen puerto com (si es que lo han tenido en algún momento) y además el programa por excelencia para realizar este tipo de conexiones (putty) no funciona en Mac OS X.

Al buscar un poco de info sobre el tema, no parecía haber ningún software para realizar esta función y, aunque al principio me extrañó, la respuesta es más que simple. Usando un adaptador COM-USB podemos realizar esta conexión desde la Terminal con dos comandos:

  1. En primer lugar conectaremos el dispositivo USB
  2. Abrimos un Terminal
  3. Conectamos el cable del dispositivo
  4. Ejecutamos estos comandos:
  5. macbook:user$ cd /dev
    macbook:user$ ls -ltr /dev/*usb*
    crw-rw-rw- 1 root wheel 9, 66 Apr 1 16:46 tty.usbmodem1a21
  6. Con esto veremos una lista de los dispositivos USB que tenemos conectados
  7. Conectamos con el siguiente comando usando el dispositivo TTY que nos aparece en la lista especificando la velocidad del puerto (habitualmente 9600)
  8. macbook:user$ screen /dev/tty.usbmodem1a21 9600

Y listo! Con esto ya podremos configurar nuestro dispositivo sin problemas. Para salir simplemente hacemos un Enter Ctrl+Aseguido de Ctrl+\ o simplmente cerramos el terminal finalizando todos los procesos.

¡Espero que haya sido útil!

¡Hola mundo!

Una prueba de código!

using System;

public class Hello3
{
   public static void Main(string[] args)
   {
      Console.WriteLine("Hello, World!");
      Console.WriteLine("You entered the following {0} command line arguments:",
         args.Length );
      for (int i=0; i < args.Length; i++)
      {
         Console.WriteLine("{0}", args[i]); 
      }
   }
}