Compartir a través de


Compilar elementos web del lado cliente de SharePoint Framework con AngularJS

Nota:

Esta página se ha retirado ya que AngularJS v1 es muy antiguo y ya no es compatible con los mantenedores de Angular.

Si está familiarizado con AngularJS, también puede usar este popular marco para compilar elementos web del lado cliente. Gracias a su diseño modular, se puede usar para todo, desde aplicaciones complejas de una página de vista múltiple hasta componentes más pequeños, como elementos web. Muchas organizaciones han usado AngularJS para crear soluciones de SharePoint en el pasado. En este artículo, se muestra cómo usar AngularJS para compilar un elemento web del lado cliente de SharePoint Framework y aplicarle un estilo mediante Office UI Fabric.

Durante este tutorial, compilará un elemento web simple que administra tareas pendientes.

Elemento web del lado cliente de SharePoint Framework que se compila mediante AngularJS mostrado en SharePoint Workbench

El origen del elemento web de trabajo está disponible en GitHub en samples/angular-todo/.

Nota:

Antes de seguir los pasos de este artículo, asegúrese de configurar el entorno de desarrollo para compilar soluciones de SharePoint Framework.

Crear un proyecto

  1. Cree una carpeta para el proyecto

    md angular-todo
    
  2. Vaya a la carpeta del proyecto:

    cd angular-todo
    
  3. En la carpeta del proyecto, ejecute el generador de Yeoman de SharePoint Framework para aplicar scaffolding a un nuevo proyecto de SharePoint Framework:

    yo @microsoft/sharepoint
    
  4. Cuando se le pida, defina los valores de la manera siguiente:

    • angular-todo como el nombre de la solución
    • Usar la carpeta actual como la ubicación para colocar los archivos.
    • Ningún marco de JavaScript como punto de partida para compilar el elemento web
    • Tarea pendiente como el nombre del elemento web
    • Administración simple de tareas pendientes como la descripción del elemento web

Generador de Yeoman de SharePoint Framework con las opciones predeterminadas

  1. Una vez finalizada la técnica de scaffolding, ejecute el comando siguiente para bloquear la versión de las dependencias del proyecto:
npm shrinkwrap
  1. Abra la carpeta del proyecto en el editor de código. En este tutorial, usará Visual Studio Code.

    Proyecto de SharePoint Framework abierto en Visual Studio Code

Agregar AngularJS

En este tutorial, cargará AngularJS desde una red CDN.

En el editor de código, abra el archivo config/config.json y, en la propiedad externals, agregue las líneas siguientes:

"angular": {
  "path": "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.min.js",
  "globalName": "angular"
}

AngularJS se agrega al archivo config.json

Agregar declaraciones de tipo de AngularJS para TypeScript

Ya que hace referencia a AngularJS en el código del elemento web, también necesita declaraciones de tipos de AngularJS para TypeScript.

Para instalarlos, ejecute lo siguiente en la línea de comandos:

npm install @types/angular --save-dev

Implementar la aplicación de AngularJS

Una vez que todos los requisitos previos están preparados, puede comenzar a implementar la aplicación de ejemplo de AngularJS. Dado que consta de varios archivos, cree una carpeta independiente para ellos denominada app.

La carpeta app resaltada en Visual Studio Code

Implementar el servicio de datos de tareas pendientes

En la carpeta app que acaba de crear, cree un archivo denominado DataService.ts. Pegue el código siguiente en el archivo:

export interface ITodo {
  id: number;
  title: string;
  done: boolean;
}

export interface IDataService {
  getTodos(hideFinishedTasks: boolean): angular.IPromise<ITodo[]>;
  addTodo(todo: string): angular.IPromise<{}>;
  deleteTodo(todo: ITodo): angular.IPromise<{}>;
  setTodoStatus(todo: ITodo, done: boolean): angular.IPromise<{}>;
}

export default class DataService implements IDataService {
  public static $inject: string[] = ['$q'];

  private items: ITodo[] = [
    {
      id: 1,
      title: 'Prepare demo Web Part',
      done: true
    },
    {
      id: 2,
      title: 'Show demo',
      done: false
    },
    {
      id: 3,
      title: 'Share code',
      done: false
    }
  ];
  private nextId: number = 4;

  constructor(private $q: angular.IQService) { }

  public getTodos(hideFinishedTasks: boolean): angular.IPromise<ITodo[]> {
    const deferred: angular.IDeferred<ITodo[]> = this.$q.defer();

    const todos: ITodo[] = [];
    for (let i: number = 0; i < this.items.length; i++) {
      if (hideFinishedTasks && this.items[i].done) {
        continue;
      }

      todos.push(this.items[i]);
    }

    deferred.resolve(todos);

    return deferred.promise;
  }

  public addTodo(todo: string): angular.IPromise<{}> {
    const deferred: angular.IDeferred<{}> = this.$q.defer();

    this.items.push({
      id: this.nextId++,
      title: todo,
      done: false
    });

    deferred.resolve();

    return deferred.promise;
  }

  public deleteTodo(todo: ITodo): angular.IPromise<{}> {
    const deferred: angular.IDeferred<{}> = this.$q.defer();

    let pos: number = -1;
    for (let i: number = 0; i < this.items.length; i++) {
      if (this.items[i].id === todo.id) {
        pos = i;
        break;
      }
    }

    if (pos > -1) {
      this.items.splice(pos, 1);
      deferred.resolve();
    }
    else {
      deferred.reject();
    }

    return deferred.promise;
  }

  public setTodoStatus(todo: ITodo, done: boolean): angular.IPromise<{}> {
    const deferred: angular.IDeferred<{}> = this.$q.defer();

    for (let i: number = 0; i < this.items.length; i++) {
      if (this.items[i].id === todo.id) {
        this.items[i].done = done;
      }
    }

    deferred.resolve();

    return deferred.promise;
  }
}

El archivo DataService.ts abierto en Visual Studio Code

En el fragmento de código anterior, se implementan tres tipos:

  • La interfaz ITodo, que representa una tarea pendiente en la aplicación.
  • La interfaz IDataService, que define la firma del servicio de datos.
  • La clase DataService, que es la responsable de recuperar y manipular las tareas pendientes.

El servicio de datos implementa métodos sencillos para agregar y modificar las tareas pendientes. Aunque las operaciones son instantáneas, por coherencia, cada función CRUD devuelve una promesa.

En este tutorial, las tareas pendientes se almacenan en la memoria. En cambio, podría ampliar fácilmente la solución para almacenar tareas en una lista de SharePoint y usar el servicio de datos para comunicarse con SharePoint mediante la API de REST.

Implementar el controlador

Después, implemente el controlador que realiza la comunicación entre la vista y el servicio de datos.

En la carpeta app, cree un archivo denominado HomeController.ts y pegue el código siguiente:

import { IDataService, ITodo } from './DataService';

export default class HomeController {
  public isLoading: boolean = false;
  public newItem: string = null;
  public newToDoActive: boolean = false;
  public todoCollection: any[] = [];
  private hideFinishedTasks: boolean = false;

  public static $inject: string[] = ['DataService', '$window', '$rootScope'];

  constructor(private dataService: IDataService, private $window: angular.IWindowService, private $rootScope: angular.IRootScopeService) {
    const vm: HomeController = this;
    this.init();
  }

  private init(hideFinishedTasks?: boolean): void {
    this.hideFinishedTasks = hideFinishedTasks;
    this.loadTodos();
  }

  private loadTodos(): void {
    const vm: HomeController = this;
    this.isLoading = true;
    this.dataService.getTodos(vm.hideFinishedTasks)
      .then((todos: ITodo[]): void => {
        vm.todoCollection = todos;
      })
      .finally((): void => {
        vm.isLoading = false;
      });
  }

  public todoKeyDown($event: any): void {
    if ($event.keyCode === 13 && this.newItem.length > 0) {
      $event.preventDefault();

      this.todoCollection.unshift({ id: -1, title: this.newItem, done: false });
      const vm: HomeController = this;

      this.dataService.addTodo(this.newItem)
        .then((): void => {
          this.newItem = null;
          this.dataService.getTodos(vm.hideFinishedTasks)
            .then((todos: any[]): void => {
              this.todoCollection = todos;
            });
        });
    }
  }

  public deleteTodo(todo: ITodo): void {
    if (this.$window.confirm('Are you sure you want to delete this todo item?')) {
      let index: number = -1;
      for (let i: number = 0; i < this.todoCollection.length; i++) {
        if (this.todoCollection[i].id === todo.id) {
          index = i;
          break;
        }
      }

      if (index > -1) {
        this.todoCollection.splice(index, 1);
      }

      const vm: HomeController = this;

      this.dataService.deleteTodo(todo)
        .then((): void => {
          this.dataService.getTodos(vm.hideFinishedTasks)
            .then((todos: any[]): void => {
              this.todoCollection = todos;
            });
        });
    }
  }

  public completeTodo(todo: ITodo): void {
    todo.done = true;

    const vm: HomeController = this;

    this.dataService.setTodoStatus(todo, true)
      .then((): void => {
        this.dataService.getTodos(vm.hideFinishedTasks)
          .then((todos: any[]): void => {
            this.todoCollection = todos;
          });
      });
  }

  public undoTodo(todo: ITodo): void {
    todo.done = false;

    const vm: HomeController = this;

    this.dataService.setTodoStatus(todo, false)
      .then((): void => {
        this.dataService.getTodos(vm.hideFinishedTasks)
          .then((todos: any[]): void => {
            this.todoCollection = todos;
          });
      });
  }
}

El archivo HomeController.ts abierto en Visual Studio Code

Primero debe cargar el servicio de datos que ha implementado antes. El controlador lo necesita para obtener la lista de tareas y modificarlas tal y como ha pedido el usuario. El servicio se inserta en el controlador mediante la inserción de dependencias de AngularJS. El controlador implementa una serie de funciones que se exponen en el modelo de vista y a las que se llama desde la plantilla. Con estas funciones, los usuarios pueden agregar nuevas tareas, marcarlas como finalizadas o como pendientes, o eliminarlas.

Implementar el módulo principal

Con el servicio de datos y el controlador listos, defina el módulo principal de la aplicación y registre el servicio de datos y el controlador con él.

En la carpeta app, cree un archivo denominado app-module.ts y pegue el contenido siguiente:

import * as angular from 'angular';
import HomeController from './HomeController';
import DataService from './DataService';

const todoapp: angular.IModule = angular.module('todoapp', []);

todoapp
  .controller('HomeController', HomeController)
  .service('DataService', DataService);

Archivo app-module.ts abierto en Visual Studio Code

Empiece por hacer referencia a AngularJS y cargar el controlador y el servicio de datos previamente implementados. Ahora, defina el módulo de la aplicación. Por último, registre el controlador y el servicio de datos con la aplicación.

Acaba de compilar una aplicación de AngularJS para el elemento web. En los pasos siguientes, registrará la aplicación de AngularJS con el elemento web y la podrá configurar mediante propiedades de elementos web.

Registrar una aplicación de AngularJS con un elemento web

El siguiente paso consiste en agregar la aplicación de AngularJS al elemento web.

  1. En el editor de código, abra el archivo ToDoWebPart.ts.

  2. Justo antes de la declaración de clase, agregue las líneas siguientes:

    import * as angular from 'angular';
    import './app/app-module';
    

    Instrucciones de importación en el archivo ToDoWebPart.ts resaltadas en Visual Studio Code

    Esto nos permite cargar una referencia a AngularJS y a la aplicación, las necesita para arrancar la aplicación de AngularJS.

  3. Cambie la función render() del elemento web por:

    public render(): void {
      if (this.renderedOnce === false) {
        this.domElement.innerHTML = `
    <div class="${styles.toDo}">
      <div data-ng-controller="HomeController as vm">
        <div class="${styles.loading}" ng-show="vm.isLoading">
          <div class="${styles.spinner}">
            <div class="${styles.spinnerCircle} ${styles.spinnerLarge}"></div>
            <div class="${styles.spinnerLabel}">Loading...</div>
          </div>
        </div>
        <div ng-show="vm.isLoading === false">
          <div class="${styles.textField} ${styles.underlined}" ng-class="{'${styles.isActive}': vm.newToDoActive}">
            <label for="newToDo" class="${styles.label}">New to do:</label>
            <input type="text" label="New to do:" id="newToDo" value="" class="${styles.field}" aria-invalid="false" ng-model="vm.newItem" ng-keydown="vm.todoKeyDown($event)" ng-focus="vm.newToDoActive = true" ng-blur="vm.newToDoActive = false">
          </div>
        </div>
        <div class="list" ng-show="vm.isLoading === false">
          <div class="listSurface">
            <div class="listPage">
              <div class="listCell" ng-repeat="todo in vm.todoCollection" uif-item="todo" ng-class="{'${styles.done}': todo.done}">
                <div class="${styles.listItem}">
                  <span class="${styles.listItemPrimaryText}">{{todo.title}}</span>
                  <div class="${styles.listItemActions}">
                    <div class="${styles.listItemAction}" ng-click="vm.completeTodo(todo)" ng-show="todo.done === false">
                      <i class="${styles.icon} ${styles.iconCheckMark}"></i>
                    </div>
                    <div class="${styles.listItemAction}" ng-click="vm.undoTodo(todo)" ng-show="todo.done">
                      <i class="${styles.icon} ${styles.iconUndo}"></i>
                    </div>
                    <div class="${styles.listItemAction}" ng-click="vm.deleteTodo(todo)">
                      <i class="${styles.icon} ${styles.iconRecycleBin}"></i>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>`;
    
        angular.bootstrap(this.domElement, ['todoapp']);
      }
    }
    

    Función render del elemento web en Visual Studio Code

  4. En primer lugar, el código asigna la plantilla de la aplicación directamente al elemento DOM del elemento web. En el elemento raíz, especifique el nombre del controlador que controla los eventos y el enlace de datos en la plantilla.

  5. Arranque la aplicación con el nombre todoapp que ha usado anteriormente al declarar el módulo principal.

  6. Con la propiedad renderedOnce del elemento web, asegúrese de que la aplicación de AngularJS arranque solo una vez. Sin ella, si ha cambiado alguna de las propiedades del elemento web, la función render() se vuelve a invocar, lo que provoca un nuevo arranque de la aplicación de AngularJS, que da lugar a un error.

  7. Implemente los estilos CSS que use en la plantilla. En el editor de código, abra el archivo ToDo.module.scss y reemplace su contenido por lo siguiente:

    .toDo {
      .loading {
        margin: 0 auto;
        width: 6em;
    
        .spinner {
          display: inline-block;
          margin: 10px 0;
    
          @-webkit-keyframes spinnerSpin {
            0% {
              -webkit-transform:rotate(0);
              transform:rotate(0);
            }
            100% {
              -webkit-transform:rotate(360deg);
              transform:rotate(360deg);
            }
          }
          @keyframes spinnerSpin {
            0% {
              -webkit-transform:rotate(0);
              transform:rotate(0);
            }
            100% {
              -webkit-transform:rotate(360deg);
              transform:rotate(360deg);
            }
          }
    
          .spinnerCircle {
            margin: auto;
            box-sizing: border-box;
            border-radius: 50%;
            width: 100%;
            height: 100%;
            border: 1.5px solid #c7e0f4;
            border-top-color: #0078d7;
            -webkit-animation: spinnerSpin 1.3s infinite cubic-bezier(.53, .21, .29, .67);
            animation: spinnerSpin 1.3s infinite cubic-bezier(.53, .21, .29, .67);
    
            &.spinnerLarge {
              width: 28px;
              height: 28px;
            }
          }
    
          .spinnerLabel {
            color: #0078d7;
            margin-top: 10px;
            text-align: center;
          }
        }
      }
    
      .label {
        font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
        -webkit-font-smoothing: antialiased;
        font-size: 14px;
        font-weight: 400;
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        box-shadow: none;
        color: #333333;
        box-sizing: border-box;
        display: block;
        padding: 5px 0
      }
    
      .textField {
        font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
        -webkit-font-smoothing: antialiased;
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        box-shadow: none;
        color: #333333;
        font-size: 14px;
        font-weight: 400;
        margin-bottom: 8px;
    
        &.underlined {
          border-bottom: 1px solid #c8c8c8;
          display: table;
          width: 100%;
    
          &:hover {
            border-color: #767676;
          }
    
          &.isActive, &:active {
            border-color: #0078d7;
          }
    
          .field {
            border: 0;
            display: table-cell;
            padding-top: 8px;
            padding-bottom: 3px
          }
        }
    
        .label {
          padding-right: 0;
          padding-left: 12px;
          margin-right: 8px;
          font-size: 14px;
          display: table-cell;
          vertical-align: top;
          padding-top: 9px;
          height: 32px;
          width: 1%;
          white-space: nowrap;
          font-weight: 400;
        }
    
        .field {
          text-align: left;
          float: left;
          box-sizing: border-box;
          margin: 0;
          padding: 0;
          box-shadow: none;
          font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
          -webkit-font-smoothing: antialiased;
          border: 1px solid #c8c8c8;
          border-radius: 0;
          font-weight: 400;
          font-size: 14px;
          color: #333333;
          height: 32px;
          padding: 0 12px 0 12px;
          width: 100%;
          outline: 0;
          text-overflow: ellipsis;
        }
    
        .field:hover {
          border-color: #767676;
        }
      }
    
      .listItem {
        font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
        -webkit-font-smoothing: antialiased;
        font-size: 14px;
        font-weight: 400;
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        box-shadow: none;
        padding: 9px 28px 3px;
        position: relative;
        display: block;
    
        &::before {
          display: table;
          content: "";
          line-height: 0;
        }
    
        &::after {
          display: table;
          content: "";
          line-height: 0;
          clear: both;
        }
    
        .listItemPrimaryText {
          font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
          -webkit-font-smoothing: antialiased;
          font-size: 21px;
          font-weight: 100;
          padding-right: 80px;
          position: relative;
          top: -4px;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
          display: block;
        }
    
        .listItemActions {
          max-width: 80px;
          position: absolute;
          right: 30px;
          text-align: right;
          top: 10px;
    
          .listItemAction {
            color: #a6a6a6;
            display: inline-block;
            font-size: 15px;
            position: relative;
            text-align: center;
            top: 3px;
            cursor: pointer;
            height: 16px;
            width: 16px;
    
            &:hover {
              color: #666666;
              outline: 1px solid transparent;
            }
    
            .icon {
              vertical-align: top;
            }
          }
        }
      }
    
      .done {
        text-decoration: line-through;
      }
    
      .icon {
        -moz-osx-font-smoothing: grayscale;
        -webkit-font-smoothing: antialiased;
        display: inline-block;
        font-family: FabricMDL2Icons;
        font-style: normal;
        font-weight: 400;
        speak: none;
    
        &.iconCheckMark::before {
          content: "\E73E";
        }
    
        &.iconUndo::before {
          content: "\E7A7";
        }
    
        &.iconRecycleBin::before {
          content: "\EF87";
        }
      }
    }
    

    Archivo ToDo.module.scss abierto en Visual Studio Code

  8. Ejecute el comando siguiente para confirmar que todo funciona como esperaba:

    gulp serve
    
  9. En el explorador, debería ver el elemento web Tareas pendientes con tareas pendientes.

    Elemento web Tareas pendientes que muestra las tareas pendientes presentadas con Office UI Fabric

Permitir la configuración del elemento web

En este momento, el elemento web Tareas pendientes muestra una lista fija de tareas pendientes. Después, la extiende con una opción de configuración para permitir que los usuarios elijan si quieren ver las tareas que están marcadas como realizadas o no.

Agregar una propiedad en el manifiesto del elemento web

Comience por agregar una propiedad de configuración en el manifiesto del elemento web.

En el editor de código, abra el archivo ToDoWebPart.manifest.json. En la sección preconfiguredEntries, vaya a la matriz properties y reemplace la propiedad description existente por la siguiente línea:

"hideFinishedTasks": false

Propiedad hideFinishedTasks resaltada en el manifiesto del elemento web

Actualizar la firma de la interfaz de propiedades del elemento web

Después, actualice la firma de la interfaz de propiedades del elemento web.

En el editor de código, abra el archivo ToDoWebPart.ts y actualice la interfaz IToDoWebPartProps a lo siguiente:

export interface IToDoWebPartProps {
  hideFinishedTasks: boolean;
}

Archivo IToDoWebPartProps.ts abierto en Visual Studio Code

Agregar la propiedad al panel de propiedades del elemento web

Para permitir que los usuarios configuren el valor de la propiedad que acaba de agregar, tiene que exponerla mediante el panel de propiedades del elemento web.

  1. En el editor de código, abra el archivo ToDoWebPart.ts.

  2. En la primera import instrucción, reemplace por PropertyPaneTogglePropertyPaneTextField :

    import {
      BaseClientSideWebPart,
      IPropertyPaneConfiguration,
      PropertyPaneToggle
    } from '@microsoft/sp-webpart-base';
    

    Instrucción import de PropertyPaneToggle resaltada en Visual Studio Code

  3. Cambie la implementación de la función propertyPaneSettings por:

    protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
      return {
        pages: [
          {
            header: {
              description: strings.PropertyPaneDescription
            },
            groups: [
              {
                groupName: strings.ViewGroupName,
                groupFields: [
                  PropertyPaneToggle('hideFinishedTasks', {
                    label: strings.HideFinishedTasksFieldLabel
                  })
                ]
              }
            ]
          }
        ]
      };
    }
    
  4. Para corregir las referencias de cadena que faltan, primero tiene que cambiar la firma de las cadenas. En el editor de código, abra el archivo loc/mystrings.d.ts y cambie su contenido por:

    declare interface IToDoWebPartStrings {
      PropertyPaneDescription: string;
      ViewGroupName: string;
      HideFinishedTasksFieldLabel: string;
    }
    
    declare module 'ToDoWebPartStrings' {
      const strings: IToDoWebPartStrings;
      export = strings;
    }
    

    El archivo loc/mystrings.d.ts abierto en Visual Studio Code

  5. Proporcione los valores reales de las cadenas que acaba de definir. En el editor de código, abra el archivo loc/en-us.js y cambie su contenido por:

    define([], function() {
      return {
        "PropertyPaneDescription": "Manage configuration of the To do web part",
        "ViewGroupName": "View",
        "HideFinishedTasksFieldLabel": "Hide finished tasks"
      }
    });
    

    El archivo loc/en-us.js abierto en Visual Studio Code

  6. Ejecute el comando siguiente para confirmar que todo funciona como esperaba:

    gulp serve
    
  7. En el panel de propiedades del elemento web debería ver un botón de alternancia para la propiedad que acaba de definir.

    Botón de alternancia que aparece en el panel de propiedades del elemento web

    Si en este momento selecciona el botón de alternancia, no pasará nada en el elemento web, ya que no está conectado con AngularJS. Lo podrá hacer en el paso siguiente.

Permitir la configuración de la aplicación de AngularJS mediante las propiedades del elemento web

En el paso anterior, ha definido una propiedad del elemento web que permite a los usuarios elegir si quieren mostrar las tareas completadas o no. Ahora, pasará el valor seleccionado por el usuario a la aplicación de AngularJS, para que pueda cargar elementos.

Difundir un evento de AngularJS en el cambio de propiedad del elemento web

  1. En el editor de código, abra el archivo ToDoWebPart.ts. Justo encima del método de representación render(), agregue la línea siguiente:

    private $injector: angular.auto.IInjectorService;
    

    Variable de clase $injector resaltada en Visual Studio Code

  2. Cambie la función render() del elemento web por lo siguiente:

    public render(): void {
      if (this.renderedOnce === false) {
        this.domElement.innerHTML = `
          <div class="${styles.toDo}">
            <div data-ng-controller="HomeController as vm">
              <div class="${styles.loading}" ng-show="vm.isLoading">
                <div class="${styles.spinner}">
                  <div class="${styles.spinnerCircle} ${styles.spinnerLarge}"></div>
                  <div class="${styles.spinnerLabel}">Loading...</div>
                </div>
              </div>
              <div ng-show="vm.isLoading === false">
                <div class="${styles.textField} ${styles.underlined}" ng-class="{'${styles.isActive}': vm.newToDoActive}">
                  <label for="newToDo" class="${styles.label}">New to do:</label>
                  <input type="text" label="New to do:" id="newToDo" value="" class="${styles.field}" aria-invalid="false" ng-model="vm.newItem" ng-keydown="vm.todoKeyDown($event)" ng-focus="vm.newToDoActive = true" ng-blur="vm.newToDoActive = false">
                </div>
              </div>
              <div class="list" ng-show="vm.isLoading === false">
                <div class="listSurface">
                  <div class="listPage">
                    <div class="listCell" ng-repeat="todo in vm.todoCollection" uif-item="todo" ng-class="{'${styles.done}': todo.done}">
                      <div class="${styles.listItem}">
                        <span class="${styles.listItemPrimaryText}">{{todo.title}}</span>
                        <div class="${styles.listItemActions}">
                          <div class="${styles.listItemAction}" ng-click="vm.completeTodo(todo)" ng-show="todo.done === false">
                            <i class="${styles.icon} ${styles.iconCheckMark}"></i>
                          </div>
                          <div class="${styles.listItemAction}" ng-click="vm.undoTodo(todo)" ng-show="todo.done">
                            <i class="${styles.icon} ${styles.iconUndo}"></i>
                          </div>
                          <div class="${styles.listItemAction}" ng-click="vm.deleteTodo(todo)">
                            <i class="${styles.icon} ${styles.iconRecycleBin}"></i>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>`;
    
        this.$injector = angular.bootstrap(this.domElement, ['todoapp']);
      }
    
      this.$injector.get('$rootScope').$broadcast('configurationChanged', {
        hideFinishedTasks: this.properties.hideFinishedTasks
      });
    }
    

En el ejemplo de código anterior, cada vez que se cambia la propiedad del elemento web, difunde un evento de AngularJS al que se suscribe la aplicación de AngularJS. Una vez que la aplicación de AngularJS recibe el evento, lo controla correctamente.

Suscribirse al evento de cambio de propiedad del elemento web en AngularJS

  1. En el editor de código, abra el archivo app/HomeController.ts. Extienda el constructor de la siguiente forma:

    constructor(private dataService: IDataService, private $window: angular.IWindowService, private $rootScope: angular.IRootScopeService) {
      const vm: HomeController = this;
      this.init();
    
      $rootScope.$on('configurationChanged', (event: angular.IAngularEvent, args: { hideFinishedTasks: boolean }): void => {
        vm.init(args.hideFinishedTasks);
      });
    }
    

    Definición del constructor en el archivo HomeController.ts resaltada en Visual Studio Code

  2. Compruebe que la aplicación de AngularJS funciona como se esperaba y responde correctamente a la propiedad cambiada; para ello, ejecute lo siguiente en la línea de comandos:

    gulp serve
    
  3. Si alterna el valor de la propiedad Ocultar las tareas finalizadas, el elemento web debería mostrar u ocultar las tareas finalizadas correctamente.

elemento web que muestra solo las tareas sin finalizar con la opción “Ocultar las tareas finalizadas” habilitada

Vea también