spfx

SPFX: Dynamic Data en App Extension

Si echamos la vista atrás y retrocedemos dos veranos, hasta el momento en que se anunció el SharePoint FrameWork, podemos ver cómo ha evolucionado y sin duda, afirmar que ha venido para quedarse.Todavía vemos muchas novedades que llegan versión tras versión, una de ellas es la que ha traído la nueva versión 1.5 del Spfx, que va a permitir que dispongamos de mecanismos para conectar los distintos Webparts y Extensions entre sí.

Podemos ver cómo hacer la conexión de WebParts con tan solo mirar el ejemplo publicado en el repositorio oficial de GitHub . En este artículo vamos a ver cómo conectar de forma dinámica las Extensión de tipo Application, con los Webparts que hay en nuestra aplicación.

Un poco de historia

Los más nuevos dentro del desarrollo en SharePoint, tal vez no sepan que desde la versión 2007 existía la posibilidad de conectar los WebParts, tanto los que desarrollábamos como los que venían de serie dentro del Producto. No obstante, ejecutar esta funcionalidad mediante programación era un auténtico engorro (todavía recuerdo el tiempo que invertimos en varios proyectos que desarrollamos en ENCAMINA). Si alguien tiene curiosidad por ver cómo se hacía, escribí un artículo en la revista CompartiMOSS (mi primer artículo, por cierto :))

Ahora bien, cuando Spfx nació, no eché mucho de menos esta funcionalidad. Quizá fue por el engorro que suponía en las versiones anteriores, o bien porque debido a que el desarrollo se realizaba en la parte cliente, se podía conseguir el mismo fin de una forma un tanto peculiar.

Por ejemplo: almacenando la información solicitada en el caché del propio navegador y desde el otro WebPart, leer dicha caché. De esta forma se comparte información entre WebParts y nos ahorramos peticiones innecesarias a los servicios que consumamos. Eso sí,  hay que tener en cuenta algunos factores que pueden hacer que nuestro desarrollo se complique: el orden de carga de los WebParts, la modificación de los datos del WebPart origin, etc..

Resumiendo, teníamos dos opciones: montar un desarrollo relativamente complicado o bien hacer un Mega WebPart que los agrupara a todos para simplificar el desarrollo.

Cómo lo han implementando en Spfx

Este era mi gran temor. Sufridor como he sido de las versiones antiguas, tenía miedo de que los dynamic data fueran  difíciles de utilizar y cayeran en el desuso. Sin embargo, cuál ha sido mi sorpresa al descubrir que han implementado una propiedad de manera muy sencilla y fácil de usar.

Ejemplo:

1.Vamos a implementar una extensión que va a ser un caja de texto.

2. Este texto lo vamos a enviar a un WebPart

3. Y éste, con dicha información, ya realizará lo que el crea conveniente…

¿Cómo lo implementamos?

Nuestra solución va a tener dos elementos:

Una Extensión de tipo Application que será la encargada de notificar a todos los WebParts/extensión que estén conectadas.
Un WebPart que va a ser el encargado de recibir los datos de la Extensión.

Para crear la solución utilizaremos el generado de Yeoman, pero le indicaremos que vamos a utilizar la versión en beta. Para ello:

yo @microsoft/sharepoint --plusbeta

Si la plantilla de Yeoman os da error, es debido a que no está actualizada. Os recomiendo que leáis este artículo sobre cómo actualizar nuestra versión de Spfx.

Crearemos dos artefactos, la extensión de tipo Application y un webPart normal, seleccionando el Framework ReactJS. Dentro de los dynamic tenemos dos actores:

  • el objeto «emisor» que emite información y que la recibe a quien se subscribe.
  • el objeto «subcriptor» que es el encargado de recibir la información.

En este caso, esta claro que la Extensión  será nuestro emisor y el WebPart el receptor.

Implementación del emisor

Para que en la extensión pueda ser un «emisor» deberemos indicar en el método Init que este artefacto tiene información que compartir:

    this.context.dynamicDataSourceManager.initializeSource(this);

Para poder utilizar la clase DynamicDataSource Manager deberemos de tener importadas las siguientes referencias:

import { IDynamicDataController, IDynamicDataPropertyDefinition } from "@microsoft/sp-dynamic-data";

Nos faltaría indicar qué clase de objeto vamos a devolver y cómo se llama, para que el resto de elementos se puedan subscribir al mismo. Para ello implementaremos estos dos métodos getter/setter que nos devuelve la información, de manera que en el componente tendremos una propiedad con el valor que vamos a devolver.

  private _selectedText: IQuery;
 public getPropertyDefinitions(): ReadonlyArray<IDynamicDataPropertyDefinition> {
    return [
      {
        id: "text",
        title: "Text"
      }
    ];
  }
  /**
   * Return the current value of the specified dynamic data set
   * @param propertyId ID of the dynamic data set to retrieve the value for
   */  public getPropertyValue(propertyId: string): IQuery  {
    switch (propertyId) {
      case "text":
        return this._selectedText;
    }

    throw new Error("Bad property id");
  }

La interfaz IQuery no es más que lo siguiente:

/**
 * Represents search
 */export interface IQuery  {
    /**
     * The text  to search
     */    text: string;
  }

Una vez ya sabemos la estructura que vamos a devolver, ahora solo nos queda notificar al resto de WebParts los datos que queremos enviar. En nuestro caso, tenemos el siguiente componente ReactJS

export interface ISearchExtensionProps {
  context:any;
  onchange: (text: IQuery)=> void ;
}
export default class SearchExtension extends React.Component<ISearchExtensionProps, {}> {
  constructor (props:ISearchExtensionProps) {
    super(props);
this._onChanged.bind(this);
  }

  public render():any {
    return (
      
<div >
        
<div className="ms-bgColor-themePrimary">
        <TextField label="Search:" onChanged={this._onChanged} />
        </div>

      </div >
    );
  }
  private _onChanged = (text: any): void => {
    this.props.onchange({text:text});
  }
}

Con este componente dentro de nuestra Extensión, tendremos que implementar un método para que cada vez que el campo se modifique, cambien el resto de los WebParts. Para ello nos definimos el siguiente método:

  private _textSelected = (text: IQuery): void => {
    // store the currently selected event in the class variable. Required
    // so that connected component will be able to retrieve its value
    this._selectedText = text;
    // notify subscribers that the selected text has changed
    this.context.dynamicDataSourceManager.notifyPropertyChanged("text");
  }

Cuando creamos el Componente SearchExtension le vamos a pasar este método, de tal forma que tengamos capturado el evento de modificación de la caja de texto.

 if (this._headerPlaceholder.domElement) {
      const element: React.ReactElement<any> = React.createElement(
        SearchExtension,
        {
          context: this.context,
          onchange : this._textSelected
        }
      );
      ReactDom.render(element, this._headerPlaceholder.domElement);
    }

Implementación del Subscriptor

Una vez ya tenemos la Extensión que se encarga de enviar a sus sucriptores el contenido que se ha encargado de compartir, vamos a implementar un WebPart que se encargará de mostrar dicho contenido. En primer lugar
debemos de subscribirmos al evento para ello dentro del render tendremos que poner la siguiente linea:

 this.context.dynamicDataProvider.registerPropertyChanged(this.properties.sourceId, this.properties.propertyId, this.render);

Los tres parámetros que esta esperando son el sourceId, el propertyId y el método al que se notificara cuando se modifiquen los datos. Los dos primeros valores los definimos en el emisor, pero no hace falta que los pongamos directamente se puede obtener del contexto o bien ponerlos como parámetro de configuración. Para ello podemos poner el siguiente código dentro del ToolPart del código:

// get all available dynamic data sources on the page
    const sourceOptions: IPropertyPaneDropdownOption[] =
      this.context.dynamicDataProvider.getAvailableSources().map(source => {
        return {
          key: source.id,
          text: source.metadata.title
        };
      });
    const selectedSource: string = this.properties.sourceId;

    let propertyOptions: IPropertyPaneDropdownOption[] = [];
    if (selectedSource) {
      const source: IDynamicDataSource = this.context.dynamicDataProvider.tryGetSource(selectedSource);
      if (source) {
        // get the list of all properties exposed by the currently selected
        // data source
        propertyOptions = source.getPropertyDefinitions().map(prop => {
          return {
            key: prop.id,
            text: prop.title
          };
        });
      }

Una vez ya tenemos nuestro webPart subscrito a las notificaciones el siguiente paso es acceder a dicho valor para ello:

  const source: IDynamicDataSource = this.context.dynamicDataProvider.tryGetSource(this.properties.sourceId);
  query = source ? source.getPropertyValue(this.properties.propertyId) : undefined;

con el valor que tiene Query ya podemos implementar lo que consideremos oportuno, en nuestrocaso vamos a pasarlo al siguiente objeto y se mostrará por pantalla.

export default class SearchWebPart extends React.Component<ISearchWebPartProps, {}> {
  public render(): React.ReactElement<ISearchWebPartProps> {
    let data:string="No hay nada que buscar";
    if (this.props.query!==undefined) {
      data=this.props.query.text;
    }
    return (
      
<div className={ styles.searchWebPart }>
        
<div className={ styles.container }>
          
<div className={ styles.row }>
            
<div className={ styles.column }>
              
<h2>BUscar por </h2>

 {data}
            </div>

          </div>

        </div>

      </div>

    );
  }
}

Resumen
Como hemos podido ver en este extenso artículo, Spfx cada vez es más completo y presenta  más opciones para cubrir todos los casos que se utilizaban en las clasificadas Farm Solution. Pero en este caso, y este es un punto a favor de Spfx,  han aprendido de los errores del pasado y ahora nos ofrecen mucho mejores posibilidades para poder incorporarlas en nuestro día a día. ¿Qué os parece?

Compartir
Publicado por
Adrián Díaz

Este sitio web utiliza cookies para que tengas la mejor experiencia de usuario. Si continuas navegando, estás dando tu consentimiento para aceptar las cookies y también nuestra política de cookies (esperemos que no te empaches con tanta cookie 😊)