מדריך: בניית אפליקציית מטרו לניוזגיק עבור Windows 8 עם JavaScript ו-HTML5

שלב אחר שלב בבניית אפליקציית מטרו לניוזגיק עבור Windows 8 מבוססת JavaScript ו-HTML5.

אפליקציית ניוזגיק בממשק מטרו עבור חלונות 8

במדריך זה נראה צעד אחרי צעד כיצד לבנות אפליקציית מטרו ל-Windows 8 באמצעות JavaScript ו-HTML5. האפליקציה מציגה את הכתבות האחרונות מניוזגיק בחלוקה לקטגוריות השונות.

המדריך מבוסס על גירסת ה- Consumer Preview של Windows 8 ועל גירסת ה- Beta של Visual Studio 11, כך שיתכנו שינויים בתבניות, ב- API’s ובכלי הפיתוח ביום בו תעשו שימוש במדריך הזה.

מה צריך להכין לפני?

יצירת פרוייקט חדש מסוג אפליקציית מטרו

נפעיל את Visual Studio 11 Beta, ובמסך הפתיחה ניצור אפליקציה חדשה ע”י לחיצה על New Project.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

בחלון שנפתח, נבחר בתבנית בשפת JavaScript מסוג Grid Application, ניתן לה שם (לדוגמא: RSSReader), ונלחץ OK.

תבנית ה- Grid היא התבנית הפופולריות ביותר לאפליקציות מטרו ל- Windows 8, ומתאימה במיוחד לאפליקציות קריאה.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

Visual Studio יצור את פרוייקט חדש עבור האפליקציה, ויכין את סביבת העבודה להמשך הפיתוח.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

תבנית האפליקציה הבסיסית

לפני שנכיר את מבנה הפרוייקט והקבצים שנכללים באפליקציה, פשוט נריץ אותה ע”י לחיצה על F5 או על הכפתור עליו כתוב Local Machine. בכך, Visual Studio יארוז את האפליקציה, יתקין אותה על המחשב המקומי ויריץ אותה.

ניתן לראות שהאפליקציה מכילה כותרת ראשית (RSSReader), וכן  Grid בסיסי עם נתונים סטטיים המחולקים לפי קטגוריות. ניתן לגלול לצד ימין כדי לראות את שאר הנתונים בקטגוריות הנוספות.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

בלחיצה על כותרת של קטגוריה מסויימת, נגיע לעמוד קטגוריה, המכיל תמונה ראשית של הקטגוריה וכן רשימת פריטים. בפינה השמאלית העליונה מופיע כפתור הניווט המאפשר לנו לחזור אחורה למסך הראשי.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

בלחיצה על כל פריט, נגיע לעמוד הפריט, המציג את המידע המלא עליו. במקרה הזה – הטקסט של הכתבה.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

אחד הדברים היפים בתבנית הבסיסית של האפליקציה הוא שהיא מכילה תמיכה במצבי תצוגה שונים באופן אוטומטי. למשל בעת הטיית המכשיר ב- 90 מעלות, האפליקציה תתאים עצמה לתצוגה אנכית.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

הכירות עם מבנה הפרוייקט והקבצים באפליקציה

נסתכל על מבנה הקבצים של אפליקציית מטרו בסיסית של Windows 8 הפנויה ב-JavaScript ו-HTML5. כצפוי, ניתן לראות את התיקיות html, css ו- js המכילות את הדפים השונים באפליקציה, כללי העיצוב שלהם והלוגיקה. עוד ניתן למצוא את התיקייה Images שמכילה מספר תמונות שבשימוש באפליקציה.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

הקובץ package.appxmanifest מכיל מספר מאפיינים של האפליקציה שחנות האפליקציות וכן מערכת ההפעלה צריכים לדעת לפני הרצת האפליקציה. בין הדברים המוגדרים שם הם מצבי התצוגה השונים שהאפליקציה תומכת בהם, הרשאות גישה למשאבי ה- Device כגון מצלמה,  GPS ועוד.

הקובץ RSSReader_TemporaryKey.pfx הוא קובץ החתימה הדיגיטלית של האפליקציה וישמש אותנו כאשר נעלה את האפליקציה לחנות באחד מהפרקים הבאים במדריך.

נסתכל בקובץ default.html שהוא הדף הראשון שנפתח בעת הפעלת האפליקציה:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>RSSReader</title>

  <!-- WinJS references -->
  <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
  <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
  <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

  <!-- RSSReader references -->
  <link href="/css/default.css" rel="stylesheet">
  <script src="/js/data.js"></script>
  <script src="/js/navigator.js"></script>
  <script src="/js/default.js"></script>
</head>
<body>
  <div id="contenthost"
       data-win-control="RSSReader.PageControlNavigator"
       data-win-options="{home: '/html/groupedItemsPage.html'}">
  </div>
  <!-- <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand"
         data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}">
        </button>
      </div> -->
</body>
</html>

ראשית, ניתן לראות שזהו קובץ HTML5 סטנדרטי עם DOCTYPE סטנדרטי של HTML5, ומבנה טיפוסי של דף HTML הכולל איזור head ו- body.

באיזור ה- head ניתן לראות references לספריית WinJS. ספרייה זו היא ספריית התשתית לפיתוח אפליקציות מטרו ב- HTML5 ו- JavaScript וכוללת שלל פקדים ב- look and feel של Windows 8, ספריית אנימציות, רכיבי תשתית כגון Templating, Data Binding ועוד.

<!-- WinJS references -->
<link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
<script src="//Microsoft.WinJS.0.6/js/base.js"></script>
<script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

באיזור ה- body ניתן לראות את האלמנט:

<div id="contenthost"
      data-win-control="RSSReader.PageControlNavigator"
      data-win-options="{home: '/html/groupedItemsPage.html'}">
</div>

בתוך אלמנט זה ישולבו הדפים השונים באפליקציה, כיוון שמדובר ב- Single Page Application. ניתן לראות שימוש במאפייני ה- *-data החדשים ב- HTML5 כדי להוסיף מידע, בין היתר את הנתיד לדף הראשון שיוצג בעת הפעלת האפליקציה.

הבאת הנתונים לאפליקציה

אם נתבונן בקובץ data.js שבתיקיית קבצי ה- JavaScript של האפליקציה, נראה את שהמידע בתבנית הבסיסית הוא hard-coded. נרצה לשנות את הקובץ הזה כך שיביא נתוני אמת שיוצגו באפליקציה.

הנתונים שנביא יהיו הכתבות האחרונות מאתר ניוזגיק. נשתמש ב- RSS Feed של האתר כדי למשוך ממנו את תוכן הכתבות האחרונות, אך במקום לקרוא את המידע כ- RSS Feed ולעבוד עם XMLים, נשתמש בשירות הממיר RSS Feed ל- json. בסופו של דבר נשתמש בקישור הבא כדי לקבל את התוכן מהאתר בפורמט json:

http://www.blastcasta.com/feed-to-json.aspx?feedurl=http://feeds2.feedburner.com/geektimefeed

כעת, נמחוק את כל התוכן של הקובץ data.js ונבנה אותו מחדש, שלב אחר שלב.

ראשית, כל מודול באפליקציית מטרו ל- Windows 8 הכתובה ב- HTML5 ו- JavaScript יהיה תחום ב- Immediate Function:

// data.js
(function () {

  // Code comes here

})();

הדבר הראשון שנרצה להוסיף לתוכן הפונקציה הזאת, הוא ההצהרה use sctrict, ע”מ להכריח את מנוע הדפדפן להיות נוקשה יותר עם קוד ה- JavaScript כדי להפחית שגיאות שלרוב נובעות מהגמישות שבשפה. לפרטים נוספים, ניתן לקרוא עוד בפוסט JavaScript Strict Mode.

// data.js
(function () {
  "use strict"

  // Strict code comes here

})();

נגדיר משתנה בשם title מסוג WinJS.Binding.List. אובייקט זה שייך לתשתית ה- data binding של WinJS, ומאפשר לנו לקשר בין אוסף נתונים לבין רכיבי תצוגה שיציגו אותם. האובייקט במספר תכונות שימושיות כמו מיון, חלוקות לקטגוריות וכו’ שאנחנו צריכים באפליקציה.

// data.js
(function () {
  "use strict"

  var titles = new WinJS.Binding.List();

})();

כעת נרצה לגשת לאינטרנט ולהביא מידע. לצורך כך, נשתמש בקריאה WinJS.xhr המבצע קריאת XMLHttpRequest לכתובת אותה אנחנו מעבירים כפרמטר. למה בחרתי להשתמש באובייקט הזה ולא לעשות שימוש באובייקט הסטנדרטי של XmlHttpRequest? יש לכך 2 תשובות. הראשונה – הסינטקס הפשוט – שימוש בפקודה אחת לעומת מספר פקודות בעת שימוש בסיסי ב- XMLHttpRequest.

הסיבה השניה, היא שהקריאה ל-WinJS.xhr מחזירה Promise, המאפשרת לי עבודה קלה בעת כתיבת קוד אסינכרוני ב- JavaScript. מומלץ לקרוא פוסט שכתבתי בזמנו על תכנות אסינכרוני ב- JavaScript עם Promises.

// data.js
(function () {
  "use strict"

  var titles = new WinJS.Binding.List();

  var url = "http://www.blastcasta.com/feed-to-json.aspx?            feedurl=http://feeds2.feedburner.com/geektimefeed";
  WinJS.xhr({ url: url }).then(function (xhr) { });

})();

עם קבלת הנתונים מהרשת, נרצה להוציא מה- Feed את הנתונים הנחוצים לנו לאפליקציה, כגון כותרת הפוסט, הקטגוריה, התמונה המובילה וכן הקישור לפוסט המקורי. את הנתונים האלה  נרצה להוסיף לרשימת ה- Binding שיצרנו.

WinJS.xhr({ url: url }).then(function (xhr) {
  var response = JSON.parse(xhr.responseText);
  var items = response.rss.channel[0].item;
  var div = document.createElement('div');

  for (var i = 0, len = items.length; i < len; i++) {
    var title = items[i];

    var content = title['content:encoded'];
    div.innerHTML = content;
    var images = div.querySelectorAll('img');

    titles.push({
      category: title.category[0],
      name: title.title,
      image: images[0].href,
      content: content
    });
  };

כעת, נוסיף לקובץ הזה 2 פונקציות עזר, שעוזרות לתשתית לחלק את הפוסטים לפי קטגוריית הפוסט. יש למקם את הפונקציות האלה מתחת לקריאה ל- WinJS.xhr:

WinJS.xhr({ url: url }).then(function (xhr) {
  ...
});

function getTitlesFromCategory(category) { return titles.createFiltered(             function (title) { return title.category === category; }); } var titlesByCategory = titles.createGrouped(         function (title) { return title.category; },         function (title) { return title.category; });

כיוון שהשתמשנו ב- Immediate Function כדי לעטוף את כל הפונקציונאליות בקובץ data.js, הפונקציות הנ”ל אינן חשופות ל- Global Namespce ואינן ניתנות לשימוש במקומות אחרים באפליקציה. כדי ליצור מעין איזור public, שמגדיר פונקציות שניתן להשתמש בהן מחוץ לקובץ הזה, נגדיר namespace חדש בשם data, מתחת להגדרת הפונקציות האלה (אך עדיין בלוק ה- immediate function):

(function () {
  "use strict";

  ...

  WinJS.xhr({ url: url }).then(function (xhr) {
    ...  });

  var titlesByCategory = ...
  WinJS.Namespace.define("data", { items: titlesByCategory, groups: titles.groups, getItemsFromGroup: getTitlesFromCategory });
})();

כעת, מכל מקום באפליקציה נוכל לקרוא לפונקציות items, groupd ו- getItemsGromGroup בתוך ה- namespace ששמו data.

חיבור הנתונים לתצוגה

תבנית האפליקציה הבסיסית בתצוגת Grid משתמשת ב- Data Binding כדי לחבר בין הנתונים שהבאנו מהאינטרנט לבין תצוגת ה- Grid במסך. עוד ראינו את הפונקציות בהן משתמשת האפליקציה כדי לחלק את הפוסטים השונים לפי הקטגוריות.

כעת, עלינו לבצע מספר שינויים כדי להתאים את מבנה הנתונים שלנו לתצוגה ולחלוקה לקטגוריות. נפתח את הקובץ groupedItemsPage.html בתיקיית קבצי ה- html של האפליקציה, ונבצע בו מספר שינויים:

1. נחפש את הביטוי title: item.group.title ונחליף אותו לביטוי title: item.category

2. נחליף את הביטוי { group: item.group } לביטוי { group: item.category }

3. נחליף את הביטוי return item.group.key לביטוי return item.category.

אם נריץ את האפליקציה כעת, נראה את הכתבות האחרונות באתר מסודרות בגריד הסטנדרטי בחלוקה לקטגוריות, אולם הנתונים אינם מוצגים כהלכה.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

הסיבה לכך היא שעדיין לא שינינו את ביטויי ה- Data Binding המקשרים בין המידע שהבאנו לבין רכיבי התצוגה כגון התמונה, הכותרות וכו’. כדי להשלים זאת, נפתח את הקובץ groupedItemsPage.html שאחראי לתצוגת הדף הזה.

<!DOCTYPE html>
<html>
<head>
    ...</head>
<body>
     <div class="headerTemplate" data-win-control="WinJS.Binding.Template">
        <h2 class="group-title"
           data-win-bind="onclick: click; textContent: title" role="link"></h2>
    </div>
    <div class="itemtemplate" data-win-control="WinJS.Binding.Template">
        <img class="item-image" src="#"
              data-win-bind="src: backgroundImage; alt: title" />
        <div class="item-overlay">
            <h4 class="item-title" data-win-bind="textContent: title"></h4>
            <h6 class="item-subtitle win-type-ellipsis"
                data-win-bind="textContent: subtitle"></h6>
        </div>
    </div>

    <!-- The content that will be loaded and displayed. -->
    <div class="fragment groupeditemspage">
        <header aria-label="Header content" role="banner">
        </header>
        <section aria-label="Main content" role="main">
        </section>
    </div>
</body>
</html>

איזור ה- body של העמוד הזה מחולק ל- 3 אלמנטי div. האחרון ביניהם, המכיל הגדרת class בשם fragment הוא התוכן של הדף, וישולב בתוך אלמנט ה- contenthost שראינו מוקדם יותר במדריך כשסקרנו את התוכן של default.html.

2 אלמנטי ה- div הנוספים, הם Templates לתצוגה של האלמנטים המוצגים בעמוד זה. ניתן לראות שלשניהם ישנה הבאה המציינת לספריית WinJS שאין להציג את האלמנטים האלה, והם משמשים בתוך Templates בלבד.

data-win-control="WinJS.Binding.Template"

בתוך ה- Templates, ניתן לראות את החיבור בין מאפיינים של אלמנטי DOM לבין שדות של אובייקטי מידע באפליקציה. למשל, הביטוי הבא מקשר בין התכונה src של אלמנט ה- image לשדה backgroundImage של האובייקט, ומקשר בין התכונה alt למאפיין title.

<img class="item-image" src="#"
      data-win-bind="src: backgroundImage; alt: title" />

נערוך את ביטויי ה- Data Binding הנ”ל כדי להתאים למבנה הנתונים שאיתו אנחנו עובדים באפליקציה הנוכחית:

1. נחליף את הביטוי src: backgroundImage; alt: title לביטוי src: image; alt: name.

2. נחליף את הביטוי textContent: title לביטוי textContent: name.

3. נמחוק את כל אלמנט ה- H6 בתוך ה- itemtemplate.

בנוסף, רק בשביל הכיף, נשנה את הביטוי <span class="pagetitle">RSSReader</span>

לשם האפליקציה שלנו: <span class="pagetitle">Newsgeek</span>

לאחר השינויים האלה, תיראה האפליקציה כך:

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

וקיבלנו אפליקציית ניוזגיק עם עמוד ראשי עובד.

עמודים משניים באפליקציה

עד כאן כיסינו מרכיבים מרכזיים שכדאי להכיר בעת בניית אפליקצי מטרו בסיסית ב- JavaScript ל- Windows 8. כדי לגרום לאפליקציה לעבוד במלואה, עלינו לטפל גם בעמודים המשניים – עמוד הקטגוריה ועמוד הכתבה הבודדת.

עבור עמוד הכתבה הבודדת, ניכנס לערוך את הקוד בקובץ: itemDetailPage.js. נאתר את איזור הקוד בו מציבים את הערכים של הכתבה במקומות הנכונים, ונשנה אותו להיות הקוד הבא:

ready: function (element, options) {
  var item = options && options.item ? options.item : data.items.getAt(0);
  element.querySelector(".titlearea .pagetitle").textContent = item.category;
  element.querySelector("article .item-title").textContent = item.name;
  element.querySelector("article .item-subtitle").textContent = item.name;
  element.querySelector("article .item-image").src = item.image;
  element.querySelector("article .item-image").alt = item.name;

  var staticHTML = window.toStaticHTML(item.content);
  WinJS.Utilities.setInnerHTMLUnsafe(
            element.querySelector("article .item-content"),
            item.content);
}

הקוד הנ”ל די סטנדרטי – הוא מגיע לאלמנטים השונים בדף ושם בהם את הערך המתאים מבין נתוני הדף.

החלק המעניין הוא דווקא בסוף, בו אנחנו משתמשים בפונקציות window.toStaticHTML ו- WinJS.Utilities.setInnerHTMLUnsafe כדי להציב את תוכן הכתבה באלמנט המתאים. הסיבה לכך היא שתוכן הכתבה יכול להכין HTML שאינו בטוח או מתאים לאפליקציות מטרו (למשל מכיל סרטון מ- YouTube או סקריפט כלשהו. הפונקציות האלה מנקה את הקוד מקטעים לא בטוחים אך משאירות את התוכן כ- HTML.

אם נריץ כעת את האפליקציה, ונכנס לעמוד כתבה, נראה שהיא נראית כך:
אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

ניתן לראות את הקטגוריה, הכותרת התמונה הראשית ואת טקסט הכתבה. ניתן לראות שהתמונה הראשית של הכתבה מופיעה פעמיים בגוף הכתבה, דבר שניתן לפתור בקלות ע”י שינוי קטן ב- HTML.

הדבר האחרון שנרצה לעשות לסיום, הוא לשנות את כיוון האפליקציה לעברית. את זאת נעשה בקלות ע”י פתיחת מאפייני האפליקציה באמצעות בחירת פרוייקט האפליקציה (RSSReader), לחיצה על הכפתור הימני ובחירה באפשרות Properties.

image

במסך זה, פשוט נבחר את שפת האפליקציה להיות בעברית he-IL.

אפליקציית מטרו ל- Windows 8 באמצעות JavaScript ו- HTML5

סיכום

במדריך זה בנינו אפליקציית מטרו בסיסית ל- Windows 8 ע”י שימוש ב- HTML5 ו- JavaScript. התבססנו על תבנית ה- Grid של Visual Studio וע”י מספר התאמות חיברנו אותה למידע חי שמגיע מהאינטרנט.

בפרקים הבאים נצלול למחזור חיי האפליקציה ותכונות נוספות של Windows 8.

הפוסט פורסם לראשונה בבלוג של גיא בורשטיין.


בחסות מיקרוסופט


פוסט זה נכתב בחסות Windows 8 המביאה למפתחים ויזמים הזדמנות להגיע למיליוני משתמשי טאבלט ו- PC’s עם ממשק המשתמש החדש בסגנון "מטרו", וחנות האפליקציות החדשה של Windows עם מגוון מודלים עסקיים.

אנחנו ממליצים להוריד את Windows 8 Consumer Preview, ללמוד איך לעצב אפליקציות מטרו ל- Windows 8, ולהתחיל לפתח אפליקציות שיעלו לחנות האפליקציות בקרוב.

גיא בורשטיין

מומחה טכנולוגיות פיתוח במיקרוסופט ישראל, אחראי על הקשר עם קהילת הפתחים בארץ ובימים אלו מוביל את פעילות פיתוח האפליקציות ל- Windows 8

תגיות לכתבה:

להגיב

3 תגובות

  1. מאת פבל:

    מה זה querySelector ? למה לא להשתמש בספריה הסטנדרטית דה-פקטו בעולמנו? jQuery

    זה צריך להראות כך:

    $(".titlearea .pagetitle").textContent = item.category;
    $("article .item-title").textContent = item.name;
    $("article .item-subtitle").textContent = item.name;
    $("article .item-image").src = item.image;
    $("article .item-image").alt = item.name;

    נא לא להמציא את הגלגל. ואם אתם ממציאים ורוצים שמפתחים יגיעו לכיוונכם, הפרס צריך להיות משכנע יותר מזוג אופניים

  2. מאת גיא בורשטיין:

    פבל, תודה על התגובה.

    כדאי שתכיר, querySelectorAll היא פונקציה סטנדרטית של JavaScript לאיתור אלמנטי DOM, כחלק מ- Selectors API Level 1:
    http://www.w3.org/TR/selectors-api

    בברכה,
    גיא

  3. מאת מולי:

    מדוע התמונות חתוכות?
    למה אין תמונה שלמה של המסך?
    האם זו תכונה או תקלה?

הרשם לאתר

רק גיקים יכולים ליצור דיון חדש! כדי להפוך לגיק מדופלם, הרשם לאתר.