Hi everybody!

This is my first time writing content for SCN after years of learning from the gurus of SAP Community. And how amazing it is, especially when you have the opportunity to do so combining it with a subject that (almost) all Brazilians love: Soccer! (or Football – I believe you got the idea 🙂 ).

I remember the last 2010 World Cup not only because of Sjneider and that terrible game where Brazil lost to Netherlands in quarter finals 🙁 , but at that time I managed to develop a Web Dynpro ABAP where my friends and I could enter our bets for the World Cup’s Group phase and the better with more number of hits would earn some money.My prove that I didn’t play any trick on them is that I was the worst ranked at the end!

Four years have passed and with all these World Cup talk going on I remembered how cool doing that Bet Manager was, so I decided to develop an App related to World Cup before the grand 2014 World Cup opening on June 12th, 2014 in São Paulo. I knew I wouldn’t be able to develop a complete Bet Manager, but I knew I could do something almost similar. SAP’s UI strategy has really changed after four years, so I really needed to try a different UI approach. That’s where SAPUI5 comes in!

In the very beginning I didn’t like SAPUI5, and more specifically the idea that I previously had that Views could be only created using JavaScript – don’t ask me why, maybe because the API Documentation always refers to the JavaScript entities of SAPUI5. This idea completely changed after reading – and doing – the wonderful tutorial that DJ Adams et all have put together in Building SAP Fiori-like UIs with SAPUI5. I really liked the separation of concerns that XML Views provides, although I personally believe that HTML would be the ideal place for presentation as we see in JQuery Mobile (HTML Views in SAPUI5 is in my To-Do list). But this blog was inspiration enough to get me into SAPUI5.


Here are some screenshots of what the App looks like:


Desktop viewport:

Capturar0.PNGCapturar2.PNG

Mobile viewport:

Capturar11a.PNGCapturar1a.PNGCapturar2a.PNGCapturar3a.PNG


My boilerplate was basically the resulting App from the tutorial mentioned above, including the awesome technique of propagating the sap.m.SplitApp container throughout the Views of the App. But first off, I needed some resources to get this App started:


  • Flags: The circle-like flags from here are elegant and are available to use for non-commercial purposes. An special mention to the blog SAPUI5 and Responsive Web Design (RWD) from Morgan Apelqvist that helped me adjust the flag sizes according to the viewport;
  • Fonts(optional): I have set Samba as the App default font for a while. I thought it wasn’t as readable as I wanted, so I’ve put it aside;
  • 2014 World Cup logo: The first Google hit for “2014 World Cup logo” 😉 ;
  • Data Models: Despite all possible on-line resources, I ended up using this JSON model as a base for development, which I could easily change locally. It is made of 5 JSON files: group.json, match.json, team.json, team_bracket.json (not used in this App) and venue.json. That’s a sample of each of them:


group.json:


{
  "group_collection":
  [
  {
  "id": 1,
  "name": "A"
  },
  ...//  7 more groups
  ]
}












team.json:


{
  "team_collection":
  [
  {
  "id": 1,
  "group_id": 1,
  "code": "BRA",
  "name": "Brazil", "wins":5
  },
  ... // 31 more teams
  ]
}












match.json:


{
  "match_collection":
  [
  {
  "id": 1,
  "stage": 0,
  "venue_id": 12,
  "team1_id": 1,
  "team2_id": 2,
  "kickoff": "2014-06-12 17:00:00"
  },
  ... // 63 Matches, but only 48 happens in the Groups' phase
  // "stage": 0 - indicates that it is a classification match
  ]
}











venue.json:


{
  "venue_collection":
  [
  {
  "id": 1,
  "name": "Estadio Mineirao",
  "city": "Belo Horizonte"
  },
  ... //11 more venues
  ]
}











It was quite tricky to work with the JSON model above. The examples I had seen so far (including the tutorial above) relied on a single flattened Model, where you can navigate top-down using the facilities that Contexts provide. In my case I had 4 different JSON models and I wasn’t willing to flatten them. I couldn’t find a way to link them using Binding properties inside the View, so I’ve come up with a solution where the models which provide descriptions (venue.json and team.json) were loaded when the Component is created and assigned to the Formatter class sap.ui.demo.worldCup2014.util.TeamFormatter as JavaScript objects:


Component.js:


jQuery.sap.require("sap.ui.demo.worldCup2014.util.TeamFormatter");
jQuery.sap.declare("sap.ui.demo.worldCup2014.Component");
sap.ui.core.UIComponent.extend("sap.ui.demo.worldCup2014.Component", {
  createContent : function() {
  // create root view
  var oView = sap.ui.view({
  id : "app",
  viewName : "sap.ui.demo.worldCup2014.view.App",
  type : "JS",
  viewData : {
  component : this
  }
  });
  // set i18n model
  var i18nModel = new sap.ui.model.resource.ResourceModel({
  bundleUrl : "i18n/messageBundle.properties"
  });
  oView.setModel(i18nModel, "i18n");
  // Loading TeamFormatter with JSON Models:
  jQuery.ajax({
  url : "./model/team.json",
  dataType : "json"
  }).done(function(data) {
  sap.ui.demo.worldCup2014.util.TeamFormatter.teams = data;
  });
  jQuery.ajax({
  url : "./model/venue.json",
  dataType : "json"
  }).done(function(data) {
  sap.ui.demo.worldCup2014.util.TeamFormatter.venues = data;
  });
  ...
  // Using a local model for offline development
  var oMatch = new sap.ui.model.json.JSONModel("model/match.json");
  oView.setModel(oMatch, "matches");
  var oGroup = new sap.ui.model.json.JSONModel("model/group.json");
  oView.setModel(oGroup, "groups");
  var oTeam = new sap.ui.model.json.JSONModel("model/team.json");
  oTeam.setDefaultBindingMode("OneWay");
  oView.setModel(oTeam, "teams");
  ...
  return oView;
  }
});











TeamFormatter.js:


jQuery.sap.declare("sap.ui.demo.worldCup2014.util.TeamFormatter");
sap.ui.demo.worldCup2014.util.TeamFormatter = {
  teamName : function(value) {
  return sap.ui.demo.worldCup2014.util.TeamFormatter.teams.team_collection
  .filter(function(item) {
  return item.id === value;
  })[0].name;
  },
  venueName : function(value) {
  return sap.ui.demo.worldCup2014.util.TeamFormatter.venues.venue_collection
  .filter(function(item) {
  return item.id === value;
  })[0].name;
  },
  venueCity : function(value) {
  return sap.ui.demo.worldCup2014.util.TeamFormatter.venues.venue_collection
  .filter(function(item) {
  return item.id === value;
  })[0].city;
  },
};











This way I could use the TeamFormatter class above in order to get the name of a team, as detailed below in the XML View Matches:


Matches.view.xml:


<core:View
  xmlns:core="sap.ui.core"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  controllerName="sap.ui.demo.worldCup2014.view.Matches"
  xmlns:html="http://www.w3.org/1999/xhtml">
  <Page
  title="{i18n>MatchTableTitle} {path: 'navigation>/team',
   formatter: 'sap.ui.demo.worldCup2014.util.TeamFormatter.teamName'}"
  showNavButton="true"
  navButtonPress="handleNavButtonPress">
  <Table
  items="{path: 'matches>/match_collection',
  sorter: [{path: 'kickoff', descending: false}]}"
  id="matches">
  <columns>
  <Column>
  <header>
  <Label
  class="wc2014"
  text="{i18n>MatchTableColumnHT}" />
  </header>
  </Column>
  <Column>
  <header>
  <Label
  class="wc2014"
  text="{i18n>MatchTableColumnVT}" />
  </header>
  </Column>
  <Column
  minScreenWidth="Tablet"
  demandPopin="true"
  hAlign="Center">
  <header>
  <Label
  class="wc2014"
  text="{i18n>MatchTableColumnDT}" />
  </header>
  </Column>
  <Column
  minScreenWidth="Tablet"
  demandPopin="true"
  hAlign="Center">
  <header>
  <Label
  class="wc2014"
  text="{i18n>MatchTableColumnVN}" />
  </header>
  </Column>
  <Column
  minScreenWidth="Tablet"
  demandPopin="true"
  hAlign="Center">
  <header>
  <Label
  class="wc2014"
  text="{i18n>MatchTableColumnVC}" />
  </header>
  </Column>
  </columns>
  <ColumnListItem type="{sap.m.ListType.Detail}">
  <cells>
  <ObjectIdentifier
  title="{path: 'matches>team1_id',
   formatter: 'sap.ui.demo.worldCup2014.util.TeamFormatter.teamName'}"
  class="wc2014" />
  <ObjectIdentifier
  title="{path: 'matches>team2_id',
   formatter: 'sap.ui.demo.worldCup2014.util.TeamFormatter.teamName'}"
  class="wc2014" />
  <Text
  text="{matches>kickoff}"
  class="wc2014" />
  <Text
  text="{
  path:'matches>venue_id',
  formatter:'sap.ui.demo.worldCup2014.util.TeamFormatter.venueName'
   }"
  class="wc2014" />
  <Text
  text="{
  path:'matches>venue_id',
  formatter:'sap.ui.demo.worldCup2014.util.TeamFormatter.venueCity'
   }"
  class="wc2014" />
  </cells>
  </ColumnListItem>
  </Table>
  </Page>
</core:View>









In order to navigate between models I created CustomDatas inside of sap.m.List / sap.m.Table items so I could store navigation id’s and retrieve them inside their respective press events. Notice that sap.ui.model.Filter objects are created so the items binding of the sap.m.Table object could be properly filtered according to the Group id:

Groups.view.xml:


<core:View
  xmlns:core="sap.ui.core"
  xmlns="sap.m"
  controllerName="sap.ui.demo.worldCup2014.view.Groups"
  xmlns:app="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"
  xmlns:html="http://www.w3.org/1999/xhtml">
  <Page title="{i18n>GroupsPageTitle}">
  <List items="{groups>/group_collection}">
  <StandardListItem
  class="wc2014"
  type="Active"
  app:groupId="{groups>id}"
  title="{i18n>GroupsListTitle} {groups>name}"
  press="handleListItemPress"/>
  </List>
  </Page>
</core:View>










Groups.controller.js:


sap.ui.controller("sap.ui.demo.worldCup2014.view.Groups", {
  handleListItemPress : function(evt) {
  // Retrieve Custom Data:
  var itemPressed = evt.getSource();
  var groupId = itemPressed.data("groupId");
  // Pass Group description to Navigation model:
  var oNavigation = this.getView().getModel("navigation");
  oNavigation.setProperty("/group", itemPressed.getTitle());
  // Filter Team model using the Group id:
  var filtersTeam = [];
  filtersTeam.push(new sap.ui.model.Filter("group_id",
  sap.ui.model.FilterOperator.EQ, groupId));
  var filterObj = {
  id : "teams",
  binding : "items",
  filter : filtersTeam
  };
  this.nav.to("Teams", undefined, filterObj);
  },
  ...
});














I have just pointed out here the details that I found important, but the complete version of the App can be found in https://github.com/edersouza38/WorldCup2014-SAPUI5. Please check it out and leave your comments!

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. L. van Hengel

    Hi Eder,

    Great first blog with SAPUI5 and XMLViews!

    Thanks for sharing the code on github and supporting the Netherlands in 2010. Will you support them this year as well?

    Cheers,

    Leo

    (0) 
    1. Eder Torres de Souza Post author

      Hi Leo, I am really glad you’ve liked it!

      However, please don’t expect me to support Netherlands this year: I have constant nightmares where Sjneider appears running through the field tapping his own head. 🙂

      Cheers!

      (0) 
    1. Eder Torres de Souza Post author

      Hi Gustavo! Good to hear from you!

      I’m glad you enjoyed the post, hope we can share SAPUI5 experiences!

      Best wishes for your family out there in Canada!

      Abraços,

      Éder

      (0) 

Leave a Reply