Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

(Windows8) WinJS single page navigacija i ViewModels

od Ioannis 23. Mart 2012. 06:43



U prethodnom postu, videli smo jednostavne data-binding scenarije korišćenjem WinJS-a i stekli smo uvid u način na koji se može kreirati ViewModel (prateći MVVM obrazac). U ovom postu, pravićemo aplikaciju sa više od jednog prikaza (aplikacija: stranice - jednostavna navigacija kroz stranice) sa jednostavnim html elementima i ListView-ima sa stvarnim podacima. Pitanje na koje treba da damo odgovor je:

“Kako ViewModel-i i MVVM mogu biti primenjeni u WinJS aplikaciji sa single page navigacijom?”

(Za .NET programere: ako dolazite iz sveta .NET -a, a posebno XAML-a, vi ćete takođe dobiti priliku da vidite kako se stvari poput INotifyPropertyChanged i ObservableCollections primenjuju u WinJS svetu. Ovde prikazani ViewModel je najbliži mogući onom koji bi bio primenjen u WPF-Silverlight prozoru)

Napravićemo jednostavnu aplikaciju koja prikazuje rođenja i smrti poznatih ličnosti na današnji dan. Podaci će dolaziti sa RSS prijema sa IMDB-a (Internet Movie Data Base). Klik na neku poznatu ličnost bi trebalo da nas odvede na novu stranicu gde ćemo dobiti IMDB veb stranu te ličnosti. U okviru ove aplikacije ViewModel-i će biti korišćeni za svaku stranu. Zahtevi koje imamo u pogledu naših ViewModel-a su sledeći:
  • Za svaku stranicu treba da postoji po jedan ViewModel.
  • ViewModel treba da čuva sve promenljive (varijable) i sve metode potrebne da se obezbede odgovarajući podaci prikazu (html strani).
  • Prikaz (view) će se povezati na ViewModel kroz data binding.
  • Svi događaji generisani na stranici takođe će “okidati” i metode ViewModel-a.
Otvorite VS 2011 i kreirajte jedan “Navigation Application” projekat. Generisani “default.html” fajl sadrži sledeći kod:

 

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

Koji definiše radni prostor u kome će se učitavati vaše stranice, prema vašem application’s state-u. Ovaj prostor se inicijalizuje i učitava se “homePage.html” stranica (VS je takođe za vas generisao “navigate.js” fajl koji služi za navigaciju. Za uvod u single page navigaciju možete pogledati ovaj članak).

Trebalo bi da svaku stranicu koja će biti učitana u ovaj radni prostor posmatrate kao poseban prozor ili prikaz vaše aplikacije. Tako, svaka stranica treba da ima sopstveni ViewModel. U našem slučaju imamo dve stranice: jednu (“homePage”) koja prikazuje rođenja i smrti u dva ListView-a i drugu (“actorDetails”) koja prikazuje detalje o izabranom glumcu onda kada korisnik klikne na neki od elemenata (item-a) u listama.

Krenimo sa “homePage” prikazom koji će izgledati kao ovaj na sledećoj slici:



Što će reći, treba da postoje dve liste, jedna koja prikazuje smrti i jedna koja prikazuje rođenja. Takođe, odmah ispod glavnog zaglavlja (main header-a) treba da prikažemo datum i vreme i “downloading” poruku onda kada se učitavaju podaci sa IMDB-a. Html kod za generisanje ovog prikaza je (naravno, tu je i .css koji takođe stvara magiju layout-a):

<section aria-label="Main content" role="main">
<span id="dateSpan"></span>&nbsp;&nbsp;
<span id="downloadingSpan"></span>
<section class="listsContainer">
<h2 class="birthsHeader">born today</h2>
<h2 class="deathsHeader">died today</h2>
<div id="birthsListElement" data-win-control="WinJS.UI.ListView"></div>
<div id="deathsListElement" data-win-control="WinJS.UI.ListView"></div>
</section>
</section>

Tako će nam za naš ViewModel trebati: svojstvo (property) za datum/vreme, svojstvo za “downloading” poruku, dve liste za rođenja i za smrti, dva metoda da učitavaju smrti i rođenja i rutina za obradu događaja (event handler) koja će se pozivati onda kada korisnik klikne na neki element liste. Postupak za kreiranje ovog ViewModel-a u “homepage.js” fajlu je sledeći:
Unutar samoizvršavajuće (self executing) funkcije koja je generisana za nas, definišemo novu klasu (“WinJS.Class.define”) koju nazivamo “HomePageViewModelClass”:

var HomePageViewModelClass = WinJS.Class.define(
function () {
this.onShowActorDetailsBirths = this.showActorDetailsBirths.bind(this);
this.onShowActorDetailsDeaths = this.showActorDetailsDeaths.bind(this);
},
{
_today: "",
today: {
get: function () {
return this._today;
},
set: function (value) {
this._today = value;
this.notify("today", value);
}
},

_downloading: "",
downloading: (...),

birthsList: new WinJS.Binding.List(),
deathsList: new WinJS.Binding.List(),
getBirths: function () {
this.downloading = "(downloading...)";
var that = this;
var syn = new Windows.Web.Syndication.SyndicationClient();
var url = new Windows.Foundation.Uri("http://rss.imdb.com/daily/born/");
syn.retrieveFeedAsync(url).then(
function (feed) {
for (var i = 0, len = feed.items.length; i < len; i++) {
var item = feed.items[i];
var birth = {
title: item.title.text,
date: item.publishedDate,
content: item.summary.text,
link: item.links[0].nodeValue
}
that.birthsList.push(birth);
}
that.downloading = "";
});
},
getDeaths: (...),
showActorDetailsBirths: function (e) {
this._showActorDetails(this.birthsList.getAt(e.detail.itemIndex));
},
showActorDetailsDeaths: function (e) {
this._showActorDetails(this.deathsList.getAt(e.detail.itemIndex));
},

_showActorDetails: function (item) {
WinJS.Navigation.navigate("/html/actorDetail.html", item);
}

}
);


Treba imati na umu sledećih nekoliko stvari:
  • Za svojstva “today” i “downloading” koristimo aksesor (accessor) metode. Aksesori uzimaju (get) i podešavaju (set) vrednosti njima odgovarajućih privatnih polja. Primetićete da, pošto podese svoje vrednosti, oni pozivaju “neobičan” (za sada) metod nazvan notify sa imenom svojstva koje se promenilo i njegovom novom vrednošću. Za nekoliko trenutaka ćete videti gde je ovaj metod definisan, ali za sada upamtite da je to ono što čini, kada se posmatrano svojstvo menja, deo ažuriranja korisničkog interfejsa (UI) vezan za podatke (za .NET programere: isto kao “PropertyChanged”).

  • Metodi “showActorDetailsBirths” i “showActorDetailsDeaths” su ti koji će biti pozivani kada korisnik klikne na neki elemenat liste i stoga će biti vezani za “oniteminvoked” događaj. Zbog javascript-ovih problema sa ključnom reči “this”, u konstruktoru klasa, koristeći bind metod, iz tih metoda (sa prefiksom “on”) kreiramo dva nova metoda u kojima se “this” ispravno tumači, kao zaseban objekat, i koji će biti vezani za događaje.

  • Obe liste su “WinJS.Binding.List” tipa kako bi se obezbedili neophodni događaji za ažuriranje korisničkog interfejsa onda kada se listama dodaju elementi (za .NET programere: isto kao ”ObservableCollection”).
Sada, nakon prethodno navedene definicije klase kreiramo iz nje sledeću novu definiciju klase:

var BindableHomePageViewModelClass = WinJS.Class.mix(HomePageViewModelClass, WinJS.Binding.mixin);


Ovde ustvari koristimo “WinJS.Class.mix” da u našu HomePageViewModelClass klasu dodamo sve metode i svojstva sadržana u “WinJS.Binding.mixin” klasi i - pogodite šta, to su metodi potrebni za pružanje obaveštenja o promenama kod data bindinga (ovo je izvor notify metoda). To takođe znači da nikada ne bi trebalo da koristimo HomePageViewModelClass, već uvek BindableHomePageViewModelClass, pošto je ona ta koja je “kompletna” - nakon primene mix metoda.

Sada ćemo naš ViewModel napraviti javnim u “ProgwareOrg” nejmspejsu, na sledeći način, kreirajući novi objekat. Taj objekat će biti vezan za prikaz:

WinJS.Namespace.define("ProgwareOrg", {
HomePageViewModel: new BindableHomePageViewModelClass()
});


U funkcijama stranice, koje obrađuju osnovne događaje naše stranice, možemo vezati ViewModel za prikaz na sledeći način (za .NET programere: ovaj kod bi trebalo posmatrati kao xaml.cs fajl i vezivanje za ViewModel koje se tu javlja):

WinJS.UI.Pages.define("/html/homePage.html", {
init: function init(element, options) {

var calendar = new Windows.Globalization.Calendar();
ProgwareOrg.HomePageViewModel.today = calendar.dayOfWeekAsString() + " " +

calendar.day + " " + calendar.monthAsString() + " " + calendar.yearAsString();
ProgwareOrg.HomePageViewModel.getBirths();
ProgwareOrg.HomePageViewModel.getDeaths();
},
ready: function ready(element, options) {
WinJS.Binding.processAll(home, ProgwareOrg.HomePageViewModel);
}
});



Inicijalizujemo naš ViewModel i u ready funkciji ga vezujemo za prikaz posredstvom “WinJS.Binding.ProcessAll” metoda (za .NET programere: ovo bi u XAML-u bilo this.DataContext=ViewModel). Ovaj metod kao prvi parametar uzima root elemenat tamo gde je ViewModel primenjen, a u našem slučaju to je elemenat čiji je id=”home”.

Povezivanje u prikazu odvija se na sledeći način:

<div class="homepage" id="home">
<section aria-label="Main content" role="main">
<span id="dateSpan" data-win-bind="innerText:today"></span>&nbsp;&nbsp;
<span id="downloadingSpan" data-win-bind="innerText:downloading"></span>
<section class="listsContainer">
(...)
<div id="birthsListElement"
data-win-control="WinJS.UI.ListView"
data-win-options="{itemDataSource:ProgwareOrg.HomePageViewModel.birthsList.dataSource,
itemTemplate:select('#template'),
layout:{type:WinJS.UI.ListLayout},
oniteminvoked : ProgwareOrg.HomePageViewModel.onShowActorDetailsBirths}"
>
</div>
<div id="deathsListElement" (...)></div>
</section>
</section>
</div>


Prvo vidite da je top-level elemenat (onaj koji je upotrebljen u processAll-u) elemenat čiji je id=”home”. Takođe vidite “data-win-bind” atribute koji obezbeđuju povezivanje za HTML elemente.

Kod dva ListView-a obratite pažnju na sledeće:

itemDataSource je vezan za naš ViewModel, ali koristi potpuno kvalifikovano ime (puno ime). To znači da ne uzima u obzir processAll metod. Ne znam zašto se ovo dešava i mislim da je to nešto što treba popraviti, ali za sada rešenje koje sam našao je da za kontrole WinJS-a za vezivanje za ViewModel treba da koristimo pune pristupne putanje našeg ViewModel-a. Isto se odnosi i na vezivanje oniteminvoked rutine za obradu događaja. Komplet templejtova je definisan na stranici odmah ispod ovog koda i nije prikazan ovde zbog jednostavnosti.

Sve u svemu, ovo je način na koji se ViewModel može vezati za naš prikaz. ViewModel za “actorDetails” stranicu je jednostavniji:

(function () {
"use strict";

var ActoDetailViewModelClass = WinJS.Class.define(
function () {},
{
_pageToShow: "",
pageToShow: {
get: function () {
return this._pageToShow;
},
set: function (value) {
this._pageToShow = value;
this.notify("pageToShow", value);
}
}
}
);

var BindableActoDetailViewModelClass = WinJS.Class.mix(ActoDetailViewModelClass, WinJS.Binding.mixin);

WinJS.Namespace.define("ProgwareOrg", {
ActoDetailViewModel: new BindableActoDetailViewModelClass()
});

WinJS.UI.Pages.define("/html/actorDetail.html", {
ready: function ready(element, options) {
ProgwareOrg.ActoDetailViewModel.pageToShow = options.link;
WinJS.Binding.processAll(mainSection, ProgwareOrg.ActoDetailViewModel);
},
updateLayout: function updateLayout(element, viewState) { }
});
})();


I prikaz:

<div id="mainSection" class="actorDetail fragment">
...
<section aria-label="Main content" role="main">
<iframe id="actorsPage" data-win-bind="src:pageToShow" style="width: 100%;height: 100%" />
</section>
</div>


Podaci se prosleđuju između stranica kao drugi po redu parametar u WinJS.Navigation.navigate("/html/actorDetail.html", item);. Oni se mogu preuzeti sa odredišne stranice kroz “options” parametar “ready” funkcije.

“actorDetail” stranica koristi jedan iframe za prikaz preuzete veb strane sa podacima o glumcu. Pošto ta strana sadrži ActiveX kontrole, kada pokrenete projekat iz VS-a dobijaćete grešku: “JavaScript runtime error: Access is denied”. Ukoliko pogledate u Javascript konzoli videćete da je greška: “Cannot load the ActiveX plug-in that has (…)” i “Apps can't load ActiveX controls”. Ali ukoliko kompajlirate aplikaciju i postavite je u “Release”, odete na Windows8 početnu stranu i pokrenete aplikaciju, ovo se neće javiti. To ne znači da greška nestaje, već znači da se u “release” modu ActiveX samo neće učitati, što nama odgovara.

Dakle, u ovom postu videli smo jedan pristup kreiranju ViewModel-a u aplikaciji sa single page navigacijom pod Windows8 operativnim sistemom.




Published (Last edited): 12-07-2012 , source: http://www.progware.org/Blog/post/%28Windows8%29-WinJS-single-page-navigation-and-ViewModels.aspx