Affichage conditionné en #KnockoutJS et #JSOM

Poursuivons sur l’exemple de la création de session dans SharePoint, maintenant je vous propose de gérer l’inscription. Nous restons avec KnockoutJS et allons définir un composant dont l’affichage est déterminé par l’état des sessions et de l’utilisateur courant.

  • Utilisateur déjà inscrit;
  • Aucune session disponible;
  • Toutes les sessions sont pleines;
  • Inscription possible.

Afin de déterminer l’état du composant, JSOM sera utilisé avec CamlJS. La gestion des SPUserCollectionField réserve quelques surprises, comme vous le verrez plus loin dans l’article.

Actuce 1:  définir une énumération référençant tous les états du composant et lier le modèle à la vue en utilisant l’énumération.

var ReservationStatus = { Full: 0, None: 1, Register: 2, Unregister: 3, Error: 5 };
<div id="UserSessionsFull" style="display:none"
data-bind="visible: $root.Status() == ReservationStatus.Full" >

Astuce 2: quand on requête un élément de type Utilisateur multi-valué, l’objet renvoyé par SharePoint possède la forme suivante :

[{"$b_1": “”,"$M_1":}].

Par contre  SharePoint attend un object sous le format suivant:

[{"$M_1":"", $b_1":""}].

Le message d’erreur aide énormément à comprendre :

args.get_message() : Unknown Error
args.get_stackTrace() : undefined

var users = new Array();
var usersFromSP = ko.toJS(self.SelectedSession()).participants;
for (index = 0, max = usersFromSP.length; index < max; index++) {
    users.push(SP.FieldUserValue.fromUser(usersFromSP[index].$b_1));
}

var userToAdd = SP.FieldUserValue.fromUser(self.CurrentUserLogin());
users.push(userToAdd);
this.oListItem.set_item('Participant', users);

Astuce 3: KO tente de faire le binding de tous les éléments même s’ils ne sont pas affichés. Par conséquent pour éviter une erreur de data binding, il faut mettre une condition au traitement d’un bloc HTML

<!-- ko if: Reservation --></span>
    <span data-bind="text: Reservation().text"><br/>
    <button data-bind="click: $root.Unregister">Me d&eacute;sinscrire</button>
<!-- /ko -->

Astuce 4: dans SharePoint 2010, CamlJS nécessite d’être encapsulé dans «  » sinon cela ne fonctionne pas.

var camlQuery = new SP.CamlQuery();
var caml = new CamlBuilder().Where().UserField("Participant").EqualToCurrentUser().ToString();
camlQuery.set_viewXml("<View><Query>" + caml + "</Query></View>");

Ci-dessous l’intégralité du code HTML et JavaScript:

<div id="UserSessions">
   <div id="UserSessionsFull" style="display:none" data-bind="visible: $root.Status() == ReservationStatus.Full" >
      <h2>Inscription</h2>Toutes les sessions sont pleines.
   </div>
   <div id="UserSessionsNone" style="display:none" data-bind="visible: $root.Status() == ReservationStatus.None">
      <h2>Inscription</h2>Aucune session disponible pour l'instant.
   </div>
   <div id="UserSessionsRegister" style="display:none" data-bind="visible: $root.Status() == ReservationStatus.Register">
      <h2>Inscription</h2>
      Session(s) disponible(s) :
      <select id="DdlSessions" data-bind="options: Sessions, optionsText: 'text', value: SelectedSession"></select><br/>
      <button data-bind="click: $root.Register">M'inscrire</button>
   </div>
   <div id="UserSessionsUnregister" style="display:none" data-bind="visible: $root.Status() == ReservationStatus.Unregister" >
      <h2>D&eacute;sinscription</h2>
      <!-- ko if: Reservation -->
         <span data-bind="text: Reservation().text"></span><br/>
         <button data-bind="click: $root.Unregister">Me d&eacute;sinscrire</button>
      <!-- /ko -->
   </div>
   <div id="UserSessionsError" style="display:none" data-bind="visible: $root.Status() == ReservationStatus.Error">
      <h2>error</h2>
      <span data-bind="text: $root.ErrorText"></span>
   </div>
</div>

<script src="/_layouts/GLA/js/jquery-1.8.1.min.js" type="text/javascript"></script>
<script src="/_layouts/GLA/js/knockout-3.1.0.js" type="text/javascript"></script>
<script src="/_layouts/GLA/js/camljs.js" type="text/javascript"></script>
<script src="/_layouts/GLA/js/users.js" type="text/javascript"></script>
var ReservationStatus = { Full: 0, None: 1, Register: 2, Unregister: 3, Error: 5 };

function Session(itemId, date, location, participants) {
    this.itemId = ko.observable(itemId);
    this.date = ko.observable(date);
    this.location = ko.observable(location);
    this.participants = participants;
    this.text = ko.computed(function () {
        return this.date().toLocaleString() + " u00e0 " + this.location();
    }, this);
}

function SessionsViewModel() {
    var self = this;
    self.Reservation = ko.observable(new Session("", "", "", ""));
    self.SelectedSession = ko.observable();
    self.Status = ko.observable();
    self.ErrorText = ko.observable();
    self.CurrentUserLogin = ko.observable();
    self.Sessions = ko.observableArray([]);

    self.Register = function () {
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('calendrier');
        this.oListItem = oList.getItemById(ko.toJS(self.SelectedSession()).itemId);
        var users = new Array();
        var usersFromSP = ko.toJS(self.SelectedSession()).participants;
        for (index = 0, max = usersFromSP.length; index < max; index++) {
            users.push(SP.FieldUserValue.fromUser(usersFromSP[index].$b_1));
        }

        var userToAdd = SP.FieldUserValue.fromUser(self.CurrentUserLogin());
        users.push(userToAdd);
        this.oListItem.set_item('Participant', users);
        this.oListItem.update();
        clientContext.executeQueryAsync(
			Function.createDelegate(this, function () {
			    SP.UI.Notify.addNotification("Inscription effectuu00e9e.", true);
			    setTimeout(function () { window.location.href = _spPageContextInfo.webServerRelativeUrl }, 500);
			}),
			Function.createDelegate(this, function (sender, args) {
			    self.ErrorText('Request failed. ' + args.get_message() + '<br/>' + args.get_stackTrace());
			    self.Status(ReservationStatus.Error);
			})
		);
    }

    self.Unregister = function () {
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('calendrier');
        this.oListItem = oList.getItemById(ko.toJS(self.Reservation()).itemId);
        var users = new Array();
        var usersFromSP = ko.toJS(self.Reservation()).participants;
        for (index = 0, max = usersFromSP.length; index < max; index++) {
            if (usersFromSP[index].$b_1 != self.CurrentUserLogin()) {
                users.push(SP.FieldUserValue.fromUser(usersFromSP[index].$b_1));
            }
        }

        this.oListItem.set_item('Participant', users);
        this.oListItem.update();
        clientContext.executeQueryAsync(
			Function.createDelegate(this, function () {
			    SP.UI.Notify.addNotification("Du00e9sinscription effectuu00e9e.", true);
			    setTimeout(function () { window.location.href = _spPageContextInfo.webServerRelativeUrl }, 500);
			}),
			Function.createDelegate(this, function (sender, args) {
			    self.ErrorText('Request failed. ' + args.get_message() + '<br/>' + args.get_stackTrace());
			    self.Status(ReservationStatus.Error);
			})
		);
    }

    self.GetCurrentUser = function () {
        var context = new SP.ClientContext.get_current();
        this.currentUser = context.get_web().get_currentUser();
        context.load(this.currentUser);
        context.executeQueryAsync(
			Function.createDelegate(this, function () {
			    self.CurrentUserLogin(this.currentUser.get_title());
			}),
			Function.createDelegate(this, function (sender, args) {
			    self.ErrorText('Request failed. ' + args.get_message() + '<br/>' + args.get_stackTrace());
			    self.Status(ReservationStatus.Error);
			})
		);
    }

    self.LoadAvailableSessions = function () {
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('calendrier');
        var camlQuery = new SP.CamlQuery();
        this.collListItem = oList.getItems(camlQuery);
        clientContext.load(this.collListItem, 'Include(Id, Title, Location,EventDate, Participant, MaxParticipant)');
        clientContext.executeQueryAsync(
			Function.createDelegate(this, function () {
			    var listlength = this.collListItem.get_count();

			    if (listlength == 0) {
			        self.Status(ReservationStatus.None);
			    }
			    else {
			        var listItemEnumerator = this.collListItem.getEnumerator();
			        while (listItemEnumerator.moveNext()) {
			            var oListItem = listItemEnumerator.get_current();
			            if (oListItem.get_item('Participant') == null) {
			                self.Sessions.push(new Session(oListItem.get_id(), oListItem.get_item('EventDate'), oListItem.get_item('Location'), new Array()));
			            }
			            else {
			                var currentAttendees = oListItem.get_item('Participant').length;
			                var maxAttendee = oListItem.get_item('MaxParticipant');
			                if (currentAttendees < maxAttendee) {
			                    self.Sessions.push(new Session(oListItem.get_id(), oListItem.get_item('EventDate'), oListItem.get_item('Location'), oListItem.get_item('Participant')));
			                }
			            }
			        }

			        if (self.Sessions().length > 0) {
			            self.Status(ReservationStatus.Register);
			        }
			        else {
			            self.Status(ReservationStatus.Full);
			        }
			    }
			}),
			Function.createDelegate(this, function (sender, args) {
			    self.ErrorText('Request failed. ' + args.get_message() + '<br/>' + args.get_stackTrace());
			    self.Status(ReservationStatus.Error);
			})
		);
    }

    self.IsRegistered = function () {
        var clientContext = SP.ClientContext.get_current();
        var oList = clientContext.get_web().get_lists().getByTitle('calendrier');
        var camlQuery = new SP.CamlQuery();
        var caml = new CamlBuilder().Where().UserField("Participant").EqualToCurrentUser().ToString();
        camlQuery.set_viewXml("<View><Query>" + caml + "</Query></View>");
        this.collListItem = oList.getItems(camlQuery);
        clientContext.load(this.collListItem, 'Include(Id, Title, Location,EventDate, DisplayName, Participant)');
        clientContext.executeQueryAsync(
			Function.createDelegate(this, function () {
			    var listlength = this.collListItem.get_count();

			    if (listlength == 0) {
			        self.Status(ReservationStatus.Register);
			        self.LoadAvailableSessions();
			    }
			    else {
			        var listItemEnumerator = this.collListItem.getEnumerator();
			        while (listItemEnumerator.moveNext()) {
			            var oListItem = listItemEnumerator.get_current();
			            self.Reservation(new Session(oListItem.get_id(), oListItem.get_item('EventDate'), oListItem.get_item('Location'), oListItem.get_item('Participant')));
			        }

			        self.Status(ReservationStatus.Unregister);
			    }
			}),
			Function.createDelegate(this, function (sender, args) {
			    self.ErrorText('Request failed. ' + args.get_message() + '<br/>' + args.get_stackTrace());
			    self.Status(ReservationStatus.Error);
			})
		);
    }
}

$(function () {
    ExecuteOrDelayUntilScriptLoaded(function () {
        ExecuteOrDelayUntilScriptLoaded(function () {
            var VM = new SessionsViewModel();
            VM.IsRegistered();
            VM.GetCurrentUser();
            ko.applyBindings(VM);
        }, "sp.js");
    }, "core.js");
});