Form field id problem

Permalink 2 users found helpful
Hello,

I'm using C5 standard form widget to have form fields like this:
<?php echo $form->text('allLinks[]', 'default value');?>


As you can see the name of the field contains "[]" as this field will appear multiple times.

The problem is that C5 automatically uses the name of the field as an Id so I end up with all my fields using the same
id="allLinks[]"


Obviously that's a problem.

What should I do, forget about C5 widget and just manually code the fields or is there a way to stop C5 from automatically creating those Ids (I don't need them)?

Many thanks in advance.

mnakalay
 
Mainio replied on at Permalink Best Answer Reply
Mainio
Maybe just override and hack the form helper?

There isn't really way to do this automatically, in the form helper you can see this code:
protected function inputType($key, $type, $valueOrArray, $miscFields) {
      $val = $this->getRequestValue($key);
      if (is_array($valueOrArray)) {
         //valueOrArray is, in fact, the miscFields array
         $miscFields = $valueOrArray;
      } else {
         //valueOrArray is either empty or the default field value; miscFields is either empty or the miscFields
         $val = ($val !== false) ? $val : $valueOrArray;
      }
      $val = str_replace('"', '"', $val);
      return "<input id=\"$key\" type=\"$type\" name=\"$key\" value=\"$val\" " . $this->parseMiscFields("ccm-input-$type", $miscFields) . ' />';
   }


and it should be something like this (to achieve what you want):
public function inputType($key, $type, $valueOrArray, $miscFields) {
      static $numkeys;
      if (!isset($numkeys)) {
         $numkeys = array();
      }
      if (!array_key_exists($key, $numkeys)) {
         $numkeys[$key] = 0;
      }
      $numkeys[$key]++;
      $id = $key;
      if (($pos = strrpos($key, '[]')) === strlen($key)-2) {
         $id = substr($key, 0, $pos);
         $id .= '-' . $numkeys[$key];
      }
      $val = $this->getRequestValue($key);
mnakalay replied on at Permalink Reply
mnakalay
Thanks, that's really cool code.

I don't think I'm going to be able to use it though because my code is going to be used in an add-on and I don't think I can redistribute it with an override of the form helper.

I think I'm going to have to just not use the form helper on this one.

Many thanks for your help.
Mainio replied on at Permalink Reply
Mainio
You can create your own form helper (=copy the default) inside the package.

Just create it like "form_custom" and name the class "YourPackageNameFormCustomHelper".
JohntheFish replied on at Permalink Reply
JohntheFish
Whilst the html produced with duplicate id's is not valid, unless you are using those ids in css or JavaScript, I have never seen it break anything.

In php, the field names conveniently become an array in $_POST

When writing jQuery to work with forms, I prefer to use the field name property to select on rather than id to prevent errors arising from C5's duplication of ids. If necessary I can then select by index within that.

If I wanted to write css selected on the duplicated ids, I would do something like using a jQuery each() to modify them with an index and hence make them unique.
mnakalay replied on at Permalink Reply
mnakalay
Yes I agree with you.
So far the duplicate IDs have had no impact on my code since I also target the field name with jquery. I also thought about modifying the ids dynamically as you suggest but only for the sake of validity.

I was just wondering if that's the kind of thing that the C5 team might frown upon when I submit the add-on.

Oh and on a personal note, I am extremely envious of your career as an underwater photographer :)
Mainio replied on at Permalink Reply
Mainio
I think this is not something the PRB members (or the core team) are worried about. It's more about that your add-on brings some additional value (making it better during the PRB) and most importantly, that it will not technically break anything in various c5 environments.

John might confirm that because while he's on the ground, he's around at the PRB (although he's not from the core team).
JohntheFish replied on at Permalink Reply
JohntheFish
That pretty much sums it up. The things that take a lot of consideration in the PRB are core overrides. So unless you really really really need to, avoid overriding anything in the core. If you get creative there are usually alternatives that leave the core alone.

I personally prefer to use the form helper in preference to creating forms from scratch. It has some other idiosyncrasies, but overall it works, keeps code cleaner, and I have yet to see its use rejected by the PRB.

Make sure you read up on the guidelines before submitting (and my related howto). Things that get stuck seemingly unnecessarily in the prb have invariably missed one or more critical points.

On the id issue, using jQuery to clean up the ids will not help with any validator that works on the html before jQuery has run.
mnakalay replied on at Permalink Reply
mnakalay
Well, I think that basically spells:
1) if I really want to be hard-bent about the whole thing, do something
2) If not just leave the G.. D.... Ids alone

I think that's what I'm going to do. I don't need them. I'm (almost) pretty sure nobody is going to try to validate code behind an add-on's setup form and if the C5 team ever decides it's a big problem then I'll just not use the form widget.

Thank you both for your input. I needed some kind of confirmation/moral support from seasoned professionals so that helped a lot.
jordanlev replied on at Permalink Reply
jordanlev
And... now that you've decided on your approach, I'd like to offer my own opinion (this actually just came up in a project I am working on this week).

I just don't bother with the C5 form helpers when you need brackets in the name -- I just use plain HTML tags instead. Here's why: I don't find that the C5 form helpers really offer that much in terms of functionality, and winds up just being more verbose and less readable than plain old HTML tags (especially when you have lots of attributes on the tag -- meaning you have to pass in an array of attribute names and values, which is super clunky in an HTML view).

There are two exceptions to this rule, though:

1) Outputting <select> lists... it is so tedious to code these up by hand (e.g. <option value="1"<?php echo ($value == 1) ? ' selected="selected" : ''; ?>whatever</option>), that I am really glad to use the $form->select() helper because you can just pass it an array and it takes care of the rest

2) Repopulating form data where it exists in the $_POST array (in other words, if the user submits a form but it fails validation and you need to re-display the form with their prior entries so they can fix the invalid field). This is not even documented anywhere (as far as I could tell) and I just randomly discovered it one day -- total godsend to have this functionality and this alone kept me from coding up my own custom form helper library (well, taking one from another framework and tweaking it for C5).

So, getting back to your specific situation here -- it's not a select tag so reason #1 doesn't apply, and the bracketed values won't be re-populated automatically by C5 because it has no way to uniquely identify them, so #2 doesn't apply either.

Hence, there are now 0 reasons why using the C5 form helpers provides any benefit whatsoever in your situation.

Just my opinion of course -- Mainio and John are both very smart people and well-versed in Concrete5 things too so you won't go wrong following their advice. At the end of the day I think it's the same result, just a difference of style.

Best of luck,
Jordan
JohntheFish replied on at Permalink Reply
JohntheFish
Its ironic, my big hate about the C5 form helper is that the POST data can be annoyingly too persistent.
mnakalay replied on at Permalink Reply
mnakalay
That makes sense, thanks Jordanlev.
Actually, now that you made the point of generated code being bloated, I makes me think that I didn't like all the inline css and I should deal with that too.

So I'm back where I was before, I have a set of good reasons to choose either way and I don't have the necessary skill set to make the decision :(

Well I guess it's time I learn so I'm going to make a decision anyway based on personal variables.

Using the form helper allows my personal laziness to express itself because that's what is already coded and I don't need to do it again.

On the either hand, coding it myself gives me simpler, non bloated, hopefully semantically correct code.

Finally, "Jordanlev" is such a cool name and although it has nothing to do with anything, I think I will let that take the cake.

I'm going to keep the form helper for a very long select list I have and code myself the text boxes.

I feel a better man already.

Thank you
jordanlev replied on at Permalink Reply
jordanlev
Well, the only way to get the skill to make the decision is to try them and eventually see how they work for you (so really more of an experience thing than a skill).

And I must say this is the first time I've won a technical argument on the basis of my name. (And against "John The Fish" no less -- I mean, isn't *that* the coolest name ever?!)

-Jordan
JohntheFish replied on at Permalink Reply
JohntheFish
I have been thinking about the number of core helpers I have needed to re-work for a current project, getting them to behave better in more general situations. Over the last week I have diverted into re-working several of them with a half hour here and a half hour there.

As I noted above, my big hate with the form helper is its persistence even when I don't want it to be. I have various workarounds, but yesterday concluded it would make more sense to create my own helper with persistence and ids as selectable options - I may as well do the big one.

So when I finish today's homework of fixing a new zip into a drysuit, my own form helper that behaves as I want it to will become a work in progress.

Jordan deserves the win on this one (but not on the basis of the coolest name).
mnakalay replied on at Permalink Reply
mnakalay
I did what Jordan said, use the helper for the select and do it manually for text type input. It took me 30 seconds, works as well, and I have semantically correct code so I'm happy with the choice.

I am relatively new to C5 and php so learning is a constant struggle. I enjoy every minute of it but I also have my moments of frustration and discouragement.

For instance when John talks about the persistence of data he doesn't like in the form helper, although I understand the concept, I have no idea how or where or under what circumstances that would happen and/or become a problem or even how to find out about it and I feel I still have a long way to go.

Someone in my situation can only be extremely grateful to have two much higher level coders be so helpful and so gracious about it.

However, on the other hand, it feels like asking a 3 year old (me) to make an adult decision based on an adult's perception of the world.

So what I was trying to express was that I just was not "adult" enough in my learning process to make the best choice.

But then again, I learned so much reading people like you and going through your code that I know there is really no better way to learn.

So in the end, choosing Jordan based on his name was an admission of my limits...

But I still have to say that every time I read the name Jordanlev, I instantly see images of the game Street Fighter with the fighters taking position with that cool deep voice announcing the round... Yeah I know, it's weird.
jordanlev replied on at Permalink Reply
jordanlev
Ha-do-ken!

I understand how overwhelming things can be when first starting out (heck, sometimes they're still overwhelming to me even after many years of doing this stuff). I've found that a good policy to help deal with this is to keep things as simple as possible. This doesn't mean everything is always simple -- just that you should not worry about over-complicating things *until the situation calls for it*. So in your situation, it does not call for needing the form helpers really, so just stick with the straightforward HTML tags. If, one day in the future, you are using the HTML tags and realize that it's become super annoying to deal with them and you have to add all this crazy PHP and/or javascript code to them to do what you want, then at that time you should start looking for better solutions.

To answer the question about data persistence, imagine a situation where a user is filling out your form. There are 10 fields on the form (e.g. name, phone #, address, etc.), and some of those fields are required. The user fills out 9 out of 10 fields, but accidentally leaves the "phone #" blank. Phone # was a required field though so after the user submits the form your code sees that the phone # is missing and hence the user needs to add this in -- so you show them the form again but this time with an error message at the top (telling them that phone # is required). Since the user already filled out 9 of the 10 fields, it would be really bad to force them to re-enter all 10 fields just because they missed one of them, so instead you should put their prior responses to the other 9 fields back into the 9 fields for them, leaving only the 1 blank field for them to fill out.

This is what the "data persistence" of the form helpers does, and it is very helpful for the "most common use case" of straightforward forms. But John The Fish has developed several incredibly clever and advanced addons that do things that are a bit "out of the ordinary" (which is a good thing -- because they are useful things but fall outside the realm of what the core Concrete5 system offers). Perhaps he is showing the form again to people in situations different from "I forgot to fill out my phone number". I can't think of what his specific situations are off the top of my head, but I know I've done tricky things in the past with forms where something like this might be necessary. (Maybe John can chime in with a specific example to enlighten us?).

So that's a long rambly attempt at explaining the situation better. I hope that helps (and if you knew all this already, please accept my apologies -- I did not mean it to be condescending -- I just thought this was a very interesting discussion and wanted to flesh out the details).

Cheers,
JordanLev
JordanLev
JordanLev
Mainio replied on at Permalink Reply
Mainio
The form helper comes short when you don't want to use the autofill, so you'd like to "override" the value passed in the request.

Captcha is really bad example for this in c5 context (because it's handled separately from the form helper) but plays as a good example when wouldn't want the field to be autofilled. Currently you cannot override this behavior with the form helper.

As far of this thread: completely good reasons from @jordanlev to point you for your direction but I just probably need to clarify my own suggestion: I actually think of a bit different way than @jordanlev. To me _consistency_ is of the #1 thing and by using a centralized function, this is a great way to achieve that. Let's say you write some of your input tags like this:
<input type="text" value=""/>


and some of them like this:
<INPUT TYPE="text" VALUE="" />


Well, again bad example (because clearly I'm not in the mood to come up with good examples) but with centralized code base you could easily make them look the same with minimal amount of work (yes, I'm a bit lazy). Although, again both of those are perfectly valid, as said, this is not the best example but just had to come up with something.

Also, in some of @andrew's blog posts I remember reading something like "if you can use the API, in the long term it's better to use that although it might not be the most efficient way". I think that was more about some page list stuff and its efficiency but serves as a good point, in the long run these things can be better managed if everyone uses the same methods. And yes, I proposed a hack here but still using the same code base.

But to conclude: I think jordan pointed out the best way you could've taken with this specific issue.
JohntheFish replied on at Permalink Reply
JohntheFish
The most common situation I come across is a simple input form where the user is repeatedly adding new items using the same form.

Click add,
- If it doesn't validate, then show an error message and the persistent data to allow for correction.
- If it does validate, then show a confirmation message and a form ready with default values for the next new item.

In both cases I am showing the same form again. If I don't use the form helper, I need to provide my own persistence code. If I do use the form helper, I need to clear the persistence after a successful input.

(Generally I like to restrict inputs to valid values or do as much validation as possible in JavaScript before the form gets submitted, but that is not always viable.)

The easy way round it is to have an intermediate page with a confirm message and a 'continue' button to generate an empty $_POST and swap back to the input form page. But that means unwanted pages and clicks for the user. Another way is selectively clearing parts of the php superglobals, a practice I am not sure is reliable between php versions or installations and while it can work on pages where I control all the forms, could mess up any other forms on the same page. Or I end up with a combination of form helper and directly coded elements in the same form.

The situation gets worse where I have an expanding form with growing rows of inputs. In this case I need to allow the form to be saved then redisplayed, so a user can edit or correct what they have already added and continue to add new rows at the same time. So some rows need persistence while other rows do not.

Here, the form helper can loose track of arrays of text inputs and helpfully serialises them, so I end up with text inputs showing the content value 'Array' (I suspect this is a bug rather than a persistence issue).

Anyway, my hacked version of the form helper is ready for testing. I have added methods to enable/disable id field generation, and enable/disable continuity. The new code is simple but verbose as I have just wrapped much of the existing code in tests for these conditions rather than be too clever and add to already tortuous logic (which may itself be buggy).

By default, I intend it should behave as the existing form helper, but I can disable or re-enable continuity as and when I need to, even within a form. I was planning to inherit from the existing form helper, but as I needed to modify nearly every method I decided on replacement.

The untested hack is attached. I will be giving it a workout and if anyone else wants to give it a go and report back, any improvements will be welcome.

(EDIT -Attachment removed - see post below for later version with some bugs fixed)
jordanlev replied on at Permalink Reply
jordanlev
Hey John,
Thanks for sharing -- I'll take a look at your new helper when I get a
chance (probably can't do so right away though).

But as for your first issue of re-displaying the form for new input --
I think you can work around this problem by redirecting the page back
to itself. In fact, it's generally a good practice to do this kind of
redirect after a successful form submission anyway because otherwise
if the user hits "refresh", the browser asks them if they want to re--
submit form data again (which, if they've already successfully
submitted the form, they will never want to do).
So you'd get the benefits of the "confirm/continue" page, but without
the user actually needing to see that or have to deal with clicking an
"ok" button.
And you can set your success message (or a flag indicating to show it)
to the SESSION so your form knows to display that success message at
the top of the new fresh form that is now cleared of all prior inputs
(called a "flash" message in Rails and probably some other PHP
frameworks too).

-Jordan
JohntheFish replied on at Permalink Reply
JohntheFish
Thanks for the tip. The redirect to self could be useful in some cases, especially with it also serving to clear page refresh.

I already have the session trick for success and error messages working between related pages (List page to edit/delete page and back again), so can re-use that. (And the equivalent redirect on OK/cancel handlers for these pages.)

I have not found it yet, but there is at least one bug in the code I posted....
JohntheFish replied on at Permalink Reply
JohntheFish
A bug before bed time. I left a closing quote out of the function that does the id string. It should be:

private function idStr($id){
  if ($this->idEnable){
    return ' id="' . $id . '" ';
  } else {
    return '';
  }
}
JohntheFish replied on at Permalink Reply 1 Attachment
JohntheFish
Well, a few more quotes screwed up, but I hope I have caught most of them in this version (attached)
mnakalay replied on at Permalink Reply
mnakalay
@Jordanlev
OOOhhhh! that's what it is. I'm having an ahaa moment here. Of course I know what it is, I just didn't know it was called Data persistence. I actually sent a message to the C5 team about that a while ago because after using the feedback form here, I wanted to send another message about something I forgot to mention but the form still had my previous message although it had already been sent. I had to reload the page. I think they fixed it since then. (yeah me :)

Simple is an absolute best advice, I have to control myself all the time. I'm trying to develop an add-on and I have to keep telling myself that it doesn't need every little option I can think of just because it crosses my mind.

On another topic please don't worry about being condescending. I've been reading a lot of your messages on the forum and one thing you're not is condescending (Although frankly sometimes you would totally be entitled to be condescending)

Thank you again

@JohntheFish
I'll look at your code, hopefully I'll learn something.
Thank you for your help

@Jordanlev & JohntheFish & Mainio
On a (almost) totally unrelated topic, I think it would be really great if you (separately, together...) would consider writing a how-to about how you organize your time and work. I'm a lecturer in a college in China where I teach 20 hours a week plus a lot of time on dealing with the students and correcting asignments... things like that. Basically I have 3 days a week during which I can spare 8 hours to work on websites and coding.
I can see that both of you have busy professional lives but still find time to develop add-ons that you give away for free, write articles, maintain, blogs, answer questions...
How do you do it???

I (and probably many others) would love to read about how you organize yourselves, manage your time, your priorities. How much do you work every day. What tools do you use to improve your productivity. Do you still plan for private / family time (I have a 3 year old son)...

Anyway, just a thought.
jordanlev replied on at Permalink Reply
jordanlev
Working for yourself (as I do) is incredibly challenging, especially with time management. One thing I've found that really helps is to be genuinely excited about working on what you're doing. For me, helping people in the forums is an enjoyable thing to do when I lose focus on my own work for a few minutes (it's so much easier to solve other people's problems than my own). With the addons it's usually something that I find myself recreating over and over again for client projects, which gets boring so I keep myself into it by abstracting it out to a more re-usable package (and putting things up on the marketplace like that makes future maintenance much easier because you know there's one official version of your code instead of slightly modified versions scattered around each client project).

So for me all the stuff I do in the community is a way to keep me motivated on the work -- I consider it all part of the same thing. Hope that helps explain things. (this is a really great question -- you might want to start a new forum post about it).

-Jordan
Mainio replied on at Permalink Reply
Mainio
Quite same words with jordanlev about the time management. I feel that the forum posting is a small way to give back for the awesome product we get to use for free. I used to be in that position at some time and now when I know something about c5, answering these really doesn't even take that much time. Also (exactly like jordanlev), I find it relaxing to answer these because it gives me a small break from other stuff.

I actually have a small team here but most of the c5 work is done by me currently. So I'm actually working full time in a software company and we're also doing other stuff than c5 alone, but yes, I do quite a lot work overall. Managing schedules is hard but maybe the best advice I can give you is: deadlines and goals e.g. on weekly or daily basis. They keep you focused and not doing something you shouldn't be doing.

And we develop both commercial and free add-ons. Usually if something is generic and something that I believe should be free for an open source project, then I decide to publish that one for free. Nothing else behind that, it just usually comes from the though "my god, why doesn't c5 have a free add-on for this". But that decision is made always BEFORE the add-on development starts, so it's genuinely something I believe should be free.

And most importantly as jordanlev mentioned: love what you're doing and enjoy the moments when you get things done. Those are the best motivators I've yet to come across.

-Antti
mnakalay replied on at Permalink Reply
mnakalay
Thank you both for you input on this.
For me, distractions are what's hardest to manage.

I think I'm going to follow Jordanlev suggestion and start a new thread about this see what comes up.

Thanks again.
jordanlev replied on at Permalink Reply
jordanlev
Yes, distractions are a very big problem (especially with a task like programming that requires large amounts of "in the zone" focused time to be truly productive).
One method I've come across that works pretty well for me is the "Pomodoro Technique" --http://www.pomodorotechnique.com/... -- you don't need to buy anything, just read the free e-book (even just the beginning of it that explains the broad overview -- it's pretty easy to understand), and print out the free worksheets and try using them.
Be careful about "heavyweight" time management systems that require lots of additional work just to manage themselves -- this is really just another form of distraction in disguise.
It really comes down to forming good habits (this is what the Pomodoro technique helps me do I've found), and also as Mainio says it really helps to plan out a schedule in advance so you have some structure to work around.

Last but certainly not least, remember that *everyone* struggles with this stuff and you are not alone. For a long time I would have problems with time management but then I would add on additional guilt and stress because I thought it was a failing in me or something like that -- but over the years I've learned that everyone is like this to an extent and while it's definitely something you want to work on improving, it's detrimental to get down on yourself about it because that only makes it harder.

No matter what you do, you will *not* become instantly productive over night. It's more like exercise -- you just have to keep trying and trying and over time you'll get better and better at it as you form good habits around it (and you'll also have relapses and very difficult days or weeks, but just remember it happens to the best of us and to keep trying and things will improve slowly over time).

-Jordan
mnakalay replied on at Permalink Reply
mnakalay
WOW you're so fast, 8 minutes for an answer... you must carry your computer with you everywhere (mystery solved :)

I'm going to have a look at the Pomodoro technique. Good habits are definitely something I need to develop.

Meanwhile I have started the new post we talked about so here's the link in case you are interested in commenting / reading / adding to it.

http://www.concrete5.org/community/forums/chat/tools-tips-good-habi...
JohntheFish replied on at Permalink Reply
JohntheFish
My motivation is very similar to Jordan and Mainio. They both sum it up extremely well.

Publishing addons serves to create more robust components for my own longer term projects, as a configuration management component for those projects, and also provides development milestones and motivation. As Mainio notes, I know when I start if an addon is going to be free or paid.

Working by myself most of the time, the forums fill the role that general office technical chat would were I working in a big office. Sometimes something that just needs doing cathches my interest. I try to put in more than I take out.
mblaeser replied on at Permalink Reply
You should try usinghttp://www.jsondata.com it's still in beta, but all you have to do is input your data and it will build the whole embed code for you

let me know if it helps