-
10,234,036 members (51,916 on lin e) 317 Sign ou t
vartmaan
TweetTweet 54
home quick answers discussions features community help Search fo
r articles, qu estion s, tip s
Articles Web Development Client side scripting General
Article
Browse Code
Stats
Revisions (3)
Alternatives
Comments &Discussions (45)
Add your ownalternative version
About Article
An example applicationusing angular/Rx
forJavaScript/WebSockets/jQuery
Type Article
Licence CPOL
First Posted 14 Aug 2013
Views 46,926
Bookmarked 105 times
C# Javascript CSS .NET
Architect Dev ADO.NET ,
+
Top News
30 years ago Windows wasfirst released, see howmuch it has
changed
Get the Insider News free eachmorning.
Related Videos
Related ArticlesArcPong
Angular: The DOM API YouHave Been Waiting For
Use Backbone.js to make astructured web application
AngularJS Single PageApplication with WebAPI andUpida
backend
Data with AngularJS
404 Hall of Fame - Celebratingthe most notorious http
errorcode!
Extending HTML with AngularJSDirectives
Next
Rate:Like 18
Angular.js example applicationBy Sacha Barber, 5 Sep 2013
Demo application code : Here is a link where you can download
the : Demo Application
Table Of Contents
IntroductionThe Demo App OverviewAngular.Js Introduction
AppsServicesModulesDependency
InjectionRoutingViewsControllersScopeDirectives
The PublisherThe Angular.js Website
Require.js UsageThe Main App SetupThe RoutingThe Root Page
LiveUpdatesServiceLocalStorageServiceImageServiceUtilitiesServiceDraggable
DirectiveResizable DirectiveRoot ControllerRoot View
The Favourites Page
Favs ControllerFavs ViewColorBox Directive
The About Page
That's It
IntroductionThis article is my first one for quite a while,
there is a reason for that but I will not bore you all with
that.Anyway I have written this article after a little time off. So
what does it do, what is this article about?
I decided to spend a bit of time to learn a bit more about a
popular web MVC framework by our friends atGoogle called
Angular.js, which is a JavaScript MVC framework, which is a bit of
departure for me from my usualXAML influenced world. It is however
good to try things out to get an appreciation of how you would do
thingsin different languages/environments (my old mentor Fredrik
Bornander (AKA the Swede) told me that), so Idecided to take
Angular.js for a spin.
This article will talk about some of the fundamental ideas
behind Angular.js, and shall then focus on the specificsof the demo
application that I have created to go along with this article.
Before we get into the actual article I will just breifly (not
too techie just yet, though I know you lot will want tosee that,
and it will come don't worry) talk about what the demo app does in
plain terms, so you know how todrive the thing yourself when you
download it.
The Demo App OverviewThere are 2 parts to the attached demo
app
Publisher
This is a standard WPF project, as such produces a EXE file that
can be run. I will not be spending too muchtime talking about the
publisher in this article, as it is not the important part of the
article, it is simply a vehicleto demonstrate stuff within the
Angular.js web site. Anyway what the the publisher does it to allow
user to clicka image, when the user clicks an image a message is
sent to the Angular.js web site using web sockets (more onthis
later). In a nutshell that is all the publisher does.
Website
The Angular.js web site, is where the fun stuff happens (at
least in my opinion). The Angular.js web siteessentially carries
out these tasks
When on the root page, the Angular.js will listen to messages
sent via the WPF publisher over a websocket, which then gets
broadcast internally using the Reactive Extensions for JavaScript
to anyoneinterested, which in this article is essentially just the
root page.
The root page will display an image tile for each allowable
message received. The image tile may be resized anddragged around
thanks to some jQuery UI love. The user may then choose to save the
image tile to their
5.00 (42 votes)
Sign up for our free weekly Web Developer Newsletter.
articles
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
1 / 14
-
Directives
Displaying a Custom Dialog inWPF Before the MainApplication
Window is Shown
Examples to create yourConferencing System in .NET,C# VOIP &
Video ConferencingSystems using H.323 and TAPI 3
Working with the Web BrowserControl in Visual Studio 2005
-IE7Clone.
App_Offline.htm
Run only one instance from youprogram
JavaScript Frameworks andResources
Sending and playingmicrophone audio over network
Spell checking in MicrosoftAccess 2003 applications
Debug your ASP.NETApplication while Hosted on IIS
Carcassonne scoring boardapplication
A Walkthrough to ApplicationState
Processing Global Mouse andKeyboard Hooks in C#
How To Create a Self-Restartable Application
Related Research
Fine-Tuning the Engines of SMBGrowth: 4 strategies for
growing
your business
Custom API Management forthe Enterprise: Learn how to
build a successful API strategy[Webinar]
favourites, which will cause ALL the information about the image
tile to be saved to HTML 5 local storage. Thisinformation includes
size, position etc etc, so when the user comes back to the root
page, their favourites (theones they saved) should appear exactly
as they were before. The user may also decide to remove image
tilesfrom their favourites from the root page.
The user may also choose to navigate to a favourites page that
will display some thumbnail images oftheir HTML 5 local storage
persisted favourites. These thumbnails may be clicked on to show a
prettystandard ColorBox (Lightbox etc etc type thing) jQuery
plugin.
The user may also choose to a view static about page, which I
simply added to make enough routes tomake things more worthwhile
when demonstating the routing within Angular.js
So in plain terms that is all there is to it, this image may
help to solidify what I just stated in words, picture says1000nd
words and all that:
This is what the 2 parts of this articles demo code should look
like when they are running correctly:
Click image for larger version
IMPORTANT NOTE:
You should really ensure that you follow these steps to run the
demo code successfully
1. If you find that some of the Publisher.Wpf projects
references can not be found, I haveincluded them in a "Lib" folder
where you can simply re-reference them from
2. You should ensure that the Publisher.Wpf project is run up
first and that it is displaying allthe images. This can be done by
building the Publisher.Wpf project to an EXE and simplyfinding the
EXE in your file system and double clicking it to run (or use
Visual Studio to runup an instance in DEBUG)
3. You should then run the Angular web site, make sure that you
have Index.html set as startpage in Visual Studio and then use
Visual Studio to run the Angular web site
Angular.Js IntroductionIn this section I will discuss some of
the BASICS of working with Angular.js. This section will be a
mixture of myown words, and text lifted directly from the
Angular.js web site. I will not be covering everything that
Angular.jsdoes, as that would be more like a book really, and I
just do not have that much time. I will however becovering some of
the basic Angular.js building blocks, so should you read this
article and think "mmm...ThisAngular stuff intrigues me, where can
I learn more", by following the the hyperlinks of course
AppsEach Angular.js application will at some point need to use a
Angular.js ng-app binding within the html, or viasome code that
does the same job as the declaritive html binding. This code
essentialy bootstraps Angular.jsand lets it know what context the
application is running in. For example you may have the following
code:
Co llapse | Copy Code
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
2 / 14
-
{{name}} This is not using the angular app, as it is not within
the Angular apps scope
It can be seen that we can tell a particular section of the html
to act as an Angular.js application. In this examplewhat this means
is that the div with id="inner" WILL have a section of the html
(though there is nothing tostop you making the actual Body tag be
the angular app) that is considered to be the Angular.js
application,and as such will have full access to the Angular.js
application features (which we will discuss below).
Whilst the div with the id="outer" WILL NOT be considered to be
part of the Angular.js application, and assuch WILL NOT have ANY
access to the Angular.js application features (which we will
discuss below).
ServicesServices in Angular.js are much the same as they are in
WinForms/WPF or Silverlight. They are little helperclasses that may
provide functionality that could be used across the application. In
strongly typed languagessuch as C# we would typically make these
services implement a particular interface and inject them
(viaconstructor or property injection) into our application code.
We would then be able to provide fakes/mocks ofthese services
within our tests, or provide alternative versions if the underlying
system changes (for exampleswapping from Mongo DB to Raven DB
storage)
Whilst Angular.js doesn't support interfaces (though you could
use TypeScript for that) it does support theinjection of real/fakes
services into its code. In fact I would say that one of Angular.js
main points is it supportsIOC out of the box.
ModulesMost applications have a main method which instantiates,
wires, and bootstraps the application. Angularapps don't have a
main method. Instead modules declaratively specify how an
application should bebootstrapped. There are several advantages to
this approach:
The process is more declarative which is easier to understandIn
unit-testing there is no need to load all modules, which may aid in
writing unit-tests.Additional modules can be loaded in scenario
tests, which can override some of the configuration andhelp
end-to-end test the applicationThird party code can be packaged as
reusable modules.The modules can be loaded in any/parallel order
(due to delayed nature of module execution).
http://docs.angularjs.org/guide/module
The recommended approach is to actually split up your modules,
such that you may have a structure like this:
A services moduleA directives moduleA filters moduleApplication
module(s)
This is how you might define a Angular.js module, the give away
is the use of the function "module" thatAngular.js provides
Co llapse | Copy Code
angular.module('xmpl.service', []). value('greeter', {
salutation: 'Hello', localize: function(localization) {
this.salutation = localization.salutation; }, greet: function(name)
{ return this.salutation + ' ' + name + '!'; } }). value('user', {
load: function(name) { this.name = name; } });
Dependency InjectionAngular.js was built with dependency
injection (IOC) in mind, as such a lot of the infrastructure may
beswapped out for mocked versions, or controllers could be tested
using mocked services. Showing how to dothis, or how to test
Angular.js applications in not in the scope of this article. If you
want to know that, visit theAngular.js docs, or get a book.
Sorry
Following on from the previous module example, this is what a
module might look like that took somedependencies, in this case a
module that we just defined above, where we use the 2 values
'greeter' and'user' which are both functions available within the
'xmpl.service' module. This module could be suppliedwith a mocked
version of the 'xmpl.service' module.
Co llapse | Copy Code
angular.module('xmpl', ['xmpl.service']). run(function(greeter,
user) { // This is effectively part of the main method
initialization code greeter.localize({ salutation: 'Bonjour' });
user.load('World'); })
RoutingAngular.js is primarily a single page application
framework, and as such has the concept of view templates thatcan be
applied in response to a certain route being requested.
Routing in Angular.js is actually not that different to routing
in things like ASP MVC or even node.js for thatmatter.
Routing is accomplished by using a prebuild service called
$routeProvider, which comes for free as part ofAngular.js. It
allows the user to configure their routes using a very simple API,
which boils down to these 2functions
1. when(path, route)
Where the the route object has the following properties
controllertemplatetemplateUrl
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
3 / 14
-
resolveredirectToreloadOnSearch
2. otherwise(params)
Here is a little example
Co llapse | Copy Code
$routeProvider .when('/products', { templateUrl:
'views/products.html', controller: 'ProductsCtrl' })
.when('/about', { templateUrl: 'views/about.html' }) .otherwise({
redirectTo: '/products' });;
ViewsThere is not too much to say about the view. We have all
probably come across html before. That is what theviews contain.
The only difference being that Angular.js views will contain
additional (non standard html)bindings that allow the view template
to display data from a Angular.js scope object. The scope object
wouldtypically come from a controller (though it is not limited to
coming from a controller, it could be inherited, or becreated via a
directive say).
Here is a small example of a view, notice the bindings used in
there, such as the use of ng-model and ng-repeat and also the usage
of some of Angular.js pre-built fiters, nameley filter and orderBy
(Please note Iwill not be covering filters in this article)
Co llapse | Copy Code
Search: Sort by:
Alphabetical Newest
{{phone.name}} {{phone.snippet}}
ControllersControllers are used to define the scope for the
views. Scope can be thougt of as the variables and functionsthat
the view may use, say by using a ng-click binding. Here is the
controller code that goes with the viewtemplate that we just
saw.
Co llapse | Copy Code
function PhoneListCtrl($scope) { $scope.phones = [ {"name":
"Nexus S", "snippet": "Fast just got faster with Nexus S.", "age":
0}, {"name": "Motorola XOOM with Wi-Fi", "snippet": "The Next, Next
Generation tablet.", "age": 1}, {"name": "MOTOROLA XOOM",
"snippet": "The Next, Next Generation tablet.", "age": 2} ];
$scope.orderProp = 'age';
It can be seen that the controller defines the following 2 scope
properties
phones : which is a JSON arrayorderProp : which is a single
string value
ScopeScope is the glue that allows the view and the controllers
defined scope object properties/function to bindtogether. If you
have ever done any XAML based tech such as WPF/Silverlight/WinRT
you can think of scope asa DataContext. In fact there are quite a
few UI frameworks that have a scope like concept. XAML
technologieshave DataContext which would typically be a ViewModel,
whilst another popular MVVM JavaScript libraryKnockout.js also has
the idea of scope, and heirarchical scope, which is accessable in
binding(s) within the htmlusing various prebuilt key words.
Angular.js also supports nested/heirarchical scopes, which can
get a bit confusing at times. I personally foundthat one of the
best ways to work with Angular.js and its scopes is to install the
Batarang Chrome addin, whichhas a nice way of allowing youto drill
into scopes using a scope inspector (kind of like Snoop for WPF
orSilverlightSpy for Silverlight)
This diagram may help solidify the concept of
view-controller-scope.
Click image for larger version
Directives
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
4 / 14
-
Angular.js makes use of a pretty novel concept, which are known
as directives. Directives are clever chaps, thatactually allow you
to create extra attributes, or even new DOM fragments. This is all
controlled by applyingcertain constraints to a directive, such that
you may wish to state that a certain directive may only be used
asan attribute, or that it can only be used as an element. You can
sort of think of directives as custom controls.
Directives also follow the normal Angular.js rules, in that they
support dependency injection, and they are alsoscope aware.
One of the best bits of information I came across when writing
this article was this one by Bernardo Castilho
:http://www.codeproject.com/Articles/607873/Extending-HTML-with-AngularJS-Directives,
I urge you to readthat, it is an excellent article, and by the end
of it you will totally get directives.
Anyway that concludes the Angular.js basics, it's now on with
the actual demo application code walkthrough.
The PublisherAs I have previously stated the publisher is a WPF
application (as such it is a runnable EXE), that is not really
themain thrust of this article. The main points about the publisher
are:
1. That is uses the awesome Fleck WebSocket library to talk to
the Angular.js web site2. That is has a Win8 type panorama, so you
can scroll around using the mouse
Here is what the WPF publisher should look like when it is
running:
Now on to the important part, the web socket code.
Co llapse | Copy Code
public class WebSocketInvoker : IWebSocketInvoker{ List
allSockets = new List(); WebSocketServer server = new
WebSocketServer("ws://localhost:8181");
public WebSocketInvoker() { FleckLog.Level = LogLevel.Debug;
server.Start(socket => { socket.OnOpen = () => {
Console.WriteLine("Open!"); allSockets.Add(socket); };
socket.OnClose = () => { Console.WriteLine("Close!");
allSockets.Remove(socket); }; socket.OnMessage = Console.WriteLine;
}); }
public void SendNewMessage(string jsonMessage) { foreach (var
socket in allSockets) { socket.Send(jsonMessage); } }}
This is all there is to that part actually, pretty cool huh (all
thanks to the Fleck WebSocket library). Now I realisethat there may
be some of you out there that are like why didn't you use SignalR,
well I could have, but thatwould have been a pretty different
article, for this one I wanted to concentrate purely on the web
client side ofthings, so chose to use a raw web socket, and the
Fleck WebSocket library fits that bill perfectly.
In this code the SendNewMessage will be called when a user
clicks an image, and the name of the imageclicked will be sent via
the web socket to the Angular.js web site. The Angular.js web site,
has a copy of all thepossible images, as I did not want to get into
complex POST operations of files, and obviously a web servercan't
show a local file (which would be a security risk in my (and any
others) opinion), so I opted for shared filesthat the publisher and
the Angular.js web site both know about for the purpose of this
demo application/article.
The Angular.js WebsiteThis section will discuss the nitty gritty
about the attached demo code Angular.js web site. Hopefully if you
havegot to this point, some of the stuff I mentioned above will
begin to make sense when you see some code.
Require.js Usage
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
5 / 14
-
Before I starting looking into Angular.js I was looking into
using Require.js, which is a module loadingframework for
JavaScript, which allows you to specify your dependencies and
preferred module load order. Iwrote about this in another article,
which you can read here: Modular Javascript Using Require.Js
I have kind of built apon that a bit more in this article (with
a little bit of a kick start with the source code thatcomes with
the Angular.js O'Reilly book :
https://github.com/shyamseshadri/angularjs-book)
So that is where you can do more reading around this subject if
you want to, but let's crack on and see whatthe Require.js element
of the attached Angular.js website looks like shall we.
It starts with this sort of code in the main Angular.js page
(Index.html):
See index.html
Co llapse | Copy Code
.....
This is standard Require.js code that tells Require.js which is
the main bootstrap code file that it should run. Itcan be seen that
this is scripts/main, so let's have a look at that now shall we
See scripts\main.js
Co llapse | Copy Code
// the app/scripts/main.js file, which defines our RequireJS
configrequire.config({ paths: { angular: 'vendor/angular.min',
jqueryUI: 'vendor/jquery-ui', jqueryColorbox:
'vendor/jquery-colorbox', jquery: 'vendor/jquery', domReady:
'vendor/domReady', reactive: 'vendor/rx' }, shim: { angular: {
deps: ['jquery', 'jqueryUI', 'jqueryColorbox'], exports: 'angular'
}, jqueryUI: { deps: ['jquery'] }, jqueryColorbox: { deps:
['jquery'] } }});
require([ 'angular', 'app', 'domReady', 'reactive',
'services/liveUpdatesService', 'services/imageService',
'services/localStorageService', 'controllers/rootController',
'controllers/favsController', 'directives/ngbkFocus',
'directives/draggable', 'directives/resizable',
'directives/tooltip', 'directives/colorbox' // Any individual
controller, service, directive or filter file // that you add will
need to be pulled in here.], function (angular, app, domReady) { .
. . .
});
There is quite a bit going on here.It does however boil down to
3 parts
1. We configure Require.js with the paths of where the
JavaScript files are2. We configure a preferred load order for
Require.js by using the Require.js shim. The shim essentially
sets
up the dependencies for the libraries to load3. We then use
Require.js [Require] to tell the Angular.js application what
dependencies we would like to be
satisfied.
I have also used Require.js to satisfy the demo applications
controller requirements. An example of which is asfollows:
Co llapse | Copy Code
define(['controllers/controllers', 'services/imageService',
'services/utilitiesService', 'services/localStorageService'],
function (controllers) { controllers.controller('FavsCtrl',
['$window', '$scope', 'ImageService', 'UtilitiesService',
'LocalStorageService', function ( $window, $scope, ImageService,
UtilitiesService, LocalStorageService) { ...... ...... ......
...... ......
}]);});
The Main App SetupSee scripts\main.js
Now that you have seen the main bootstrapping code (which is
mainly taken up by the Require.jsconfiguration), lets just have a
quick look at the actual Angular.js bootstrapping bit.
This is the bit that we discussed right at the start of this
article, you know the bit that actual makes theattached code an
"Angular.js" application.
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
6 / 14
-
That part is as follows:
Co llapse | Copy Code
function (angular, app, domReady) { 'use strict';
app.config(['$routeProvider', function ($routeProvider) { .... ....
.... }]); domReady(function () { angular.bootstrap(document,
['MyApp']);
// The following is required if you want AngularJS Scenario
tests to work $('html').addClass('ng-app: MyApp'); });}
This bootstrapping does 2 things:
1. It sets up the available valid routes, which we will be
looking at next2. It relies on a special Angular.js addin called
"DomReady" which works much the same way as jQuery and
its ready() event. After the dom is ready the "HTML" element is
attributed up to make it act as theAngular.js application.
There is also the question of where the "MyApp" module comes
from. Who creates that prior to it beingbootstrapped here?
The answer to that is that is lives in its own file "app.js"
which looks like this
See scripts\app.js
Co llapse | Copy Code
// The app/scripts/app.js file, which defines our AngularJS
appdefine(['angular', 'controllers/controllers',
'services/services', 'filters/filters', 'directives/directives'],
function (angular) { return angular.module('MyApp', ['controllers',
'services', 'filters', 'directives']);});
The RoutingSee scripts\main.js
For the demo application there are 3 valid routes
Root/Favs/About. Each of these is configured using thestandard
Angular.js $routeProvider service, where all the set up code is
done within the boostrapping filemain.js.
Co llapse | Copy Code
unction ($routeProvider) { $routeProvider .when('/', {
templateUrl: 'views/root.html', controller: 'RootCtrl' })
.when('/favs', { templateUrl: 'views/favs.html', controller:
'FavsCtrl' }) .when('/about', { templateUrl: 'views/about.html'
}).otherwise({ redirectTo: '/' });;}
I think it is pretty obvious from that there are 3 routes as
stated above and how they are configured. So I wilnot say any more
on that.
The Root PageThis is the most complex page within the demo web
site I have put together, as it brings in lots of differentthings
together.
So what does this page do exactly?
The idea is that there is a service called "LiveUpdatesService"
that listens to the client end of the WebSocket that the WPF
publisher is pushing data out on. The LiveUpdatesService uses the
reactive extensionsfor JavaScript to provide a stream of data that
may be subscribed to.
The root page will subscribe to this pulished stream, and every
time it sees a new entry it will add a new jQueryUI
Draggable/Resizable UI element, providing it has not already got an
element with the same image nameshown.
It also allows the user to save the images to HTML 5 local
storage and to remove them from local storage too.If there are
items aready within local storage theirs details are used as the
initial start state for the Root page. Ihave to say this looks
cool, as all the information is persisted, so it remembers the
sizes, locations, ZIndex, so itcomes back exactly how you saved
it.
So in general terms that is what the root page does.
This is what the root page looks like
Click image for larger version
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
7 / 14
-
So that is what it looks like, want to see some code?
LiveUpdatesService
This is the service that listens for the incoming data from the
publisher web socket, and also pushes the newlyrecieved web socket
data out, via a reactive extensions for JavaScript Subject object.
Here is the code for theservice:
Co llapse | Copy Code
define(['services/services'], function (services) {
services.factory('LiveUpdatesService', ['$window', function (win)
{
var subject = new Rx.Subject(); if ("WebSocket" in window) { //
create a new websocket and connect var ws = new
WebSocket('ws://localhost:8181/publisher', 'my-protocol');
// when data is comming from the server, this metod is called
ws.onmessage = function (evt) { subject.onNext(evt.data); };
// when the connection is established, this method is called
ws.onopen = function () { win.alert('Websocket connection opened');
};
//// when the connection is closed, this method is called
ws.onclose = function () { subject.onError('Websocket connection
closed, perhaps you need to restart the Publisher, and refresh web
site'); }; }
return {
publishEvent: function (value) { subject.onNext(value); },
eventsStream: function () { return subject.asObservable(); } };
}]); });
And here is the root controller code that make use of the
reactive extension Subjects stream, where we firstcheck to see
whether we have seen an item with the same name before, and if we
have simply show a messageto the user (note that we DO NOT use
window directly, but rather use a $window angular service (which
maybe more easily mocked)).
If we have not seen the image name before, a new item is created
using the ImageService which we positionrandomly
Co llapse | Copy Code
LiveUpdatesService.eventsStream().subscribe( function (data) {
if ($location.path() == '/') { var idx =
$scope.imageitems.propertyBasedIndexOf('name', data); if (idx >=
0) { $window.alert('An item with that name has already been
added'); } else { var randomLeft =
UtilitiesService.getRandomInt(10, 600); var randomTop =
UtilitiesService.getRandomInt(10, 400); var randomWidth =
UtilitiesService.getRandomInt(100, 300); var randomHeight =
UtilitiesService.getRandomInt(100, 300);
$scope.imageitems.push(ImageService.createImageItem( data,
randomLeft, randomTop, randomWidth, randomHeight, false));
$scope.$apply(); } } }, function (error) { $window.alert(error);
});
LocalStorageService
This service is responsible for persisting/fetching data items
from HTML 5 local storage. I think this code ispretty self
explanatory, so I will leave it at that:
Co llapse | Copy Code
define(['services/services'], function (services) {
services.factory('LocalStorageService', [ function () {
return { isSupported: function () { try { return 'localStorage'
in window && window['localStorage'] !== null; } catch (e) {
return false; } }, save: function (key, value) { localStorage[key]
= JSON.stringify(value); }, fetch: function (key) { return
localStorage[key]; }, parse: function(value) { return
JSON.parse(value); },
clear: function (key) { localStorage.removeItem(key); } }; }]);
});
ImageService
This service simply aids in the creation of ImageItem objects
for the Root page and FavItem objects for theFavs page.
Co llapse | Copy Code
function ImageItem(name, left, top, width, height, isFavourite)
{
var self = this;
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
8 / 14
-
self.name = name; self.left = left; self.top = top; self.width =
width; self.height = height; self.isFavourite = isFavourite;
self.styleProps = function () { return { left: self.left + 'px',
top: self.top + 'px', width: self.width + 'px', height: self.height
+ 'px', position: 'absolute' }; }; return self;};
function FavImageItem(name) {
var self = this; self.name = name; return self;};
define(['services/services'], function (services) {
services.factory('ImageService', [ function () { return {
createImageItem: function (name, left, top, width, height,
isFavourite) { return new ImageItem(name, left, top, width, height,
isFavourite); }, createFavImageItem: function (name) { return new
FavImageItem(name); } }; }]); });
One important thing to note is how the dynamic CSS is done in
Angular.js. To achieve dynamic CSS that isupdated when objects
change you need to provide a function to call, which is what you
can see here in thestyleProps() function:
Co llapse | Copy Code
self.styleProps = function () { return { left: self.left + 'px',
top: self.top + 'px', width: self.width + 'px', height: self.height
+ 'px', position: 'absolute' };};
The markup that uses this as follows, which means when ever an
update is done on the JSON object the CSS isupdated too, and the
html reflects this. This was not that easy to find out, so make
sure you read this bit a fewtimes, solidify that knowledge, wedge
it in there good and hard
Co llapse | Copy Code
ng-style="imageitems[$index].styleProps()"
UtilitiesService
This service provides the following functions:
Adds a propertyBasedIndexOf() to arrays, which allows an array
to be searched for a particular itemproperty where the index will
be returnedgetRandomInt() : which is used to get a random x/y point
to place new image items at the 1st timethey are
showndelayedAlert() : shows an alert after some delay time
Here is the code:
Co llapse | Copy Code
define(['services/services'], function (services) {
services.factory('UtilitiesService', [ function () {
var initialised = false;
return { addArrayHelperMethods: function () { if (!initialised)
{ initialised = true; Array.prototype.propertyBasedIndexOf =
function arrayObjectIndexOf(property, value) { for (var i = 0, len
= this.length; i < len; i++) { if (this[i][property] === value)
return i; } return -1; }; } }, getRandomInt: function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; },
delayedAlert: function(message) { setTimeout(function () {
$window.alert(message); }, 1000); } }; }]); });
Draggable Directive
To achieve the dragging I already knew I had to use the jQuery
UI library, but when working with Angular.js,there is a definate
Angular.js way, and littering your controller code with DOM
changing jQuery code is mostcertainly NOT the Angular.js way. So
what option does that leave us. Well that is really where
Angular.jsdirectives fit, they are designed to replace and enhance
the DOM, that is what directives do best.
So anytime you need to alter the DOM directly (not through scope
changes) you should be thinking aboutusing Angular.js
directives.
So all that said, it turs out to be a simple matter to create a
small jQuery UI Angular.js directive, which can beseen in the ....
table tags in the markup.
Here is the draggable directives code, where you can see that
this is restricted to attribute usage, and simplydelegates the work
to the actual jQuery UI (which was referenced as a requirement
within the Require.jsconfiguration, so we know its loaded ok,
otherwise the Angular.js would not have started, as it has jQuery
UI as
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
9 / 14
-
a dependency within the Require.js configurtion).
One thing worth mentioning here is that once the drag has
finished I wanted to inform the controller of thenew position
values, such that they could be reflected in the styling. Now since
the positioning is done outsideof an Angular.js controllers scope
(as it is done via the inbuilt jQuery UI code), we need to get the
draggabledirective to update the controller scope, such that it
knows something outside of it has changed one of itsvariables.
Luckily the jQuery UI draggable widget provides a nice callback
that we can make use of, which weuse and then tell the Angular.js
controller's scope that something has changed, this is done using
the Angular.js$scope.apply() which is used for this purpose
Co llapse | Copy Code
define(['directives/directives'], function (directives) {
directives.directive('draggable', ['$rootScope', function
($rootScope) { return { restrict: 'A', //may need the model to be
passed in here so we can apply changes to its left/top positions
link: function (scope, element, attrs) {
element.draggable( { stop: function (event, ui) {
scope.$apply(function() { scope.updatePosition(
scope.imageitem.name, { left: ui.position.left, top:
ui.position.top } ); }); } }); } }; }]);});
Where this is the controller code that gets called when the
directive calls into the controller's scope
Co llapse | Copy Code
// NOTE: $scope.$apply is called by the draggable
directive$scope.updatePosition = function (name, pos) { var idx =
$scope.imageitems.propertyBasedIndexOf('name', name); var foundItem
= $scope.imageitems[idx]; foundItem.left = pos.left; foundItem.top
= pos.top;};
Resizable Directive
The resizable directive works much the same as the draggable
directive, it is another jQuery UI based Angular.jsdirective. Here
is its code:
Co llapse | Copy Code
define(['directives/directives'], function (directives) {
directives.directive('resizable', ['$rootScope', function
($rootScope) { return { restrict: 'A', //may need the model to be
passed in here so we can apply changes to its left/top positions
link: function (scope, element, attrs) {
element.resizable( { maxHeight: 200, minHeight: 100,
//aspectRatio: 16 / 9, stop: function (event, ui) {
scope.$apply(function () { scope.updateScale( scope.imageitem.name,
{ top: ui.position.top, left: ui.position.left }, { width:
ui.size.width, height: ui.size.height } ); }); } }); } };
}]);});
As before since we are changing things (the scale of a UI
element) outside the knowledge of the Angular.jscontroller, we need
to get the directive to update the controller scope, here is the
relevant code:
Co llapse | Copy Code
// NOTE: $scope.$apply is called by the resizable
directive$scope.updateScale = function (name, pos, size) { var idx
= $scope.imageitems.propertyBasedIndexOf('name', name); var
foundItem = $scope.imageitems[idx]; foundItem.left = pos.left;
foundItem.top = pos.top; foundItem.width = size.width;
foundItem.height = size.height;};
Root Controller
Some of the internals of the root controller have already been
covered, so I will remove the internal code fromthe functions we
have already covered, which really just leaves this controller
code:
Co llapse | Copy Code
define(['controllers/controllers',
'services/liveUpdatesService', 'services/utilitiesService',
'services/imageService', 'services/localStorageService'], function
(controllers) { controllers.controller('RootCtrl', ['$window',
'$scope', '$location', 'LiveUpdatesService', 'UtilitiesService',
'ImageService', 'LocalStorageService', function ( $window, $scope,
$location, LiveUpdatesService, UtilitiesService, ImageService,
LocalStorageService) {
$scope.imageitems = [];
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
10 / 14
-
$scope.imageItemsStorageKey = 'imageItemsKey'; //load existing
items from local storage which looks cool, as they show up in their
persisted //positions again...Cool if
(LocalStorageService.isSupported()) { var currentFavs =
LocalStorageService.fetch($scope.imageItemsStorageKey); if
(currentFavs != undefined) { currentFavs = JSON.parse(currentFavs);
for (var i = 0; i < currentFavs.length; i++) { var favItem =
currentFavs[i];
$scope.imageitems.push(ImageService.createImageItem(
favItem.name, favItem.left, favItem.top, favItem.width,
favItem.height, true)); } } }
UtilitiesService.addArrayHelperMethods();
LiveUpdatesService.eventsStream().subscribe( ..... .....
.....
);
$scope.addToFavourites = function (index) { if
(!LocalStorageService.isSupported()) { $window.alert('Local storage
is not supported by your browser, so saving favourites isn\'t
possible'); } else { var currentStoredFavsForAdd =
LocalStorageService.fetch($scope.imageItemsStorageKey); if
(currentStoredFavsForAdd == undefined) { currentStoredFavsForAdd =
[]; } else { currentStoredFavsForAdd =
JSON.parse(currentStoredFavsForAdd); }
var scopeImageItem = $scope.imageitems[index]; var favsIdx =
currentStoredFavsForAdd.propertyBasedIndexOf('name',
scopeImageItem.name); if (favsIdx >= 0) { $window.alert('An item
with that name is already in your favourites.'); return; }
$scope.imageitems[index].isFavourite = true;
currentStoredFavsForAdd.push(scopeImageItem);
LocalStorageService.save($scope.imageItemsStorageKey,
currentStoredFavsForAdd); $window.alert('Saved to favourites'); }
};
$scope.removeFromFavourites = function (index) { if
(!LocalStorageService.isSupported()) { $window.alert('Local storage
is not supported by your browser, so removing from favourites
isn\'t possible'); } else { var currentStoredFavsForRemoval =
LocalStorageService.fetch($scope.imageItemsStorageKey); if
(currentStoredFavsForRemoval == undefined) { return; } else {
currentStoredFavsForRemoval =
JSON.parse(currentStoredFavsForRemoval); }
var scopeImageItem = $scope.imageitems[index];
var favsIdx =
currentStoredFavsForRemoval.propertyBasedIndexOf('name',
scopeImageItem.name); $scope.imageitems.splice(index, 1); if
(favsIdx >= 0) { currentStoredFavsForRemoval.splice(favsIdx, 1);
LocalStorageService.save($scope.imageItemsStorageKey,
currentStoredFavsForRemoval); } $window.alert('Item removed from
favourites'); } };
// NOTE: $scope.$apply is called by the draggable directive
$scope.updatePosition = function (name, pos) { ..... ..... .....
};
// NOTE: $scope.$apply is called by the resizable directive
$scope.updateScale = function (name, pos, size) { ..... ..... .....
}; }]);});
It can be seen that most of the root controller's code has
already been covered, so what is left to discuss?
Essentially the code that is left does the the following:
1. Allows the user to call the addToFavourites function (which
will add it to HTML 5 local storage) byusing a button in the image
tile UI
2. Allows the user to call the removeFromFavourites function
(which will remove it from the UI and alsofrom HTML 5 local
storage) by using a button in the image tile UI
3. Will read all items and their persisted state from HTML 5
local storage when the page is first rendered,which will cause all
the persisted favourite items to appear exactly as they were when
the user savedthem to local storage
Root View
The view is the easy part, since most of the real work has been
done by the various services and the controller.Here is the root
view markup:
Co llapse | Copy Code
{{imageitem.name}}
-
The Favourites PageThough not as complex as the Root page and
its controller the favourites is the 2nd most complicated page,
soprobably justifies a bit of an explanation, before we dive head
long in to its code.
So what does this page do exactly?
The idea is that there will be a set (which could be an empty
set) of image data which is stored against a certainkey in HTML 5
local storage. When the favourites view is requested this HTML 5
locally stored data will beexamined, and for all items found a smal
thumbnail will be rendered. The user may also click on any of
thethumbnails to launch a ColorBox jQuery plugin.
This is what the favourites page looks like with some items
saved within the local storage.
Click image for larger version
And this is what it would look like when you have clicked one of
the thumnbails
So how does this page work. Well one of the hard parts was
something that you would all probably think was avery trivial thing
to do. So in local storage we have a 1 dimensional JSON stringified
array, and I wanted to turnthat into a 2 dimensional table layout
that I could use with Angular.js ng-repeat binding.
Favs Controller
Let's look at the controller first.
Co llapse | Copy Code
define(['controllers/controllers', 'services/imageService',
'services/utilitiesService', 'services/localStorageService'],
function (controllers) { controllers.controller('FavsCtrl',
['$window', '$scope', 'ImageService', 'UtilitiesService',
'LocalStorageService', function ( $window, $scope, ImageService,
UtilitiesService, LocalStorageService) {
$scope.imageItemsStorageKey = 'imageItemsKey';
$scope.favImageItems = []; $scope.columnCount = 5; $scope.favText =
''; $scope.shouldAlert = false;
$scope.tableItems = []; while ($scope.tableItems.push([]) <
$scope.columnCount);
if (!LocalStorageService.isSupported()) { $scope.favText =
'Local storage is not supported by your browser, so viewing
favourites isn\'t possible'; $scope.shouldAlert = true; } else
{
var currentStoredFavs =
LocalStorageService.fetch($scope.imageItemsStorageKey); var
currentFavs = []; if (currentStoredFavs != undefined) { currentFavs
= JSON.parse(currentStoredFavs); }
if (currentFavs.length == 0) { $scope.favText = 'There are no
favourites stored at the moment'; $scope.shouldAlert = true; } else
{ var maxRows = Math.ceil(currentFavs.length / $scope.columnCount);
$scope.favText = 'These are your currently stored favourites. You
can click on the images to see them a bit larger'; if
(currentFavs.length < $scope.columnCount) { $scope.tableItems[0]
= []; for (var i = 0; i < currentFavs.length; i++) {
$scope.tableItems[0].push(ImageService.createFavImageItem(currentFavs[i].name));
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
12 / 14
-
} } else { var originalIndexCounter = 0; for (var r = 0; r <
maxRows; r++) { for (var c = 0; c < $scope.columnCount; c++) {
if (originalIndexCounter < currentFavs.length) {
$scope.tableItems[r][c] =
ImageService.createFavImageItem(currentFavs[originalIndexCounter].name);
originalIndexCounter++; } } } } } if ($scope.shouldAlert) {
UtilitiesService.delayedAlert($scope.favText); } } }]);});
It can be seen that the bulk of the work here is getting the
data from HTML 5 local storage, and translating itfrom a string
representation into a JSON 1 dimensional array, and then into a 2
dimenionsal stucture that canbe used to bind against within the
markup.
There are a few other things of note here:
We use the Angular.js $window rather than "window" to allow the
$window service to be replaced by amockWe make use of the
LocalStorageService that we saw earlierWe make use of the
UtilitiesService that we saw earlierWe make use of the ImageService
that we saw earlier
Favs View
With all the grunt work done in the controller, the view markup
is pretty tiny:
Co llapse | Copy Code
Favourites
{{favText}}
Notice that neat nested ng-repeat, that is how easy it is to do
a table layout in Angular.js once you have thecorrect structure in
your scope to iterate over
ColorBox Directive
The final piece in the puzzle is how to make these items into a
jQuery ColorBox. Knowing what we know now,we should be able to
realise that the answer to this lies in the use of yet another
directive.
Yes you guessed it a colorbox directive, which can be seen in
the .... anchor tags in themarkup.
Here is the colorbox directives code, where you can see that
this is restricted to attribute usage, and simplydelegates the work
to the actual jQuery ColorBox (which was referenced as a
requirement within the Require.jsconfiguration, so we know its
loaded ok, otherwise the Angular.js would not have started)
Co llapse | Copy Code
define(['directives/directives'], function (directives) {
directives.directive('colorbox', ['$rootScope', function
($rootScope) { return { restrict: 'A', //may need the model to be
passed in here so we can apply changes to its left/top positions
link: function (scope, element, attrs) { $(element).colorbox({ rel:
'group3', transition: "elastic", width: "50%", height: "50%" }); }
}; }]);});
The About PageThe about page is just static text, so nothing
groovy here really. I added this page just so there was
enoughroutes in the demo app to make it more full featured I
suppose. For completeness here is the screen shots ofwhat the about
page looks like
"
Click image for larger version
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
13 / 14
-
Permalin k | Advertise | Privacy | Mob ile W eb03 | 2.7.131126.1
| Last U pdated 5 Sep 2013
Article Copyrigh t 2013 by Sach a B arberEveryth in g else
Copyrigh t CodePro ject, 1999-2013
Terms o f U se
Layou t: f ixed | f lu id
Sacha BarberSoftware Developer (Senior) United Kingdom
I currently hold the following qualifications (amongst others, I
also studied Music Technology andElectronics, for my sins) - MSc
(Passed with distinctions), in Information Technology for
E-Commerce- BSc Hons (1st class) in Computer Science &
Artificial Intelligence Both of these at Sussex University UK.
Award(s)
I am lucky enough to have won a few awards for Zany Crazy code
articles over the years
Microsoft C# MVP 2013Codeproject MVP 2013Microsoft C# MVP
2012Codeproject MVP 2012Microsoft C# MVP 2011Codeproject MVP
2011Microsoft C# MVP 2010Codeproject MVP 2010Microsoft C# MVP
2009Codeproject MVP 2009Microsoft C# MVP 2008Codeproject MVP
2008And numerous codeproject awards which you can see over at my
blog
Add a Comment or Question Search this forum Go
That's ItAnyway that is all I wanted to say for now, I hope you
have enjoyed this article and got something out of it. Iknow I did
personally. It was a tricky one to write in a lot of ways as it had
a few new concepts for me, I amhowever pretty pleased with the end
result. If you like what you have read/seen, and feel inclined to
leave avote/comment that would be cool.
Anyway bye bye until the next one, which hopefully will not be
as long in coming as this one was.
LicenseThis article, along with any associated source code and
files, is licensed under The Code Project Open License (CPOL)
About the Author
Article Top
Comments and Discussions
Profile popups Spacing Relaxed Noise Very High Layout Normal Per
page 10
Update
First Prev Next
Veronica Blanco 16-Nov-13 10:16
S. M. Ahasan Habib 5-Nov-13 11:50
M Rayhan 27-Oct-13 3:41
Bernardo Castilho 6-Sep-13 11:22
Sacha Barber 6-Sep-13 11:29
saxenaabhi6 5-Sep-13 20:29
Sacha Barber 6-Sep-13 3:34
Marcelo Ricardo deOliveira
5-Sep-13 17:44
Sacha Barber 6-Sep-13 3:33
thangskyline 2-Sep-13 3:53
Last Visit: 31-Dec-99 23:00 Last Update: 29-Nov-13 2:02 Refresh
1 2 3 4 5 Next
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch
threads, Ctrl+Shift+Left/Right to switch pages.
I have a pair of questions, separate the model objects
My vote of 5
My vote of 5
My vote of 5 [modified]
Re: My vote of 5
My vote of 5
Re: My vote of 5
My vote of 5
Re: My vote of 5
Cannot download the source code
Angular.js example application - CodeProject 29-11-2013
http://www.codeproject.com/Articles/637430/Angular-js-example-application
14 / 14
Angular.js example applicationTable Of ContentsIntroductionThe
Demo App OverviewAngular.Js
IntroductionAppsServicesModulesDependency
InjectionRoutingViewsControllersScopeDirectives
The PublisherThe Angular.js WebsiteRequire.js UsageThe Main App
SetupThe RoutingThe Root
PageLiveUpdatesServiceLocalStorageServiceImageServiceUtilitiesServiceDraggable
DirectiveResizable DirectiveRoot ControllerRoot View
The Favourites PageFavs ControllerFavs ViewColorBox
Directive
The About Page
That's ItLicenseAbout the AuthorComments and Discussions