Corrado's Blog 2.0

Online thoughts of a technology funatic

MvvmStack for WinJS: Application Resuming support

I’ve checked in a new version of MvvmStack that has a small tweak and a “new” feature, let’s start with the latter:

Resuming support

Sometimes you need to be informed when app resumes, an example is because you want to download some fresh data if app has been suspended longer than a certain amount of time.

With MvvmStack all you need to do to is to add a onAppResuming method to your services and/or ViewModels, something like: (see sectionViewModel.js on sample code)

onAppResuming: function () {
            console.log("SectionViewModel notified that app has been resumed.")
        }

And it will be invoked each time app resumes.

The ‘tweak’ regards the way you add new  ViewModels, in this post, on Closing section i mention:

“3- Add a new property “pagenameViewModel” to ViewModelLocator’s  viewModels object“ 

starting from this version the step is no longer required Smile

MvvmStack for WinJS: Command support

A Command represents an abstraction over an action, the command can also indicate whether the action is available or not and, normally, UI elements tied to a command disables themselves when command is not available, if you’re new to MVVM’s commanding I recommend this article.

While more a .NET concept than a Mvvm pattern core feature, i decided to add support for it with latest checkin since its wide popularity and to keep the stack aligned with other .NET based libraries.

In  .NET a Command depends upon the ICommand interface and it is generally implemented by generic classes like RelayCommand or DelegateCommand, for MvvmStack I’ve used a simpler implementation available in relayCommand.js

(function (winjs) {
    "use strict";

    var RelayCommand = WinJS.Class.define(function(execute) {
        this.execute = execute;
        this.execute.supportedForProcessing = true;
        this.canExecute = winjs.Binding.as({value:true});
    },
        {
            execute: null,
            canExecute: null
        });

    winjs.Namespace.define("MvvmStack", {
        RelayCommand: RelayCommand
    });

})(WinJS)

To use it all you need is to expose instances of RelayCommand from your viewmodels, sample code exposes them in sectionViewModel.js

firstCommand: new mvvmStack.RelayCommand(function (element) {
            this.currentPage.value = 0;
            this.firstCommand.canExecute.value = false;
            this.lastCommand.canExecute.value = true;
        }),
        lastCommand: new mvvmStack.RelayCommand(function (element) {
            this.currentPage.value = this.images.length - 1;
            this.firstCommand.canExecute.value = true;
            this.lastCommand.canExecute.value = false;
        }),

then we wire the commands with application bar commands in section.html page

<div data-win-control="WinJS.UI.AppBar">
            <button
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-bind="onclick:firstCommand Binding.Mode.command"
                data-win-options="{icon:'home', id:'', label:'Home', section:'global', type:'button'}">
            </button>
            <button
                data-win-control="WinJS.UI.AppBarCommand"
                data-win-bind="onclick:lastCommand Binding.Mode.command"
                data-win-options="{icon:'next', id:'', label:'Last', section:'global', type:'button'}">
            </button>
        </div>

In order to keep button state in sync with commands availability we use a new Binding.Mode.command initializer, also note how click event binds to command and not to its execute function.

As usual the binding initializer is defined inside binding-extensions.js, you can update it in case you need to handle other enable/disable scenarios.

Binding.Mode.command automatically adds a mvvm-disabled class to invoking element (app bar buttons in our case) so that you can use a css class to change the look of the control while in disabled state.

To see commands in action, run the sample, navigate to section page and click application bar’s buttons or navigate through images to see how buttons follow current’s image position.

Hope you like it Smile

PS: In order to simplify dev life I’ve added a watch function to viewModelBase that allows to invoke a function when an observable property changes (just a simple wrapper around bind function indeed Winking smile)

MvvmStack for WinJS: Services persistence

While refactoring MvvmStack for WinJS code i noticed that I did not show how to persist the state of the services when the app get suspended, so i checked in a new version that persist the data contained inside imageService.js (in a real world app, the data will probably come from a remote server).

The strategy I use is to let the services that need to keep their state add themselves to a services collection exposed by applicationControllerBase object that is passed to each service instance:

(function (winjs, applicationController) {
    var images = [];
    var cachedId;
    var imageService = {
        load: function (id) {
            return new winjs.Promise(function (c, e) {
                //Returns cached images when available
                if (cachedId == id && images.length > 0) {
                    c(images);
                } else {
                    cachedId = id;
                    if (id == 1) {
                        images = [
                            { uri: "/images/data/Photo1.jpg", title: 'Photo 1' },
                            { uri: "/images/data/Photo2.jpg", title: 'Photo 2' },
                            { uri: "/images/data/Photo3.jpg", title: 'Photo 3' },
                            { uri: "/images/data/Photo4.jpg", title: 'Photo 4' },
                            { uri: "/images/data/Photo5.jpg", title: 'Photo 5' }];
                    } else {
                        images = [
                            { uri: "/images/data/Photo6.jpg", title: 'Photo 6' },
                            { uri: "/images/data/Photo7.jpg", title: 'Photo 7' },
                            { uri: "/images/data/Photo8.jpg", title: 'Photo 8' }];
                    }

                    c(images);
                }
            });
        },
        serialize: function () {
            return {
                images: images,
                id:cachedId
            };
        },
        hydrate: function (state) {
            images = state.images;
            cachedId = state.id;
        }
    };

    winjs.Namespace.define("Demo.Services", {
        imageService: imageService
    });

    applicationController.services.push(Demo.Services.imageService);

})(WinJS, Demo.Application.ApplicationController)

As you see in code above the service also exposes the same methods serialize and hydrate we met when speaking about viewmodels that serialize and deserialize service state to/from a json object.

Once we registered and added these functions, the task of invoking them when needed is contained inside Mvvm Stack’s common peristenceService.js

Here’s a fragment of serializaion code:

 //De-Serializes services that implements serialize function
            var j = 0;
            applicationController.services.forEach(function (service) {
                if (service.hydrate !== undefined) {
                    var serviceKey = "service" + j;
                    var serviceState = winjs.Application.sessionState.app[serviceKey];
                    service.hydrate(serviceState);
                }
            });

No rocket science, but it makes stack more complete and usable in real production code.

MvvmStack for WinJS Part#5

In this last post about MvvmStack I’m going to cover two aspects: Binding and Blendabilty.

Binding

I’m not going into WinJS Binding since MSDN documentation provides a lot of material, i just want to describe some binding extensions available into binding-extension.js file that extends WinJS binding capabilities.

image

Inside Binding.Extensions namespace you find:

twoWay: I’ve described it here

eventToProperty: Allows you to update a viewmodel property when a control event occurs, you can also specify a two-way mode so that when viewmodel’s property changes the control’s property is updated with new value. The demo uses it to notify the viewmodel that user flipped the image on flipview control (on section.html page) and to move to fist or last image on set using application bar’s buttons.

Here’s how is declared inside section.html page:

        <div id="flip"
            data-win-control="WinJS.UI.FlipView"
            data-win-bind="winControl.itemDataSource:images.dataSource; winControl.onpagecompleted$currentPage.value:winControl.currentPage$two Binding.Extensions.eventToProperty"
            data-win-options="{ itemTemplate : select('.flipItemTemplate') }">
        </div>

The syntax is [triggerEvent]$[viewModelProperty]:[controlProperty][$two], previous html snippet shows how, when FlipView’s onPageCompleted triggers, we update viewmodel’s currentpage.value property using control’s currentPage property in two way mode.

invokeOnEvent: Invokes a viewmodel’s method when an event occurs, passing event source as parameter to target function (sort of ‘sender’ parameter C# counterpart) if you don’t need sender, just use standard WinJS binding.

arrayBind: Allows binding to viewmodel’s properties exposed as arrays using this syntax: [sourceProp]:[property][index][property] e.g: data-win-bind="src:dataTile[0].image"

Blendability

If you, like me, use Blend for HTML tool to design the views you know that it offers an interesting interactive mode.

By clicking following button:

image

your app is run, you can play with it, then exiting from interaction mode, start modifying the live DOM acting on current live state.

Sometimes running the app and reaching the page you want to edit is tedios, that’s why i prefer to use an alternative way.

Inside defaul.html you’ll see that there’s a commented PageControlNavigator definition, this is the one i use to start app from a specific page, let’s change default.html markup this way.

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

    <!--Uncomment this when you want to design a specific application page, dont forget to create fake data inside ViewModelLocator-->
    <div id="contenthost" data-win-control="Application.PageControlNavigator" data-win-options="{home:'/pages/section/section.html'}"></div>
</body>

Next we need to create some design-time data for editing purposes, so we uncomment the call to createDesignTimeViewModels line inside viewModelLocator.js

  //Uncomment this to create a fake design time viewmodel to use with the page associated with default.html's page navigator
    viewModelLocator.createDesignTimeViewModels();

what the function does is create some fake data when the page is loaded inside Blend.

Opening default.html in Blend now shows the page populated with fake data so you can start editing it.

image

Closing

Think I’ve covered everything you need to know to play with sample code, as stated initially there’s room for improvements and modifications. My goal was to provide an example of how to use MVVM patter in WinJS based applications.

Hope you enjoyed reading. Winking smile

MvvmStack for WinJS Part #4

In part3 we learned how to customize the components tied to our application, it is now time to investigate the ViewModels associated with each view.

Lets’ start seeing how a viewmodel is declared, inspecting homeViewModel.js, the one paired with demo application home view.

(function (winjs, mvvmStack, target) {
    "use strict";

    var HomeViewModel = winjs.Class.derive(mvvmStack.ViewModelBase, function (applicationController) {
        var data = [{ name: "Section 1", id: 1, description: "My color photos", image: "/images/phones/lumia920.jpg" },
            { name: "Sezione 2", id: 2, description: "My b/w photos", image: "/images/phones/lumia820.jpg" }];

        this._appController = applicationController;
        this.sections = new winjs.Binding.List(data).dataSource;
        this.processAll();
    },
        {
            sections: null,
            templateRenderer: function (itemPromise) {
                return itemPromise.then(function (item) {
                    // Select either normal product template or on sale template
                    var itemTemplate;
                    if (item.data.id == 1) {
                        itemTemplate = document.getElementsByClassName("productTemplate1")[0];
                    } else {
                        itemTemplate = document.getElementsByClassName("productTemplate2")[0];
                    }

                    // Render selected template to DIV container
                    var container = document.createElement("div");
                    itemTemplate.winControl.render(item.data, container);
                    return container;
                })
            },
            itemInvoked: function (e) {
                var self = this;
                e.detail.itemPromise.then(function (item) {
                    var selectedItem = item.data;
                    self._appController.selectedHomeSection = selectedItem.id;
                    //Navigates to detail page
                   mvvmStack.Navigation.navigate(target.section)
                });
            }
        });


    winjs.Namespace.define("Demo.ViewModels", {
        HomeViewModel: HomeViewModel
    });

})(WinJS, MvvmStack, Demo.Navigation.target)

As you see, it is a class inheriting from ViewModelBase that receives some external dependencies like other MvvmStack references, target urls object and, obviously, WinJS namespace.

Inside constructor we create the sections that are going to appear in home page using static data (in real world they might come from an injected external service), in this case we have section 1 and section 2, each one has its own characteristics, also note that at the end we invoke base class function processAll that marks all function exposed by the class “safe for databinding” and ensures that ‘this’ inside any instance functions safely points to class instance (if this sounds weird to you read here) please note that, at the moment, processAll doesn’t handle functions exposed by class within nested objects.

Then we have instance properties:

sections: The sections we’re going to bind to homepage listview.

templateRenderer: Since we want each section to appear differently, we created a couple of listview templates and render them through this function depending on item’s Id value.

invoked: Is the function that gets invoked when user taps a listview item, as you see, we store section id value into shared applicationcontroller object then we use mvvmstack navigation infrastructure to navigate to section page, this will create sectionViewModel first, then navigates to section.html page.

How do we pair this viewModel to homepage view?

(function (winjs,viewModels) {
    "use strict";

    WinJS.UI.Pages.define("/pages/home/home.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) {
            winjs.Binding.processAll(element, viewModels.homeViewModel);
        }
    });
})(WinJS,Demo.ViewModelLocator.viewModels);

The code is quite simple, it just uses WinJS.Binding.ProcessAll to set homeViewModel as datacontext for current view, nothing more, nothing less.

Mapping viewmodel to View elements, databinding to the rescue!

Connection of HomeViewModel properties to view elements is done exclusively via databinding, as any other MVVM implementation, here’s a homepage.html fragment:

<div class="fragment homepage">
        <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">MVVMStack demo...</span>
            </h1>
        </header>

        <div data-win-control="WinJS.UI.ListView"
            class="homeListView"
            data-win-bind="winControl.itemDataSource:sections; winControl.itemTemplate:templateRenderer; winControl.oniteminvoked:itemInvoked"
            data-win-options="
        {
            layout: {type: WinJS.UI.GridLayout},
            tapBehavior: 'directSelect',
            selectionMode: 'none'
        }">
        </div>
    </div>

please note the use of winControl property to bind listview control’s property with viewmodel’s.

ViewModel class naming convention

In order to let the infrastructure know what are the viewmodels to persist when a suspend event occurs, viewmodel class name must be [pageName]+”ViewModel”, so if you add an “about.html” page, associated viewmodel class must be named “aboutViewModel”.

ViewModel Persistence

homeViewModel is made of static data so it is not necessary to persist it when a suspension event occurs, let’s see instead what happens inside sectionViewModel.js using following snippet:

{
        sectionTitle: null,
        images: null,
        currentPage: null,
        id: 0,
        load: function () {
            var self = this;
            this._imageService.load(self.id).then(function (photos) {
                photos.forEach(function (photoInfo) {
                    self.images.push(photoInfo);
                });
            });
        },
        first: function () {
            this.currentPage.value = 0;
        },
        last: function () {
            this.currentPage.value = this.images.length - 1;
        },
        serialize: function () {
            return {
                currentPage: this.currentPage.value
            };
        },
        hydrate: function (bag) {
            this.currentPage.value = bag.sectionViewModel.currentPage;
        }
    }

In this case the ViewModel exposes two functions: serialize and hydrate, former is invoked by persistenceService when it needs to save viewmodel’s status, latter when it is time to rehydrated it using saved informations. If your viewmodel requires state persistance just implement both functions, persistenceService.js will take care of the rest.

Closing

Once you have setup all infrastructure, here are the step required to add new page to a project:

  • 1-  Add a new pagecontrol to the project
  • 2- Add a new viemodel class named “pagenameViewModel
  • 3- Add a new property “pagenameViewModel” to ViewModelLocator’s  viewModels object
  • 4- Add a new entry to navigationTargets.js
  • 5- Add a new switch entry into ViewModelLocator’s createViewModelForUrl function.
  • 6- Modify page code behind to set ViewModel instance as page datacontext

[note] Step 3 no longer required, see here.

MvvmStack for WinJS Part #3

In this 3rd episode of the MvvmStack saga I’m going to describe how to use the set of core mvvm modules we saw in previous posts describing application dependent modules.

image

Let’s start describing navigationTargets.js

(function (winjs) {
    "use strict";

    winjs.Namespace.define("Demo.Navigation", {
        target: {
            home: "/pages/home/home.html",
            section: "/pages/section/section.html",
            noConnectivity: "/pages/noconnectivity/noconnectivity.html"
        }
    });
    
}(WinJS))

It’s a simple object, nested into Demo.Navigation namespace (use your own namespace in production code) that exposes a set of uri pointing to application pages, this allows us to centralize pages uri and pass these infos to various modules. As soon as you add new pages this object must be updated with new properties.

applicationController.js represents an optional application object shared among viewmodels, it can be used as a general data cache and it’s also persisted during suspend/resume events.

(function (winjs,mvvmStack) {
    "use strict";

    var ApplicationController = WinJS.Class.derive(mvvmStack.ApplicationControllerBase,null,
        {
            selectedHomeSection: 0,
            serialize: function () {
                return {
                    selectedHomeSection: this.selectedHomeSection
                };
            },
            hydrate: function (applicationState) {
                this.selectedHomeSection = applicationState.selectedHomeSection;
            }
        });

    var applicationController = new ApplicationController;

    winjs.Namespace.define("Demo.Application", {
        ApplicationController: applicationController
    });

})(WinJS,MvvmStack)

As you see in previous code, the object inherits from ApplicatioControllerBase class and exposes a selectedHomeSection that is used by homepageViewModel to inform sectionViewModel about what section has been selected by the user.

serialize and hydrate are functions invoked by persistence service when object must be persisted and lately rehydrated.

ApplicationController class is nested inside Demo.Application namespace.

networkService.js

While not mandatory, it is a service that monitors network connection status and using messenger object it posts messages when network connectivity status changes. The message is subscribed and handled inside connectivityService.js module

(function (winjs, messenger) {

    var _networkInfo = Windows.Networking.Connectivity.NetworkInformation;
    _networkInfo.addEventListener("networkstatuschanged", onNetworkStatusChange);
    var notifiedStatus = isInternetAvailable();

    //Handles network status changes
    function onNetworkStatusChange() {
        var mes = messenger.networkStatusChanged();
        mes.isInternetAvailable = isInternetAvailable();
        if (mes.isInternetAvailable != notifiedStatus) {
            notifiedStatus = mes.isInternetAvailable;
            messenger.send(mes);
        }
    }

    //Gets a value indicating whether internet connection is available
    function isInternetAvailable() {
        var connectivityLevel = Windows.Networking.Connectivity.NetworkConnectivityLevel.none;
        var profile = _networkInfo.getInternetConnectionProfile();
        if (profile != null) connectivityLevel = _networkInfo.getInternetConnectionProfile().getNetworkConnectivityLevel();
        return connectivityLevel === Windows.Networking.Connectivity.NetworkConnectivityLevel.internetAccess;
    }

    winjs.Namespace.define("Demo.Application.Services.Network", {
        isInternetAvailable: isInternetAvailable
    });

})(WinJS, Demo.Application.Messenger)

connectivityService.js is a completely optional component that in the demo application handles connectivity changes messages navigating to a static noConnectivity.html pagecontrol. Your application can freely ignore this module and handle connectivity changes in a total different way, all you need to do is to subscribe the message as connectivityService does.

viewModelLocator.js takes care of creating the viewModels associated with application pages, it’s a class that derives from viewModelLocator base and overrides createViewModelForUrl function.

createViewModelForUrl is the core function that take care of creating a viewModel before page gets loaded, depending on target url.

 createViewModelForUrl: function (uri) {
                switch (uri) {
                    case target.home:
                        this.viewModels.homeViewModel = this.viewModels.homeViewModel || new Demo.ViewModels.HomeViewModel(applicationController);
                        break;
                    case target.section:
                        this.viewModels.sectionViewModel = new Demo.ViewModels.SectionViewModel(applicationController, services.imageService);
                        this.viewModels.sectionViewModel.load();
                        break;
                }
            },

The snippet demonstrates how a single instance viewModel is creates for home page, while when user navigates to section page, a SectionViewModel instance is instantiated and related images loaded.

Since each viewModel becomes a property of viewModels object, when a new ViewModel is added the object must be updated adding a property that matches ViewModel name.

this.viewModels = {
            homeViewModel: null,
            sectionViewModel: null
        };

Note: I’m sure code can be refactored to avoid this step, maybe in a future release… Smile

The code also includes a createDesignTimeViewModels function, I’ll describe it in a forthcoming post regarding Blendability.

Closing

In next post we’ll analyze pages ViewModels, how to create and pair them with related View.