Estamos como niños con zapatos nuevos desde que aparecieron las extensiones 2.0 en nuestro querido Navision, ahora llamado Business Central. Un poco abrumados, eso sí, pensado en traspasar nuestros addons. Pero con un océano de posibilidades de cara al futuro.
El equipo de desarrollo de Devaim se ha puesto las pilas. Con ganas de descubrir hasta dónde podemos llegar y qué podemos aportar para mejorar las interfaces de usuario.
En esta profesión hay que estar al día, y sabemos que uno de los frameworks de moda en el mundo del front-end en web es Vue.js. Se trata de una librería potente que ayuda a desarrollar aplicaciones reactivas de manera muy rápida. Hemos pensado que podría ser ideal para crear los addins Javascript en Business Central. Podéis aprender más de este framework en su web oficial, donde hay una gran cantidad de tutoriales:
Voy a explicar paso a paso cómo se puede añadir Vue.js en una extensión.
Creando un Addin en JavaScript
Lo primero, es crear un proyecto en Visual Studio Code, conectarlo a una instancia, descargar los símbolos y publicar para comprobar que todo funciona correctamente.
Todo esto no lo voy a explicar aquí. Y si no lo sabéis hacer podéis ir a la documentación de Microsoft:
https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-get-startedUna vez que está todo, ya podemos ponernos manos a la obra. Lo primero es eliminar el fichero HelloWorld.al para que no moleste más el mensajito. Y creamos el primer fichero. Yo lo he llamado AddinVueTest.al. Con el snippet tcontroladdin se nos rellena el código necesario para crear un addin:
controladdin MyControlAddIn { RequestedHeight = 300; MinimumHeight = 300; MaximumHeight = 300; RequestedWidth = 700; MinimumWidth = 700; MaximumWidth = 700; VerticalStretch = true; VerticalShrink = true; HorizontalStretch = true; HorizontalShrink = true; Scripts = 'script1.js', 'script2.js'; StyleSheets = 'style.css'; StartupScript = 'startupScript.js'; RecreateScript = 'recreateScript.js'; RefreshScript = 'refreshScript.js'; Images = 'image1.png', 'image2.png'; event MyEvent() procedure MyProcedure() }
Vamos a hacer unos cambios. Lo primero es crear una carpeta “css” dentro del proyecto para almacenar las hojas de estilo y otra carpeta “js” para los ficheros JavaScript.
Creamos también los siguientes ficheros:
- css/styles.css
- js/startupScript.js
- js/addin.js
Cambiamos el nombre a VueAddin y ahora ya podemos indicar los ficheros que vamos a usar. Además, adaptamos el control a 300 píxeles de altura y cambiamos el nombre del evento a ControlReady y del método a HelloWorld. Podéis llamarlos como queráis, esto es solamente para probar que funciona.
controladdin VueAddin { RequestedHeight = 300; MinimumHeight = 300; MaximumHeight = 300; VerticalStretch = true; VerticalShrink = true; HorizontalStretch = true; HorizontalShrink = true; StartupScript = 'js/startupScript.js'; Scripts = 'js/addin.js'; StyleSheets = 'css/style.css'; event ControlReady(); procedure HelloWorld(); }
En el fichero js/startupScript.js, primero llamamos a la función JavaScript init() (que crearemos después) e invocamos el evento de ControlReady() (que se definirá en control).
init(); Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ControlReady",[]);
En el fichero js/addin.js, la función init() inserta un contenedor (div) en el control. Esto es JavaScript puro. Para probar añadimos la función HelloWorld con un alert.function init(){
function init(){ var div = document.getElementById('controlAddIn'); div.innerHTML += '<div class="contenedor"></div>'; } function HelloWorld(){ alert("Hello world"); }
Vamos a aplicar un estilo al contenedor, en css/styles.css, para ver el control. Le damos un color llamativo y le indicamos que ocupe todo el espacio del addin.
.contenedor{ float: left; width: 100%; height: 300px; overflow-y: scroll; background-color: blue; }
Nos queda extender una página para añadir el control. Por ejemplo, la ficha de cliente, después de la sección General. Lo podemos añadir al mismo fichero AddinVueTest.al. Y llamamos a HelloWorld:
pageextension 50100 CustCardAddinExtension extends "Customer Card" { layout { addafter(General) { usercontrol(VueAddin; VueAddin) { trigger ControlReady() begin CurrPage.VueAddin.HelloWorld(); end; } } } }
Si publicamos y entramos en la ficha de un cliente, podemos ver que funciona el addin JavaScript.
Añadiendo Vue.JS» title_align=»left» title_size=»h2″ title_color=»custom» custom_title_color=»#7c7c7c»]Si leemos la documentación de Vue.js, vemos que podemos añadir la librería desde una url directamente, así que la incluimos directamente en nuestro código del addin. También incluimos el fichero js/app.js (lo tenemos que crear) donde irá el código de la aplicación Vue.
Scripts = 'https://cdn.jsdelivr.net/npm/vue/dist/vue.js', 'js/addin.js', 'js/app.js';
En el fichero js/addin.js, en la función init() insertamos un div en el contenedor con el id “app”. Este div contendrá la aplicación que creemos con Vue.js. Comentamos el alert de HelloWorld para que no moleste más.
function init(){ var div = document.getElementById('controlAddIn'); div.innerHTML += '<div class="contenedor">' + ' <div id="app"></div>' + '</div>'; } function HelloWorld(){ //alert("Hello world"); }
Ya podemos crear nuestra aplicación en js/app.js. Para empezar, hacemos algo sencillo, una caja de texto.
const app = new Vue({ el: '#app', template: [ '<div>', '<input type="text" v-model="texto" />', '</div>' ].join(''), data: { texto: '' } });
Lo primero es crear una nueva constante de tipo Vue. En la propiedad «el» le decimos el id del div que ejecutará la aplicación que estamos definiendo.
La propiedad «template» es una colección de código html, en este caso una caja de texto. Se inserta con join.
Y en «data» es donde definimos un objeto JavaScript con el modelo de la aplicación. En este caso, solamente estará “texto”, que es donde se almacenará el valor de lo que escribamos.
¿Cómo se asocia el valor de texto con la caja de texto? Con «v-model». Es una propiedad de Vue que conecta el html con el modelo que hemos creado.
¿Funcionará? Vamos a publicar a ver.
La caja de texto no aparece. Si inspeccionamos la página con las herramientas de Chrome podemos ver qué error está dando.
No se puede encontrar el elemento #app, pero en la inspección sí que existe.
¿Entonces? ¿No se puede usar Vue.js con Business Central? No nos vamos a rendir. Buscando información, el señor Jeremy Vyska descubrió como incluir Angular.js en un addin. Lo podéis ver aquí: https://community.dynamics.com/nav/b/smallsquareservices/archive/2018/11/21/post-3-angular-inside-dynamics-365-business-central
Si leéis la historia de Vue, sabréis que viene de Angular, así que la solución es la misma. Lo que encontró Jeremy es que los addins de JavaScript cargan todo el código a la vez. Por lo que al ir ejecutarse nuestro fichero app.js, el div “app” no existe aún porque no se ha ejecutado la función init(). Hay que cargar el fichero app.js como una imagen.
Creamos la carpeta images, y movemos el fichero app.js.[vc_single_image image=»10271″ img_size=»full» onclick=»link_image»]En el addin, indicamos que cargue el fichero, pero no como un script, sino como una imagen:
StartupScript = 'js/startupScript.js'; Scripts = 'https://cdn.jsdelivr.net/npm/vue/dist/vue.js', 'js/addin.js'; StyleSheets = 'css/style.css'; Images = 'images/app.js';
Ahora queda por JavaScript incluir el fichero de imagen como un script, en la función init(), tal y como lo hace Jeremy
function init(){ var div = document.getElementById('controlAddIn'); div.innerHTML += '<div class="contenedor">' + ' <div id="app"></div>' + '</div>'; var appUrl = Microsoft.Dynamics.NAV.GetImageResource('app.js'); appUrl = appUrl.replace('app.js','images/app.js'); var runtimeScript = document.createElement('script'); runtimeScript.src = appUrl; document.head.appendChild(runtimeScript); }
Al publicar, vemos que Vue.js funciona.
Ahora, vamos a jugar con la potencia de Vue. Si quisiéramos hacer que el texto dentro de un div se fuera actualizando con el valor de la caja de texto, tendríamos que programar en JavaScript varias cosas. En Vue se hace con una línea de código en el fichero app.js.
const app = new Vue({ el: '#app', template: [ '<div>', '<input type="text" v-model="texto" />', '<div style="color:white">{{ texto }}</div>', '</div>' ].join(''), data: { texto: '' } });
Con {{ texto }} conseguimos que se refresque el interior del div. Le decimos que el color del texto sea blanco para que se distinga en el azul chillón que hemos puesto.
Alberto Guirao Cotillas
Desarrollador en Devaim Consultores