April 04, 2013

Isolated scope listen for ($rootScope) events

Hi guys,
I came across a bitch of a problem where I needed to have an isolated scope (related to a Directive) register itself to listen to events dispatched from the $rootScope.
If you're here, you probably know that in order to listen on events you have to be chained in the scope hierarchy, but hey! no, I want my scope to be isolated. you see? a problem.
So I opened up Fiddle and started messing around trying to find some kind of a workaround, and what do you know, I came up with something which I believe is a good start.
Let me explain the concept and from there you can check out the code -
So, basically I wanted to listen for events which some service dispatches (don't be a smart ass and ask why events, just trust I had my own good reasons...). The thing is I wanted to make sure that once the isolated scope is destroyed, it will not leave it's garbage hanging and it's listeners will die with it.
and so, I'm using the service to add a listener to it. when doing that I'm giving the method 2 arguments - the isolated scope and the callback name. On the service I'm grabbing these arguments and create a listener on the $rootScope with the callback I got. If you look closely on the documentation you'll see that the $on API returns a function which is used to de-registrate for this newly created listener, so I'm keeping a map in my service, where each scope is an object which can have many properties (named by it's callback function name) - each holds the de-registration function for it's listener. cool.

You can see the code here on Fiddle: http://jsfiddle.net/YQcGG/11/

And you can also see it here -
This is the HTML:

And this is the rest of it:
var myApp = angular.module('myApp', []);

myApp.factory('myService', function ($rootScope, $timeout) {
    return {
        listeners: {},
        dispatch: function () {
            $rootScope.$emit('something-changed');
        },

        addListener: function (scope, callbackName) {
            console.log('adding event to', scope.$id);
            var me = this;
            if (!me.listeners[scope.$id]) {
                me.listeners[scope.$id] = {};
            }
            me.listeners[scope.$id][callbackName] = $rootScope.$on('something-changed', scope[callbackName]);
            scope.$on("$destroy", function () {
                for (var item in me.listeners[scope.$id]) {
                    me.listeners[scope.$id][item]();
                }
            });
            console.log($rootScope.$$listeners);
        }
    };
});

myApp.controller('myCtrl', function ($rootScope, $scope, myService) {
    $scope.removeDtrv = function () {
        var div = document.querySelector('my-dtrv');
        var e = angular.element(div);
        e.scope().$destroy();
        e.remove();
        console.log($rootScope.$$listeners);
    };

    $scope.dispatchEvent = function () {
        myService.dispatch();
    };
});


myApp.directive('myDtrv', function (myService) {
    return {
        restrict: 'E',
        scope: {},
        link: function (scope, element, attrs) {
            scope.myCallback = function (e, args) {
                console.log('I got an event', e);
            };
            myService.addListener(scope, 'myCallback');
        },
        template: '
Events to an isolated scope
' }; });

It is not perfect but it is a good start.
Hope this helps you.

Take care and do play some music, ok? :)


March 04, 2013

AngularJS Module Seperation needs help

If you're using AngularJS as your framework of choice you probably know that it is recommended that you separate your modules according to Logic (Directives, Filters, Services) of by view type (i.e. dashboard-module) or combine the 2, but the idea is to have a single main module which depends on these "sub" modules.
This is better for testing since you don't need to load them all if you only going to test one of them, and better for keeping you stuff organized.
Currently, if you wish to create a module that, for instance, holds all the Directives, what you need to do is to have a JS file which defines the module and on top of it, it will create all the Directives with the .directive() factory method, and this is where I find the design a bit lacking, since it's really smells bad to have a big (big) file with all you're directives defined in it. no... I want each Directive to live in it's own file.
So this is where it gets uglier. If I want each Directive to be in it's own file, this means I need to load each of these files on my index.html. Now, some of you might say "Hey! use RequireJS to load them up, dummy!" but I know that RequireJS creates a whole new set of issues with the E2E tests... I really don't want RequireJS.
The direction I went with was to go with other script loaders like $script or yepnope and have them define the scripts to load on each module file - on the Directive module I will define all the directive that needs to be loaded. Doesn't work.
In the meantime, if any of you wants to share his/hers idea - I'll appreciate it :)

Cheers.

February 24, 2013

Twitter - Here I come

So whud'ya know... I'm going Twitter.
I know, I know - I have written these lines (for those of you who don't understand Hebrew, I'm bashing Twitter like hell), but I found myself more and more coming up with short ideas that posting a blog entry for is a bit of an overhead, you know what I mean? I need something I can tap to easily and write a quick thought or revelation that comes to my mind. 140 chars should do the trick, no? :)

So I set myself with a Twitter account and it's here:
https://twitter.com/Flashmattic.

For now, only confirmed followers have access to it... suites me at this point. Not everyone should go there ;)

I hope you'll find the twits interesting and given I'm an easily distracted person, you can expect a lot of them :)

See you (also) there,

Cheers

January 24, 2013

What is a data grid?

I lately found myself explaining to different people what is a data grid, or to be more accurate, what is not a data grid.
There seems to be a common misunderstanding of what a data grid should do and what are it's responsibilities among developers and UX experts equally.
Below I will list what a data grid should not include with hopes it helps you in future debates over one of the most common component in RIA. Of course it really depends on UX requirements in the end, but given your UX is not trying to invent the wheel all over again, the following lines are true to you as well.

A data grid is not responsible for fetching it's data

The data grid should not know where it gets the data from nor the API which is behind the collection it receives. If you want a fully business-detached component there shouldn't be any call to the server from the data grids. This includes refreshing the data of course.

The actions available on the data grid are not a part of the data grid

Add, delete, refresh, export to excel etc. are all actions a user can do on the data of the data grid. The data, not the data grid.
Let's take "add" for example - you create a model, insert it into a collection (sync the server if required) and give the updated collection to the data grid to display it.
I can smell the next question popping - No! a toolbar is not a part of a data grid.

Paging is only triggered by the data grid, not done by it

Some data grids embed paging mechanism, but as mentioned before, grids do not fetch their data, and given that paging is actually fetching the data with params, the grid should not handle the paging but just dispatch an event with the page number, start index and range included. "Someone" else should take this data and fetch the page.

Filtering is not the grid's business

Again, grids do not fetch their data. Filtering is actually fetching a data with constraints to it. Do it elsewhere and bring the results to the grid.

Sorting, in most cases, is done on the server...

I know that most grids support sorting within results, meaning that they sort the results which were already fetched, but in most cases you would like to go to the server and ask for a new sorted data, since the results exceed the page row limit (or lazy load limit). In these cases, the data grid simply tells that one of it's header were clicked, and let's other do the fetching for it.

Bottom line...

The data grid is mainly for displaying data, not messing around with it. pretty simple concept that keeps slipping under UX big mocks. If you keep your grid at doing what it should and nothing more, you will create a more reusable and portable component.

Take care.



January 21, 2013

Flashmattic or JSMattic?

I guess that this post is out for all who have approached me and asked if it wasn't time for me to change my blog name into something more suitable to what I'm actually practicing for the past year or so - JavaScript. Well, it's not really JavaScript, since I played around with all kinds of technologies like GWT, Grails and some libraries on top of JS like ExtJS, but yeah, they had it right - I haven't been practicing (nor preaching) Flex or Flash.

There is no question here. Flashmattic.

Flash, my friends, is a state of mind and I'm sure that all my Flash/Flex mates will agree. The Flash/Flex communities were the most exciting forums I had the privileged to be a part of. Developers who took a relatively simple technology and made amazing stuff with it, including the creation of RIA.

Being a Flash-Developer meant that you had to think outside the box, always looking for ways to amaze people using your products. It is no wonder that the top Flash developers of the time are now among the top JS developers of today, people like Grant Skinner and Jesse Warden bring their creative minds into the not-so-recent-dry-as-F&^k JavaScript realms (Hey, the JS community is so hyped about discovering the MV* patterns in a way that makes any Flex developer ROFL).

So, my friends - This blog's name remains Flashmattic though you will see more and more content which has nothing to do with Flash.

Plus, it simply sounds better :)

Cheers.





November 04, 2012

Grails r:script and g:include - workaround

Hi,
As others did, I also bumped into this mess. I wanted to include a controller view into another view, and have the nested view contain JS which can be used on that view, but r:script and g:include are not best friends.
I found a workaround you might find useful as well. I simply add an empty r:script tag on the parent view (the one who is using the include) and that's it, like this:

Parent view, on the HEAD tag:
 

And in the nested view:

// Script here


Mind you that we're counting on using the main.gsp layout (or some other layout which supports r:script)

October 31, 2012

Flashmattic has a new code highligt

Hi guys,
A small update - I've took the little time I have to update the way the code is presented in my blog. I must say that I should have done this a long time ago...
You can see a sample here.
Now you will be able to read it easier and use it better. Unfortunately I don't have the time or patience to go over previous posts and modify them so you should only expect it from now on.

Cheers