is there a better way to resolve a circular dependency between aop functions and components?

30 views
Skip to first unread message

julian

unread,
Oct 12, 2013, 2:22:01 PM10/12/13
to cuj...@googlegroups.com
hi.

the code below basically keeps a view and collection synced, both ways.
there is a circular dependency (between the two components and 2 aop afterReturnings) that i've resolved.
however, i'd like to know if there's a more elegant, wirejs-oriented, solution. 
the part i feel most uncomfortable about is using 'ready' to finish wiring the aop functions.  that doesn't seem like the ready phase's intent.  or am i mistaken? 
thanks in advance!

julian

// app/afterEpisodeRemove.js
define([], function () {

    "use strict";

    var view,
        fn = function (id) {
            if (id) {
                view.remove(id);
            }
        };
    fn.ready = function (view) {
        view = view;
    };
    return fn;

});

// app/afterEpisodeViewRemove.js
define([], function () {

    "use strict";

    var episodes,
        fn = function (id) {
            if (id) {
                episodes.remove(id);
            }
        };
    fn.ready = function (episodes) {
        episodes = episodes;
    };
    return fn;

});

// app/EpisodeView.js
define([], function () {

    "use strict";

    function EpisodeView () {}

    EpisodeView.prototype = {
        remove: function (id) {
            // remove from DOM.
            return id;
        }
    };

    return EpisodeView;

});


// app/Episodes.js
define([], function () {

    "use strict";

    function Episodes () {}

    Episodes.prototype = {
        remove: function (id) {
            // remove from collection.
            return id;
        }
    };

    return EpisodeView;

});

// spec
define({
    afterEpisodeViewRemove: {
        module: 'app/afterEpisodeViewRemove',
        ready: {
            ready: [{ $ref: 'episodes' }]
        }
    },
    afterEpisodeRemove: {
        module: 'app/afterEpisodeRemove',
        ready: {
            ready: [{ $ref: 'episodeView' }]
        }
    },
    episodeView: {
        create: 'app/EpisodeView',
        on: {
            'first!.episodes': {
                'click:.stop': 'getIdFromElement | remove'
            }
        },
        afterReturning: {
            'remove': 'afterEpisodeViewRemove'
        }
    },
    episodes: {
        create: 'app/Episodes',
        afterReturning: {
            'remove': 'afterEpisodeRemove'
        }
    },
});




Brian Cavalier

unread,
Oct 14, 2013, 5:17:53 PM10/14/13
to cuj...@googlegroups.com
Hi julian,

Hmmm, yes, that is tricky.  The naive approach, connecting the two remove methods directly together in both directions, obviously creates a call cycle.

MV* frameworks typically break this cycle by introducing some sort of mediator in one direction.  Usually, user events flow through a mediator (aka "controller"), which updates the data.  In contrast, data changes may be allowed to flow directly to the view.

You could try that: introduce a mediator object between the view and data, and change the "on" event connection to call a method on the mediator, which could have an injected reference (via properties, ready, or constructor arg) to the data and call remove on it (there would be no AOP connection from the view's remove to the data's remove).  That'd break the cycle, and allow you to make a direct connection from the data to the view (without the need for the intermediary advice module).

It seems a bit less elegant at first, but also removes the need for the two intermediary advices in favor of a single mediator.  Mediators can also be a useful place to put application logic, data triage, etc etc.

Anyway, let me know if that helps!

julian

unread,
Oct 17, 2013, 12:21:13 PM10/17/13
to cuj...@googlegroups.com
great suggestion!  thanks!

Brian Cavalier

unread,
Oct 18, 2013, 9:30:45 PM10/18/13
to cuj...@googlegroups.com
np, glad that helped.
Reply all
Reply to author
Forward
0 new messages