Jump to content

Create simple forms using API


Soma

Recommended Posts

Great tutorial Soma! This is the best summary of using PW's Inputfields that I've seen.

I noticed you did $field->attr('id+name', 'email') so just wanted to explain what that is for those that may be unsure of the syntax. That syntax is basically saying to set the 'id' and 'name' attribute to have the 'email'. While every field needs a 'name' attribute (like in HTML) the 'id' attribute is optional… if you don't assign an id attribute, PW will make one up. If you intend to custom style a field with CSS or target it from javascript, then it's best to assign your own 'id' attribute. Otherwise, it doesn't matter.

// this…
$field->attr('id+name', 'email');

// …is the same as:
$field->attr('id', 'email');
$field->attr('name', 'email');

// …as is this (direct reference):
$field->id = 'email';
$field->name = 'email';

The advantage of using the attr() function over direct reference is that attr() can't ever collide with other Inputfield properties that might have the same name as a field attribute. It's basically your way of saying "this should definitely be an HTML attribute and not anything else." For recognized attributes like 'name' or 'value' it doesn't matter what syntax you use because an Inputfield already knows 'name' and 'value' are standard HTML attributes. But if you needed to add a custom attribute like "data-something", well then you'd definitely want to use the attr() method of setting.

That attr() method should only be used for things that would actually be HTML attributes of the <input>, because they will literally end up there. So if you do an $field->attr('label', 'Hello'); you'll end up with an <input label='Hello'> in the markup, which is obviously not something that you want. :) That's why you assign a non-attribute property like 'label' or 'description' directly, like: $field->label = 'Something';

Last note about $attr() is that it can be used for both setting and getting attributes:

$field->attr('value', 'something');
echo "The field's value is: " . $field->attr('value');

// same as:
$field->value = 'something';
echo "The field's value is $field->value";

To extend your example, lets say that you wanted the 'email' and 'password' fields in a fieldset titled "About You". You would create the fieldset, and then add/append the fields to the $fieldset rather than the $form. Then you'd add the $fieldset to the $form:

$fieldset = $modules->get('InputfieldFieldset');
$fieldset->label = 'About You';

$field = $modules->get("InputfieldEmail");
$field->label = "E-Mail";
$field->attr('id+name','email');
$field->required = 1;
$fieldset->append($field); // append the field

$field = $modules->get("InputfieldPassword");
$field->label = "Password";
$field->attr("id+name","pass");
$field->required = 1;
$fieldset->append($field);

$form->append($fieldset); 

Or lets say that you wanted those 'email' and 'password' fields to be each in their own column so that are next to each other horizontally rather than vertically. You would assign the 'columnWidth' property to both the email and password fields. In this case, we'd give them both a value of 50 to say that we want them to be a 50% width column:

$field->columnWidth = 50; 

To jump out of tutorial mode and into idea mode: lately I've been thinking that PW should have a YAML to Inputfields conversion tool in the core (something that would be pretty easy to build), so that one could define a form like this:

name: subscribe-form

action: ./

method: post

fields:

- name: full_name

label: Your Full Name

type: Text

required: 1

- name: email

type: Email

label: Your E-Mail Address

placeholder: you@company.com

required: 1

- name: pass

type: Password

label: Your Password

required: 1

- name: submit

type: Submit

value: Subscribe

And create it like this (where $yaml is the string above above):

$form = $modules->get('InputfieldForm');
$form->load($yaml);
echo $form->render();
  • Like 23
Link to comment
Share on other sites

This is really interesting stuff and I'm learning so much from it. I've already tested Soma's code and it works very well. Is there a way of configuring $form->render() so that it outputs different html (divs for ul/li etc.)?

  • Like 3
Link to comment
Share on other sites

Thanks a lot for these indications. It helps me a lot, even though I'm a little stuck on one thing : my form is OK, but I'm trying to use a datepicker in one field... is this possible?

Here's where I am :

I changed this part of Soma's code

// you get the idea
$field = $modules->get("InputfieldPassword");
$field->label = "Passwort";
$field->attr("id+name","pass");
$field->required = 1;
$form->append($field);

with

// you get the idea
$field = $modules->get("InputfieldDatetime");
$field->label = "Date";
$field->attr("id+name","date");
$field->required = 1;
$form->append($field);

And tried to play around with

$field->datepicker = 1;

...But I have to admit I don't really understand what I'm doing and I just know this doesn't work...

I've spent quite a few hours on that (don't laugh ;-)) and if one of you can give me a hint, I'd greatly appreciate!

Thanks !

  • Like 1
Link to comment
Share on other sites

This is a field that requires additional stuff thats why it doesn't work. It would work if you include jQuery UI (with js and css) and to init the datepicker to the input you either do it by yourself with a js script or you can use the wire/modules/Inputfield/InputfieldDatetime/InputfieldDatetime.js.

Depending on the jQuery UI theme you include, your form elements will get also styled. But you can roll your own or overwrite some stuff. Not sure what the best way would be for you but there are several.

Edited by Soma
rewrote prev mobile written text
  • Like 2
Link to comment
Share on other sites

This is really interesting stuff and I'm learning so much from it. I've already tested Soma's code and it works very well. Is there a way of configuring $form->render() so that it outputs different html (divs for ul/li etc.)?

No there isn't a way. Try stick with it is all I can recommend atm.

  • Like 1
Link to comment
Share on other sites

Thanks for your reply.

I have now managed to get the datePicker working. I am still struggling with the style since even though I put the link to the stylesheet, it doesn't seem to work... Anyway, I'll look closer later... Looks like I have to link to several stylesheets... (I told you I'm no professional developper...)

Just a quick note : you wrote : wire/core/Inputfield/InputfieldDatetime/InputfieldDatetime.js . I found the file in wire/modules/Inputfield...

Anyway. Thanks again.

  • Like 2
Link to comment
Share on other sites

[quotamos]This is really interesting stuff and I'm learning so much from it. I've already tested Soma's code and it works very well. Is there a way of configuring $form->render() so that it outputs different html (divs for ul/li etc.)? [/quotamos]

you.can usage. $form->setMarkup(); und $form->setClasses(); two.set markups und html caresses.

see.eliamos /wire/core/InputfieldWrapper.php

  • Like 13
Link to comment
Share on other sites

  • 3 weeks later...

I'm a bit confused by your question, why do you want to post to another url anyway?

It's a post to the same page the form lies. If you chose another url for the action you have to have code there to read the post and process it.

There's no such thing as post to a template. The code example is for building the form and process it at the same time, hardly ever need to separate that logic, but you could.

Link to comment
Share on other sites

I have mini version of the contact form which will be on most pages, and instead of putting the processing logic on every page, I thought I'd just post to the contact page and process it once there.

for some reason my contact page isn't receiving it. I probably just have a bug in there~

Link to comment
Share on other sites

I have mini version of the contact form which will be on most pages, and instead of putting the processing logic on every page, I thought I'd just post to the contact page and process it once there.

Assuming all the pages that have the contact form are using the same template (or including the same file to process the form), then you don't need to worry about "putting the same processing logic on every page." It may appear that there is duplication there, but there actually isn't. If the code is shared, it really doesn't matter whether it all posts to the same URL or not. A situation where it would matter is if you were using template caching on your pages. In that case, you could solve it by making all the forms submit to the same [uncached] page. Though you could also solve it just by setting a cache-disabling POST var in your template settings.

Link to comment
Share on other sites

What do you want to archive by doing this? For a public form? Or for logged in users to edit a page in the frontend?

You could, but not all fields are suited for front-end, you'll have to also include all css, js and js configs for special fields like page asm selects and file uploads.

An example that takes this approach (without special fields) is the http://modules.processwire.com/modules/form-template-processor/

  • Like 1
Link to comment
Share on other sites

Hello,

Thanks Soma.

Yes, I was looking at the "Form Template Processor" earlier. To give clients a different way to create pages, this is the kind of functionality I am looking at. For some of the site concepts I have in mind, I will have multiple authors who really should not be seeing anything except the form for creating/editing a page.

As a way to learn more about ProcessWire, I wanted to see if I can make this work just using the API, without a module.

Thanks,

Matthew

Link to comment
Share on other sites

Well, the admin is built for that purpose to add and edit pages. Every user that has a login is able to see the admin, even if he can't edit anything and if he knows the url. Although you could add hooks to check for roles to redirect out of the admin in case.

I didn't say use the module but look at it how it's done, it's also done using API (as everything in PW), so you would need that same codes anyway. I don't see any problems doing your own frontend forms based on a templates fields. But depending on the fields you need, it's not as easy as simply rendering the fields with a form wrapper and you're done (for the reasons mentioned). Jquery UI, inputfields css and scripts and the $config->js would also be needed for them, and some of them would be in the $config->scripts, $config->styles when you render fields on a page.

I've never tried to rebuild the backend for the frontend, and always try to use some shortcut link on the pages for them to edit or create pages using a simple link with url to the right admin screen.

If I combine that with &modal=1 and fancybox modal they don't even need to go to the backend.

<a class="fancybox" href="/processwire/page/edit/?id=1123&modal=1">edit page</a>
<a class="fancybox" href="/processwire/page/add/?parent_id=1022&modal=1">add page</a>

That's in cases sufficient to give a simple frontend editing capability with 2-3 lines of code.

  • Like 4
Link to comment
Share on other sites

Hello Soma,

Thanks for your patience helping me!

Most times, I definitely would just use the admin area with roles to accomplish the goal.

But in the case where "regular" members have edit/create rights, I'm sure there is a way to do this differently.

I found an old post where someone wanted essentially what I'm describing, and Ryan responded:

My interest in on the line where Ryan says this:

"output a form of just the fields you want them to edit and/or process them"

Yes!! How do we accomplish that?

Thanks again,

Matthew

Link to comment
Share on other sites

Not sure what in detail you need, but got some example code mainly taken from the Template Form Processor render a pages form to edit in front-end.

https://gist.github.com/somatonic/5011926#file-from-php

Now you need to include scripts and css needed. Note this $config stuff is only populated after you generated the form in API. In the head.inc you could include script (taken from the admin template default.php and modified a little). Also you would maybe need the inputfields.css found in the admin templates folder.

This somehwhere in the <head>

Moved code to gist.github.com for convenience..
https://gist.github.com/somatonic/5011926#file-head-php


This will give you the basic things needed, I have not tested all fields, but file uploads and date picker or even custom color picker etc works fine.

  • Like 4
Link to comment
Share on other sites

EDIT: I see that this concept has been discussed in various forum threads. But I can't quite get a handle on what is the best practice when you have multiple authors who can create/edit pages but who should not have access to the admin area.

Haven't read that well.

Ok, this is kinda hard to make it short, there's many way to archive this in different levels. Are those users public-registered users or trusted users you give explicit accounts?

Just to give the most restricted, just front-end users:

Create appropriate roles or permissions. Like a "frontend_editor" Role and only add page view permission. You give only guest and this new role to the users. When they login via backend they won't see anything and get redirected to front-page. Voila.

In your templates and modules you check for the Role the user

if($user->hasRole("frontend_editor")){
// do stuff
} else {
// do other stuff
}

Edit: To give access to only certain field to edit. Well since you only have a front-end user with no access to anything, you control it via code in template. And my examples above shows a "$ignore_fields" array you could choose to ecxlude certain fields as you create the form this is simple. But anything possible in PW.

There's a module that enables per field limit access by Ryan http://modules.proce...eld-permission/ might be something to try. Don't know if it works with the form rendering method here.

To restrict certain users to pages. Since need to know which pages a user has you can add a page field to the user template, so you can add pages a user can edit by editing it's profile. Then you can check in your front-end code if a page he wants to edit is in his added pages. For adding pages you'll have to have to parent page field or something.

// does the page field contain the page he wants to edit?
if($user->editable_pages->has($editpage)){
// do edit form stuff
}

Or just use one page that is the parent of a branch he can "work". Use this to redirect the user to his page and do stuff and always check if the user has the rights when you do something.

/Just to give some examples

  • Like 3
Link to comment
Share on other sites

Greetings Soma,

Thank you very, very much for such a thoughtful and thorough response!

Yes, I see how this can work. It is a good example of just thinking a diffent (better, more efficient) way with ProcessWire.

I know the whole "front-end editing" idea is debatable and a lot of people don't even like it. And I plan to use it in limited ways. But the technique Soma deacribes seems like a very elegant way to make use of the existing administrative form elements but configure them in certain cases to behave as if they were front-end fields. This seems like a more secure way to handle it as well.

I look forward to testing this.

Thanks again,

Matthew

Link to comment
Share on other sites

I found Janko's FormToWizard code to be one of the simplest ways to implement a multi-page/multi-slide form. Now with the Processwire this just got easier. I tweaked his script a wee bit to work with the API forms described in this thread.

PWformToWizard.js

bunching your fields into fieldsets just as Ryan described above, each fieldset will become a page/slide. Instead using the <legend> tag that Janko uses, the script now generates a legend based on the label, specifically anything that comes before a colon ':'

first add the script:

<script type="text/javascript" src="PWformToWizard.js"></script>
 

then use this javascript to initiallize formToWizard:

$("#SignupForm").formToWizard();
 

the default styles are:

   <style type="text/css">
    body { font-family:Lucida Sans, Arial, Helvetica, Sans-Serif; font-size:13px; margin:20px;}
    #main { width:960px; margin: 0px auto; border:solid 1px #b2b3b5; -moz-border-radius:10px; padding:20px; background-color:#f6f6f6;}
    #header { text-align:center; border-bottom:solid 1px #b2b3b5; margin: 0 0 20px 0; }
    fieldset { border:none; width:320px;}
    legend { font-size:18px; margin:0px; padding:10px 0px; color:#b0232a; font-weight:bold;}
    label { display:block; margin:15px 0 5px;}
    input[type=text], input[type=password] { width:300px; padding:5px; border:solid 1px #000;}
    .prev, .next { background-color:#b0232a; padding:5px 10px; color:#fff; text-decoration:none;}
    .prev:hover, .next:hover { background-color:#000; text-decoration:none;}
    .prev { float:left;}
    .next { float:right;}
    #steps { list-style:none; width:100%; overflow:hidden; margin:0px; padding:0px;}
    #steps li {font-size:24px; float:left; padding:10px; color:#b0b1b3;}
    #steps li span {font-size:11px; display:block;}
    #steps li.current { color:#000;}
    #makeWizard { background-color:#b0232a; color:#fff; padding:5px 10px; text-decoration:none; font-size:18px;}
    #makeWizard:hover { background-color:#000;}
   </style>
   <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
   <script type="text/javascript" src="formToWizard.js"></script>
   <script type="text/javascript">
    $(document).ready(function(){
	    $("#SignupForm").formToWizard({ submitButton: 'SaveAccount' })
    });
   </script>
 

see janko's page for more info~

Now, just to add some responsive jquery validation to each 'next' button...

Edited by neildaemond
updated PWformToWizard-validate.js link
  • Like 7
  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...