Can.map.delegate with wildcard props, some issues

  1. obs.delegate(’*.prop1, *.prop2’, ‘set’, function()…

does not work

  1. objs.delegate((*.prop1=myValue’, ‘set’, function() …

similarly does not work

AFAICS there is nothing in the docs to suggest these constructs are illegal so is this a bug or just not documented ?

  1. obs.delegate does not always fire. I’m almost certain it’s not my code as the obs is set absolutely correctly but during setting it an obs.attr(‘prop1’, 123) is not firing the delegate listener. I’ll try to construct a JsBin

Ron Yuen

I’d avoid using delegate. Use Component events object or a compute. I can show you what I mean next time I’m on a computer.

Justin

Avoid .delegate … arrrrrrgh !!!

I just spent half a lifetime and more blood and sweat that I thought I possessed to clean up some code with what I thought was the better can.onical approach.

At the mom I can’t use can.Components. I do use several, but the core of this rather large and complex app is not yet component-ized and no sign of enough break time to do the job - the usual production pressures.

I’m using computes a fair bit but it seemed to me that delegate offered a better solution. Prior to using delegate I had a single handler listening on the main observe and I dealt with figuring out what / why /when by inspecting the attr details.

In theory delegate takes away most of the boilerplate in the above approach and allows me to concentrate on the business logic for each piece being listened to.

I’d like to know why not use delegate and if this is something that I should just not use at all ??

Since you are a (the ?) lead developer I guess your opinion should carry some weight …

BTW my original item 3 turned out to be my code after all … incorrect listener (zero instead of capital-O in the string; I just HATE some of these fonts.

I’m also overall constrained by the fact that the legacy environment (IBM Domino) is not file based - everything is in a single rather opaque binary blob - so using modern tooling is bloody hard. I’m on as aggressive a migration path as time allows …

Regards

The reason to avoid delegate is that computes and the define plugin can do what they do, but better and are more inline with how to write a maintainable CanJS application. Delegate hasn’t been updated in 2 years https://github.com/canjs/canjs/tree/master/map/delegate.

Why/How are you using delegate? If you can give me an example, I can probably show how to convert it to computes and why they are a better solution.

You can generally create a compute that changes when your delegate code changes.

Justin

Thanks for the explanation. At the mom I’m on the road but I’ll be back at a desk Thur morning.

European time (:slight_smile:

I’m be in touch

Regards

Ron

Justin

Sorry for the delay - a very long drive across Europe to Vienna where I have been living for the last 7 years but now moving to S of France. Life’s a bitch (:slight_smile:

Background

We are in enterprise land here, not Joe Public on the web …

I have a json object comprising a list of ‘entries’ which can be one of a few different ‘types’, each of whci requires different business logic. This json is used to generate the user interface via stache templates. Before this the ‘raw’ json has to be cooked and transformed into a hierarchy as an entry may contain entries and it’s turtles all the way down, at least for about 3 levels. json becomes an observable via can.Map

The user presentation takes the form of a list of form entry ‘questions’ based on not only the usual html elements but additional constructs that allow drag and drop sorting of lists ( a so called ‘rank), drag and drop categorisation mechanism so the user can drag from a list and drop into various categories.

Edits to this interface create events which are monitored via can.Controls

Think of an online survey as a concrete example where each entry is one of the survey questions and questions are grouped into sections and maybe subsections each with their own presentation idiosyncrasies. It’s way beyond the SurveyMonkey simple stuff and allows for dependency management and many additional bits of functionality. If q.1 is ‘ please enter marital status’ with single/married/divorced as possible choices and q.2 as ‘how long have you been marries’, then q.2 needs to be disabled unless q.1 answer ==married. This is what I mean by simple dependency.

Interface edits trigger 2-way binding usually to an ‘answer’ prop eg

<input name=“testfield” {($value)}=“answer"/>, with a bit of supporting JS to deal with checkboxes, selects etc so that they also generate an ‘answer’ prop.

I also need to be able to figure out which object in the observable heirarchy is related to any given prop change. To do this I create ‘helper’ objects which as hashes of all entries in the hierarchy based on maybe fieldname or entry’s uuid as keys. That way I can instantly get the obj given a has key regardless of where it is in a maybe deep hierarchy.

Bottom line : I need to monitor changes to the ‘answer’ prop in an efficient manner as changes have many consequences … Changes to ‘answer’ not only trigger server saves may also involve calculating a score (for this question), maintaining total score, displaying question status via a ‘traffic lights’ red/green/amber) display, counting unanswered questions, determining if the questions are all ‘finished’ in which case we might move on to a workflow / approval and so on.

Define

At app init time I run an initHandlers() function containing a series of IIFEs like the following

	(function answer(surveyObs) {
		// delegate listener for changes to normal and matrix question answers
		surveyObs.entries.delegate('*.answer', 'set', handler);			// a normal question
		surveyObs.entries.delegate('*.entries.*.answer', 'set', handler);	// a matrix question, deeper in the hierarchy
		function handler(ev, newVal, oldVal, prop) {
			var entry = ev.target, json;
				// validate if applicable and do not continue if we fail
				if (entry.validation && newVal!=='') {
					validate(entry, oldVal);
				}
				saveToServer(surveyObs, entry, json);		
		}
	})(surveyObs);

The definition of the delegate wildcard tells me what is changing without me having to inspect the target and ask what type it is.

Another example in the same mould

	(function multiple(surveyObs) {
		// delegate listener for changes to multiple choice changes
		// except for rank and buckets which are handled by a compute
		surveyObs.entries.delegate('*.choices', 'set', handler);
		surveyObs.entries.delegate('*.selected', 'set', handler);
		function handler(ev, newVal, oldVal, prop) {
			// this will fire for multiple selects, checkbox , rank and buckets
			var entry, answer;
						// do the business

Without delegate I have to listen for all changes to the observable, inspect the ‘attr’ then despatch to whichever subroutine to deal with the business logic.

In addition to the above observable handlers I have a series of can.computes doing other stuff, each compute being setup by initComputes() when the app load eg

	(function state(surveyObs, realQuestions) {
		// at the mom this is the best way I can find to do this
		var groups, count;
		groups = utils.filterEntries(surveyObs.entries, 'isGroup');
		count = can.compute(function() {
			// ok run through every object’s status, figure out
			// and set the traffic lights
			// whilst we are about it we calculate the total completed
			// do the business of figuring out the state of this question
			// which might be not answered, answered, part answered, error, warning etc etc
		}).length+groupsCompleted;
		});
		surveyObs.summary.attr('completedCount', count);
	})(surveyObs);

Some of the computes take the alternative format :

	(function ranking(surveyObs) {
		// whenever we rank/unrank we need to compute the 'answer' for the rank question
		var rankings = utils.filterEntries(surveyObs.entries, 'tagtype', 'rank');
		_.each(rankings, function(o) {
			o.compute('answer').bind('change', function(ev, newVal, oldVal) {
				var answers, rankings = [], ranker = {}, ranked = [];
				// compute an answer from a user sorted collection of <li> 
				// used jQueryUI’s sortable ...
			});
		});
	})(surveyObs);

ALL OF THIS WORKS GREAT !!

Only issue was as noted in my forum entry a couple of things in delegate that don’t work as expected …

Define is not something that I’m currently using but I can certainly see value in integrating it in … time …

Sorry for the length of this, I REALLY appreciate the work that you guys are doing in this framework

BTW If you guys allow ‘guest’ blog entries than I am considering writing up some of my learning experience with canjs ??? FAR too few people seem to be using this framework despite it’s maturity and holistic approach.

Regards

Ron Yuen