מדריך: בניית קורא RSS ל- Windows 8 עם HTML5

14 ביוני 2012

one comment
מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

עדכון 1.9.2012: המדריך מבוסס על הגירסה הסופית של Windows 8 ועל הגירסה הסופית של Visual Studio 2012.

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

  • כדי לפתח אפליקציות ל- Windows 8 צריך להוריד ולהתקין Windows 8. ניתן גם להתקין כ- Dual boot או כמכונה וירטואלית.
  • Visual Studio 2012 Express for Windows 8 – כלי הפיתוח החינמי לפיתוח אפליקציות מטרו ל- Windows 8.

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

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

הקובץ 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.1.0/css/ui-dark.css" rel="stylesheet" />
  <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
  <script src="//Microsoft.WinJS.1.0/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="Application.PageControlNavigator"
    data-win-options="{home: '/pages/groupedItems/groupedItems.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'}" 
        type="button"></button>
    </div> -->
</body>
</html>

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

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

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

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

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

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

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

בקובץ data.js שבתיקיית קבצי ה- JavaScript של האפליקציה, מוגדרת הלוגיקה של הבאת המידע לאפליקציה וקיבוצו לקטגוריות. אם נגלול מעט מטה, נגיע לפונקציה generateSampleData בה מוגדר המידע הסטטי של התבנית הבסיסית. מוגדרים משתנים כמו itemDescription ו- itemContent שמכילים תוכן סטטי של דפים, ולאחר מכן אנחנו רואים הגדרה של שני מערכי נתונים עבור ה- sampleGroups ועבור ה- sampleItems.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

באפליקציה שלנו נרצה לשנות הקוד בפונקציה הזאת כך שתביא נתוני אמת שיוצגו באפליקציה במקום התוכן ה- hard-coded המופיע כאן. הנתונים שנביא יהיו הכתבות האחרונות מאתר ניוזגיק. נשתמש ב- RSS Feed של האתר כדי למשוך ממנו את תוכן הכתבות האחרונות. כיוון שמדובר במידע החוזר מ- RSS Feed, נרצה להשתמש באובייקט בשם SyndicationClient, המפשט לנו מאד את פעולת הגישה לרשת והבאת נתוני RSS.

נמחק את כל הקוד הנוכחי של הפונקציה ובמקומו נכתוב את הקוד הבא:

function generateSampleData() {
  var items = [];

  var client = new Windows.Web.Syndication.SyndicationClient();
  client.bypassCacheOnRetrieve = true;

  var uri = new Windows.Foundation.Uri('http://www.newsgeek.co.il/feed/');
  return client.retrieveFeedAsync(uri).then(function (feed) {

  });
}

ראשית, אנחנו מגדירים מערך חדש בשם items שיחזיק את המידע שנקבל מה- RSS וזה יהיה ערך ההחזר של הפונקציה.

לאחר מכן, ניצור מופע חדש של Windows.Web.Syndication.SyndicationClient, אותו אובייקט שדרכו נבצע את קריאת נתוני ה- RSS. בשורה הבאה נגדיר כי נרצה תמיד לגשת לאינטרנט ולהביא נתונים חדשים במקום לגשת ל- cache המקומי.

נשים לב שהאובייקט SyndicationClient שייך ל- namespace ששמו Windows.Web.Syndication, ולפי העובדה שה- namespace מתחיל במילה Windows, אנחנו מבינים שזוהי מחלקה ששייכת ל- WinRT (אותה שכבת API’s ש- Windows 8 חושפת). ניתן לשים לב גם לשורה הבאה, בה אנחנו יוצרים אובייקט מסוג Windows.Foundation.Uri – גם אובייקט זה שייך ל- WinRT.

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

function generateSampleData() {
  var items = [];

  var client = new Windows.Web.Syndication.SyndicationClient();
  client.bypassCacheOnRetrieve = true;

  var uri = new Windows.Foundation.Uri('http://www.newsgeek.co.il/feed/');
  return client.retrieveFeedAsync(uri).then(function (feed) {

for (var i = 0, len = feed.items.size; i < len; i++) { var item = feed.items[i]; var content = window.toStaticHTML(item.summary.text); var div = document.createElement('div'); div.innerHTML = content; var images = div.querySelectorAll('img'); items.push({ title: item.title.text, backgroundImage: images[0].href, content: content, group: { key: item.categories[0].nodeValue, title: item.categories[0].nodeValue, backgroundImage: images[0].href, subtitle: item.title.text, } }); } return items; }); }

נבין את הקוד הנ”ל:

הערך החוזר מפונקציית ה- retrieveFeedAsync לתוך ה- Promise (מושג שנכיר מיד) הוא האובייקט feed המכיל שדה בשם itemsהמחזיק את כל הכתבות באתר.

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

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

Promises ותכנות אסינכרוני ב- JavaScript

נשים לב לאופן בו מבוצעת הקריאה לרשת להבאת הנתונים. אנחנו משתמשים בפונקציה ששמה retrieveFeedAsync. פונקציה זו, השייכת ל- WinRT היא פונקציה אסינכרונית, שלא מחזירה לי את הערך שלה, אלא מחזירה לי “הבטחה לערך שיחזור בעתיד” – דפוס שנקרא Promise ומפשט כתיבת קוד אסינכרוני ב- JavaScript. מומלץ לקרוא פוסט שכתבתי בזמנו על תכנות אסינכרוני ב- JavaScript עם Promises.

בעת שימוש ב- Promise, לא נקבל את ערך ההחזר של הפונקציה לתוך משתנה כמו בפונקציה סינכרונית רגילה:

var feed = client.retrieveFeed(...);

במקום זאת, נשרשר קריאה לפונקציה then עם פונקציית המשך שתקבל לתוכה את הערך שיחזור בעתיד מהפונקציה הראשונה:

client.retrieveFeedAsync(uri).then(function (feed) {

});

בצורה הזו, נקרא לפונקציה האסינכרונית retrieveFeedAsync, שיכולה להיארך מספר שניות, וכשזו תחזיר את ה- feed, יבוצע הקוד בתוך פונקציית ההמשך. במקרה שלנו – חילוץ המידע מתוך הכתבות ושמירתן במערך items.

עוד נשים לה, כי בפונקציה הנוכחית מופיעה פעמיים המילה return. פעם אחת לפני הבאת נתוני ה- RSS ופעם שניה כדי להחזיר את מערך ה- items:

function generateSampleData() {
  ...

  var uri = new Windows.Foundation.Uri('http://www.newsgeek.co.il/feed/');
  return client.retrieveFeedAsync(uri).then(function (feed) {
    for (var i = 0, len = feed.items.size; i < len; i++) {
      ...
    }
    return items;
  });
}

נסביר את השימוש ב- return לפני הקריאה ל- client.retrieveFeedAsync: כיוון שקיימת פעולה אסינכרונית בתוך הפונקציה שלנו, הפונקציה שלנו הופכת להיות פונקציה אסינכרונית גם היא. זאת אומרת שלא נוכל לקרוא לה באופן סינכרוני ולקבל ממנה את ערך ההחזר, אלא נהיה חייבים לשרשר אליה קריאה ל- then, ולקבל בעתיד את ערך ההחזר. לכן, אנחנו מחזירים את ה- Promise של הקריאה ל- client.retrieveFeedAsync ובהמשך מחזירים את ערך ההחזר – מערך ה- items.

טיפה מסובך ולא טריוייאלי, אני יודע. אבל מתרגלים לזה…

איפה הקוד שקורא לפונקציה generateSampleData? כיוון שהפכנו את הפונקציה generateSampleData להיות אסינכרונית, עלינו לשנות את האופן בו קוראים לה, ולהשתמש ב- then כדי לשרשר את קוד ההמשך.

אם נחזור לראש הקובץ (עדיין data.js), נראה שמוגדרת בו רשימה בשם list:

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

ולאחר מכן, מתבצעת הקריאה לפונקציה generateSampleData שאת ערכיה מכניסים לאותה רשימה:

generateSampleData().forEach(function (item) {
  list.push(item);
});

נשנה את הקריאה הזו להיות אסינכרונית:

generateSampleData().then(function (items) {
  items.forEach(function (item) {
    list.push(item);
  });
});

הקוד הנ”ל קורא ל- generateSampleData שכזכור, מחזירה Promise. אנחנו משרשרים קריאה ל- then עם קוד ההמשך, שמקבל את ערך ההחזר האמיתי של הפונקציה – מערך הפריטים items. עבור כל item, אנחנו מכניסים אותו לרשימה ששמה list שמשמשת אותנו ל- data binding לתצוגה.

מבנה מודול ושימוש ב- Namespaces

הקובץ 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

})();

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

(function () {
  "use strict";

  ...

  WinJS.Namespace.define("Data", {
    items: groupedItems,
    groups: groupedItems.groups,
    getItemReference: getItemReference,
    getItemsFromGroup: getItemsFromGroup,
    resolveGroupReference: resolveGroupReference,
    resolveItemReference: resolveItemReference
  });

  ...

})();

כעת, מכל מקום באפליקציה נוכל לקרוא לפונקציות items, groups והאחרות בתוך ה- namespace ששמו Data.

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

נריץ את האפליקציה כדי לראות מה התוצאה של הקוד שכתבנו עד כה.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

כדי להשלים זאת, נפתח את הקובץ groupedItems.html בתוך תקיית ה- pages שאחראי לתצוגת הדף הראשי באפליקציה.

<!DOCTYPE html>
<html>
<head>
  ...
</head>
<body>
  <!-- These templates are used to display each item in the 
  ListView declared below. -->
  <div class="headertemplate" data-win-control="WinJS.Binding.Template">
    <button class="group-header win-type-x-large win-type-interactive"
      data-win-bind="groupKey: key"
      onclick="Application.navigator.pageControl.navigateToGroup(event.srcElement.groupKey)"
      role="link" tabindex="-1" type="button">
      <span class="group-title win-type-ellipsis"
        data-win-bind="textContent: title"></span>
      <span class="group-chevron"></span>
    </button>
  </div>

  <div class="itemtemplate" data-win-control="WinJS.Binding.Template">
    <div class="item">
      <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>
  </div>

  <!-- The content that will be loaded and displayed. -->
  <div class="fragment groupeditemspage">
    <header aria-label="Header content" role="banner">
      <button class="win-backbutton" aria-label="Back" disabled
        type="button">
      </button>
      <h1 class="titlearea win-type-ellipsis">
        <span class="pagetitle">RSSReader</span>
      </h1>
    </header>
    <section aria-label="Main content" role="main">
      <div class="groupeditemslist win-selectionstylefilled"
        aria-label="List of groups" data-win-control="WinJS.UI.ListView"
        data-win-options="{ selectionMode: 'none' }">
      </div>
    </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" />

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

במקרה שלנו, לכתבות השונות אין subtitle ולכן, נאתר את אלמנט ה- H6 בתוך ה- itemtemplate ונמחק את כולו.

<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
    <div class="item">
      <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>
  </div>

לאחר השינויים האלה, תיראה האפליקציה כך (ללא ה- Subtitles שהיו undefined קודם).

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

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

עמוד כתבה בודדת

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

נפתח את עמוד ה- HTML שמגדיר את דף הכתבה – itemDetail.html, נאתר את השורה הבאה הממקמת אלמנט תמונה בדף ונמחוק אותה.

<img class="item-image" src="#" />

כעת, אם נריץ את האפליקציה ונכנס לעמוד כתבה, נקבל את השגיאה: 0x800a138f – JavaScript runtime error: Unable to set property 'src' of undefined or null reference.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

הסיבה לכך, נמצאת בקובץ itemDetail.js:

(function () {
  "use strict";

  WinJS.UI.Pages.define("/pages/itemDetail/itemDetail.html", {
    // This function is called whenever a user navigates to this page. It
    // populates the page elements with the app's data.
    ready: function (element, options) {
      var item = options && options.item
              ? Data.resolveItemReference(options.item)
              : Data.items.getAt(0);
      element.querySelector(".titlearea .pagetitle").textContent = item.group.title;
      element.querySelector("article .item-title").textContent = item.title;
      element.querySelector("article .item-subtitle").textContent = item.subtitle;
      element.querySelector("article .item-image").src = item.backgroundImage;
      element.querySelector("article .item-image").alt = item.subtitle;
      element.querySelector("article .item-content").innerHTML = item.content;
      element.querySelector(".content").focus();
    }
  });
})();

קובץ זה, המגדיר את הלוגיקה של עמוד הכתבה הבודדת, מכיל את הפונקציה ready הנקראת כאשר העמוד נטען. קוד הפונקציה מחלץ את נתוני הכתבה הנוכחית, כגון הכותרת, התמונה והתוכן עצמו ומציב אותם באלמנטים המתאימים בדף. באופן ספציפי, השורות הבאות מציבות את התמונה הראשית של הכתבה באלמנט המסומן ב- item-image, שמתייחס לאלמנט התמונה שלפני רגע מחקנו מהדף. זאת הסיבה לשגיאה בזמן הריצה שקיבלנו.

element.querySelector("article .item-image").src = item.backgroundImage;
element.querySelector("article .item-image").alt = item.subtitle;

נמחק את שתי השורות הנ”ל, נריץ שוב את האפליקציה ונקבל את התוצאה הרצויה – התמונה מופיעה פעם אחת בלבד.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

עמוד קטגוריה

בעמוד הראשי של האפליקציה נלחץ על שם של קטגוריה, לדוגמא “אינטרנט”, נגיע לעמוד הקטגוריה שנראה כך:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

כדי לפתור זאת, נכנס לעמוד ה- html שמגדיר את דף הקטגוריה – הקובץ groupDetail.html בתיקיית pages. גם דף זה מחולק למספר אלמנטי div המגדירים את האופן בו מוצג הדף (אלמנט ה- div השלישי) והאופן בו מוצגים כותרת הקטגוריה וכן כל כתבה.


<!DOCTYPE html> <html> <head> ... </head> <body> <!-- These templates are used to display each item in the ListView declared below. --> <div class="headerTemplate" data-win-control="WinJS.Binding.Template"> <h2 class="group-subtitle" data-win-bind="textContent: subtitle"></h2> <img class="group-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> <h4 class="group-description" data-win-bind="innerHTML: description"></h4> </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-info"> <h4 class="item-title" data-win-bind="textContent: title"></h4> <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6> <h4 class="item-description" data-win-bind="textContent: description"></h4> </div> </div> <!-- The content that will be loaded and displayed. --> <div class="groupdetailpage fragment"> <header aria-label="Header content" role="banner"> <button class="win-backbutton" aria-label="Back" disabled></button> <h1 class="titlearea win-type-ellipsis"> <span class="pagetitle"></span> </h1> </header> <section aria-label="Main content" role="main"> <div class="itemslist" aria-label="List of this group's items" data-win-control="WinJS.UI.ListView" data-win-options="{ selectionMode: 'none' }"> </div> </section> </div> </body> </html>

בעמוד זה נמחוק את השורה הבאה בתוך תבנית תצוגת כותרת הקטגוריה:

<h4 class="group-description" data-win-bind="innerHTML: description"></h4>

בנוסף, נמחוק את השורות הבאות מתוך תבנית ה- itemtemplate המגדירות את האופן שבו מוצגת כל כתבה.

<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
<h4 class="item-description" data-win-bind="textContent: description"></h4>

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

הוספת Branding לאפליקציה

האפליקציה שלנו כמעט גמורה, אך ניתן לשים לב כי שם האפליקציה רשום כטקסט “RSSReader”. נראה שהאפליקציה שלנו תקבל look and feel של אתר ניוזגיק, נרצה לשנות את לוגו האפליקציה ללוגו ניוזגיק.

כדי להחליף את שם האפליקציה בלוגו ממותג, נרצה ראשית להוסיף את הלוגו לתיקיית קבצי ה- images באפליקציה. נבחר את התיקייה ב- Solution Explorer ונבחר באפשרות Add ו- Existing Item.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

נוסיף את לוגו האפליקציה הבא (לחצו על התמונה ושמרו אותה במחשב המקומי).

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

כעת יופיע הלוגו כקובץ נוסף בתיקיית ה- images, וריחוף מעליו יציג את התמונה.

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

<span class="pagetitle">RSSReader</span>

בביטוי:

<img src="/images/newsgeek_logo.png" />

ונקבל את התוצאה הבאה:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

מקומות נוספים המשפיעים על מיתוג האפליקציה הם ה- Splash Screen, ה- Tile והאייקון לחנות האפליקציות. לדוגמא, כאשר אנחנו מריצים את האפליקציה, המסך הראשון שאנחנו רואים הוא המסך הזה:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

וכן על מסך ה- Start שלנו מופיע ה- Tile הלא ממותג הבא:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

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

נחליף את התמונות האלה בתמונות ממותגות אחרות, ע”י הוספתן לתיקיית ה- images.

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

נבחר תמונות בגדלים המתאימים שיחליפו את התמונות הלא ממותגות הנוכחיות. באיזור ה- Tile, נציין תמונת Tile צר בגודל 150×150, וכן tile רחב בגודל 150×310. בנוסף, נציין תמונה ל- Splash Screen בגודל 620×300, ונבחר את צבע הרקע להיות בצבע הרקע בתמונת ה- Splash Screen.

לאחר פעולות אלה נקבל את ה- Splash Screen הבא בעת הפעלת האפליקציה:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

ועל שולחן העבודה יופיע ה- Tile:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

שינוי כיוון האפליקציה לעברית (מימין לשמאל)

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

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

לאחר השינוי, במידה ונריץ את האפליקציה, נראה את התוצאה הבאה:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

ניתן לראות שלמרות שהאפליקציה מוצמדת לימין, היא לא נראית טוב לגמרי. לדוגמא – הרווח בין הכתבות בקטגוריות השונות אינו נכון, ומיקום חלק מהאלמנטים השתבש. הסיבה לכך היא שבאופן מצער תבניות הפרוייקטים ב- Visual Studio 2012 לא תומכות באופן מושלם באפליקציות מימין לשמאל, ועלינו לבצע מספר תיקונים כדי להוסיף את התמיכה הזאת בעצמנו. אלעד כץ דיבר על כך בפוסט שלו איך מוסיפים תמיכה בשפות שנכתבות מימין לשמאל בחלונות 8?

התיקון מורכב משני שלבים: 1) תיקון באחד מקבצי ה- JavaScript של התבנית, 2) תיקוני CSS עבור תצוגת RTL.

נתחיל בתיקון ב- JavaScript: נפתח את הקובץ navigator.js שבתיקיית קבצי ה- js, ונאתר בו את הפונקציה _createPageElement שאחראית על יצירת עמוד חדש בעת ניווט אליו:

// Creates a container for a new page to be loaded into.
_createPageElement: function () {
    var element = document.createElement("div");
    element.style.width = "100%";
    element.style.height = "100%";
    return element;
},

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

// Creates a container for a new page to be loaded into.
_createPageElement: function () {
  var element = document.createElement("div");
  element.setAttribute("dir", window.getComputedStyle(this._element, null).direction);
  element.style.width = "100%";
  element.style.height = "100%";
  return element;
},

התיקון השני מתייחס לקבצי CSS אותם יש לעדכן לתמיכה ב- RTL. ניתן להוריד קובץ ה- Zip עם קבצי ה- CSS המתוקנים, ולפרוס אותם למקומות המתאימים בתוך הפרוייקט (קובץ ה- default.css לתוך תיקיית ה- css וקבצי ה- css האחרים לתוך התיקיות של דפי האפליקציה השונים).

לאחר הטמעת תיקוני ה- Right to Left תראה האפליקציה כך:

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

מדריך: בניית קורא RSS ל- Windows 8 עם HTML5 ו- JavaScript

השלב הבא

לאחר סיום הפיתוח, מומלץ לבדוק את האפליקצה בעזרת Windows App Certification Kit (בקיצור WACK). זהו כלי אוטומטי שמבצע בחינת התאמת האפליקציה שלכם לחלק מתנאי הסמכת אפליקציות לחנות האפליקציות של Windows. רשימת הבדיקות עשויה להשתנות בשחרורים שונים של חלונות 8, ובזמן כתיבת הכתבה אינו סופי. את רשימת הבדיקות הנוכחית ניתן לראות באתר MSDN.

סיכום

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

Add comment
facebook linkedin twitter email

one comment

  1. Goniak19 ביוני 2012 ב 20:43

    אחלה מדריך!
    יש מצב שבקטע קוד הזה:
    var staticHTML = window.toStaticHTML(item.content);
    WinJS.Utilities.setInnerHTMLUnsafe(
    element.querySelector("article .item-content"),
    item.content);

    שכחת להשתמש ב staticHTML, כלומר:

    WinJS.Utilities.setInnerHTMLUnsafe(
    element.querySelector("article .item-content"),
    staticHTML);

Comments are closed.