-
-
Save getify/3332023 to your computer and use it in GitHub Desktop.
<!-- here's the PHP'ish way of making inline decisions while constructing HTML markup --> | |
<select name="foobar"> | |
<option value="bam" <?=($foobar==="bam"?"selected":"")?>>Bam</option> | |
<option value="baz" <?=($foobar==="baz"?"selected":"")?>>Baz</option> | |
</select> | |
<input type="radio" name="foobar" value="bam" <?=($foobar==="bam"?"checked":"")?>> Bam | |
<input type="radio" name="foobar" value="baz" <?=($foobar==="baz"?"checked":"")?>> Baz |
<!-- here's another PHP'ish approach using parameterized function calling --> | |
<?php | |
function selected($val,$test){ return ($val===$test?"selected":""); } | |
function checked($val,$test){ return ($val===$test?"checked":""); } | |
?> | |
<select name="foobar"> | |
<option value="bam" <?=selected($foobar,"bam")?>>Bam</option> | |
<option value="baz" <?=selected($foobar,"baz")?>>Baz</option> | |
</select> | |
<input type="radio" name="foobar" value="bam" <?=checked($foobar,"bam")?>> Bam | |
<input type="radio" name="foobar" value="baz" <?=checked($foobar,"baz")?>> Baz |
<!-- this illustrates the same problem but not in relation to forms. specifically, | |
the problem revolves around how to cleanly make conditional inclusion of small | |
snippets of markup inside other bigger markup templates. --> | |
<a href="<?=$bam?>" <?=isExternalLink($bam)?"target=_blank":""?>>Bam</a> | |
<a href="<?=$baz?>" <?=isExternalLink($baz)?"target=_blank":""?>>Baz</a> |
<!-- | |
one approach I'm considering for handlegrips: | |
http://github.com/getify/handlegrips | |
let you pre-compute a comparison hash for a data value (`data.foobar`) against a set | |
of values (`"bar","baz"`), and only make the assignment for the matching set value in | |
the comparison hash. | |
--> | |
{$: "#foobar" | data.foobar{"bar","baz"} = "checked" } | |
<input name="foobar" value="bar" {$=data.foobar{"bar"}$}> | |
<input name="foobar" value="baz" {$=data.foobar{"baz"}$}> | |
{$} |
<!-- | |
an alternative approach I'm considering for handlegrips: | |
http://github.com/getify/handlegrips | |
kind of like an array comprehension, where you can specify a set-literal as the | |
looping context of values, so your computation happens once for each iteration. | |
--> | |
{$: "#foobar" } | |
<!-- {$* is the looping construct. this example allows a set literal for the | |
looping context. you then evaluate the loop expression for each iteration, | |
setting a helper local variable `checked` to use in the markup. | |
--> | |
{$* ["bar","baz"] | checked = (data.foobar==.) ? "checked" } | |
<input name="foobar" value="{$=.$}" {$=checked$}> | |
{$} | |
{$} |
Sounds like you are preferring the #4 approach from the comment I just made to @polotek, which is do all or most of the pre-computing in your controller layer, even for things that are purely presentational (like, should the "checked" flag appear, etc). I understand that approach, and I've done it before many times too.
But I think it's horribly brittle, and violates the spirit of what we're trying to achieve -- separation of concerns. You should not have the same snippet of code that's doing both: 1) consulting the M-model to figure out if you're User object is logged in; and 2) figuring out if a list of data is empty and so should display an alternate "no results found" type of message/markup. I see those as fundamentally different tasks, and they should be in distinctly different parts of the stack.
Don't you just have a function that sets the selected on the DOM given a model? you'd just bind a change in the model to that function so when a change happens it'll set the selected. Wll all you have to do is setup your template without any selected and fire off that function manually on the DOM structure before displaying it on the page.
If you've got a conditional that depends on data then you can make that data a model and update the DOM accordingly with a handler. No template logic is ever needed - it's just a quicker way to define what could go in code. If anything I'd argue that it's worse to put it in the template because you'll be tempted to just re-render the entire template rather than a small change, though it will make things faster on initial render
@rhysbrettbowen-
I think what you're pointing out is valid but orthagonal to the question at hand. The context of this question is how to handle a specific task in the template-string rendering approach.
Sure, dynamic live updating of simple properties probably should NOT occur with a whole re-rendering of a template partial for the surrounding element... that would be overkill.
But templating stands on its own as a valid approach for initial view rendering (especially on the server). I know some people would prefer that all UI is built via DOM manipulation, but that's also very distasteful to many others for lots of reasons. Look at the most recent updates from Twitter, where they've gone back to a server-rendered view (using template-strings, undoubtedly) approach, because they found that to be more performant for what they care about.
Essentially, there are two different paradigms: string-based template rendering, and DOM-manipulation based rendering. They are both equally valid, and depending on needs you would do either or both in a project.
But I don't think it's valid to blanket claim (without any supporting detail) "It should just be done in a totally different paradigm."
@polotek
Actually, I think that's reason I prefer the ability to specify your boolean selection logic in the declaration header of each template section. As such, you're not making those pre-computing decisions "too early" (tucked away in some controller that doesn't know ultimately how stuff is going to be visually presented) -- you're only doing the pre-computing at the point of the partial that needs to use it, and only computing what flags you need for THAT exact template partial. Shouldn't be any waste of effort there.
If you take as a given that these boolean decisions have to be made somewhere, seems like there's basically 4 choices as to where they are specified/computed:
I think #1 and #4 are the worst choices, and so between #2 and #3, I choose the simplicity of not having a separate file, and also not doing actual programming but really just boolean selection logic (a very small subset of programming).
Of course, the line that is important to distinguish here is that no matter where (#1 - #3) your template logic happens, that logic should only be purely presentational logic. You shouldn't be doing things there like Date formatting (that's often driven by business logic like the user's chosen TZ preference, etc), math calculations (summing up columns, etc), or other tasks. Those usually masquerade as presentational tasks, but I argue they are clearly tasks for a UI controller.
The types of logic that I see as OK to be associated with a template are basically things that be expressed as pure boolean selection (even composed boolean decisions). But calls to your M-model methods, math, formatting, etc... all that stuff needs to be handled in a controller step before the template engine gets ahold of the data.
I should also say that a "UI controller" is not necessarily the same thing as a back-end controller that handles data transfer between the M-model and the persistence layer, etc. Those are both controllers, but they are clearly different and each is specialized to his own tasks. In other words, there is no one C-controller, but many C-controllers.