├── .gitignore
├── README.md
├── dynamic-fields-on-relationship
├── README.md
├── dynamic-fields-on-relationship.js
├── dynamic-fields-on-relationship.php
└── group_57d9e88cbd4ed.json
├── dynamic-repeater-on-category
├── README.md
├── acf-json
│ ├── group_57c9938e52fab.json
│ ├── group_57c9948e8080e.json
│ └── index.php
├── dynamic-repeater-on-category.js
├── dynamic-repeater-on-category.php
└── extras.php
├── dynamic-select-example
├── README.md
├── cpt-ui-export.txt
├── dynamic-select-on-select.js
├── group_city_fields.json
├── group_post_fields.json
├── group_state_fields.json
├── my-acf-extension.js
└── my-acf-extension.php
├── dynamic-text-based-on-user-select
├── README.md
├── dynamic-text-based-on-user-select.js
├── dynamic-text-based-on-user-select.php
└── group_57ab9d41279f5.json
├── repeater-ajax-load-more
├── README.md
├── group_57cae2b099966.json
├── repeater-ajax-load-more-template-code.php
└── repeater-ajax-load-more.php
└── unique-repeater-checkbox
├── README.md
├── unique-repeater-checkbox-acf57.js
├── unique-repeater-checkbox.js
└── unique-repeater-checkbox.php
/.gitignore:
--------------------------------------------------------------------------------
1 | _notes
2 | config.codekit
3 | .DS_Store
4 | *.LCK
5 | *.svn
6 | __assets/*
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ACf Dynamic AJAX Field Examples
2 |
3 | ## Important Note
4 | ACF In constantly changing. It is possible that any of the examples here may stop working with an update to ACF. I do not actively maintian these examples or continuously monitor them to make sure they are working. This repository was created to hold examples of some things that may help other. If you find that something is not working as expected you can open an issue, however, unless I have the free time the chances of me fixing the issue are small. This is why I will gladly take pull request for fixing problems with compatibility.
5 |
6 | ***MANY OF THESE EXAMPLES CURRENTLY DO NOT WORK WITH ACF >= 5.7.0 AND NEED TO BE REWORKED FOR THIS VERION. THE ONES THAT HAVE BEEN REWORKED HAVE NOTES ABOUT IT. THERE WAS A SIGNIFICANT CHANGE IN ACFs JS API IN VERSION 5.7.0***
7 |
8 | ***These examples only works in ACF5***
9 |
10 | This repo contains several examples of using AJAX to dynamically load fields based on the values
11 | in other fields. See the individual example folders for more information.
12 |
13 | There are also some other examples here that are more complicated than what can be found in my
14 | [ACF Filters & Functions Repo](https://github.com/Hube2/acf-filters-and-functions) becuase each
15 | of these examples requires code in several files.
16 |
17 | The following is a brief explanation of each example
18 |
19 | **dynamic-fields-on-relationship:** This example shows how to load additional fields from a post selected
20 | in a relationship field.
21 |
22 | **dynamic-repeater-on-category:** This example is a bit more complicated than the ones below. It will load a
23 | repeater field located on a post with values from a repeater field of the chosen post category.
24 |
25 | **dynamic-select-example:** *updated JS to work with ACF >= 5.7* Load values into a select field based on a choice made in another select field
26 |
27 | **dynamic-text-based-on-user-select:** Loads text fields based on the selection made for a user field
28 |
29 | **repeater-ajax-load-more:** This is an example of how to create a "Load More" feature for a repeater field.
30 |
31 | **unique-repeater-checkbox:** *updated JS to work with ACF >= 5.7*
32 | This example shows how to create a true/false field in a repeater that will only
33 | allow the field to be checked in one row of the repeater. A multi-row radio field.
34 |
35 |
--------------------------------------------------------------------------------
/dynamic-fields-on-relationship/README.md:
--------------------------------------------------------------------------------
1 | #Dynamic Field on Relationship
2 |
3 | ***This example only works in ACF5***
4 |
5 | In this example we are going to populate some fields based on a selection made in a relationship field. This is
6 | something that has come up several times on the ACF Support Forum. What people want to do is pupulate fields, like
7 | title, except and maybe an image from the related post and allow the editor to change these values for the
8 | relationship.
9 |
10 | Please note, that this example will only deal with a single relationship field that allows only a single choice for
11 | that field. It will not work on something like a repeater sub field to populate other values on the same row or
12 | anything more complicated.
13 |
14 | ## Setup
15 |
16 | In order to set up this example you will need to:
17 |
18 | 1. Create a relationship field
19 | 2. Create a title "text" field to be populated
20 | 3. Create an excerpt "textarea" field to be populated
21 | 4. Create and image field to be populated
22 | 5. Copy the field keys from the fields you've created into the JavaScript code example
23 |
24 | (If you'd like to test this with what I set up I have included the JSON field group in the files)
25 |
26 | ## Code Comments
27 | Look at the files and comments in them for information on what's going on and why.
28 |
29 | Please note, like all my examples in this repo, I use generic names for all files, functions and classes.
30 | These files, functions and classes should be renamed so that they do not conflict with other code. This is meant
31 | as an example only and is not meant to just be copied and pasted into your functions.php file except for the
32 | purpose of seeing this exact example in action.
33 |
34 | Some of the things that this example does that might be usefull in other projects include
35 | * How to get the selected value of a relationship field
36 | * How to do an AJAX request using ACF
37 | * How to populate other fields based on the results of an AJAX request
38 | * How to populate and show an ACF image field
39 |
--------------------------------------------------------------------------------
/dynamic-fields-on-relationship/dynamic-fields-on-relationship.js:
--------------------------------------------------------------------------------
1 |
2 | jQuery(document).ready(function($){
3 | // make sure acf is loaded, it should be, but just in case
4 | if (typeof acf == 'undefined') { return; }
5 |
6 | // extend the acf.ajax object
7 | // you should probably rename this var
8 | var myACFextension = acf.ajax.extend({
9 | events: {
10 | // this data-key must match the field key for the relationship field that
11 | // will trigger the change of the other fields, in this case, the relationship field
12 | // is a Select2 field and we need to jump through a few extra hoops to set
13 | // an event and get the value. When ACF updates the value it triggers a change
14 | // action on a different hidden field than the one that will contain the value
15 | 'change [data-key="field_57d9e8a359b75"] .acf-input input': '_relationship_change',
16 | },
17 |
18 | _relationship_change: function(e) {
19 |
20 | // clear existing values from the fields we will update
21 |
22 | // clear input field
23 | // data-key == field key of title field
24 | $('[data-key="field_57d9e8d659b76"] input').val('');
25 |
26 | // clear textarea field
27 | // date-key == field key of excerpt field
28 | $('[data-key="field_57d9e8f559b77"] textarea').val('');
29 |
30 | // clear image field
31 | // data-key == field key of image field
32 | // target the link that will remove the image and trigger a click
33 | $('[data-key="field_57d9e92159b78"] a[data-name="remove"]').trigger('click');
34 |
35 | // maybe I'm slow, but it took several hours to figure out how to get
36 | // the actual selected value of a relationship field
37 | // this first line gets a list of the hidden input elements
38 | // for our example there should be only one element
39 | var list = e.$el.closest('.acf-field').find('.values .list input[type="hidden"]');
40 | // set the default value
41 | var $value = 0;
42 | if (list.length) {
43 | // if the list lenght > 0
44 | // in our case a lenght of 1
45 | // get the value of the first element
46 | $value = list[0].value;
47 | }
48 |
49 | // if there is no value, exit
50 | if (!$value) {
51 | return;
52 | }
53 |
54 |
55 | // now we can do our ajax request
56 |
57 | // I assume this tests to see if there is already a request
58 | // for this and cancels it if there is
59 | if( this.request) {
60 | this.request.abort();
61 | }
62 |
63 | // I don't know exactly what it does
64 | // acf does it so I copied it
65 | var self = this,
66 | data = this.o;
67 |
68 | // set the ajax action that's set up in php
69 | data.action = 'load_relationship_content';
70 | // set the term id value to be submitted
71 | data.related_post = $value;
72 |
73 | // this gets the post ID that we set when localizing the script
74 | // you should change this object name to something unique
75 | // will will also need to change this object name in the PHO file
76 | // where this script is enqueued
77 | data.post_id = my_acf_extension_object.post_id
78 |
79 | // we need to get information about the image field
80 | // to send that along with the request
81 | data.image_size = $('[data-key="field_57d9e92159b78"]').find('.acf-image-uploader').data('preview_size');
82 |
83 |
84 | // this is another bit I'm not sure about
85 | // copied from ACF
86 | data.exists = [];
87 |
88 | // this the request is copied from ACF
89 | this.request = $.ajax({
90 | url: acf.get('ajaxurl'),
91 | data: acf.prepare_for_ajax(data),
92 | type: 'post',
93 | dataType: 'json',
94 | async: true,
95 | success: function(json){
96 |
97 | if (!json) {
98 | return;
99 | }
100 |
101 | // put the values into the fields
102 | if (json['title']) {
103 | // data-key == field key of title field
104 | $('[data-key="field_57d9e8d659b76"] input').val(json['title']);
105 | }
106 | if (json['excerpt']) {
107 | // date-key == field key of excerpt field
108 | $('[data-key="field_57d9e8f559b77"] textarea').val(json['excerpt']);
109 | }
110 | if (json['image']) {
111 | // data-key == field key of image field
112 | // put the id value into the hidden field
113 | $('[data-key="field_57d9e92159b78"] input[type="hidden"]').val(json['image']['id']);
114 | // put the url into the img element
115 | $('[data-key="field_57d9e92159b78"] img').attr('src', json['image']['url']);
116 | // set the image field to show
117 | $('[data-key="field_57d9e92159b78"]').find('.acf-image-uploader').addClass('has-value');
118 | }
119 | },
120 | error: function(jqXHR, textStatus, error) {
121 | alert (jqXHR+' : '+textStatus+' : '+error);
122 | }
123 | });
124 | },
125 | });
126 |
127 | });
128 |
--------------------------------------------------------------------------------
/dynamic-fields-on-relationship/dynamic-fields-on-relationship.php:
--------------------------------------------------------------------------------
1 | $image_id,
62 | 'url' => $image_details[0]
63 | );
64 | }
65 | }
66 |
67 | // put all the values into an array and return it as json
68 | $array = array(
69 | 'title' => get_field('relationship_title', $post_id),
70 | 'excerpt' => get_field('relationship_excerpt', $post_id),
71 | 'image' => $image
72 | );
73 | echo json_encode($array);
74 | exit;
75 | }
76 |
77 | // this is a different related post
78 | // get the post and set it up
79 | global $post;
80 | $post = get_post($related_post);
81 | setup_postdata($post);
82 |
83 | // get the excerpt
84 | $excerpt = trim($post->post_excerpt);
85 | if (!$excerpt) {
86 | // the excerpt is not set, create on from the content
87 | $excerpt = apply_filters('the_excerpt', $post->post_content);
88 | }
89 |
90 | // get the image if any
91 | $image = false;
92 | $image_id = intval(get_post_thumbnail_id($post->ID));
93 | if ($image_id) {
94 | $image_details = wp_get_attachment_image_src($image_id, $image_size);
95 | if ($image_details) {
96 | $image = array(
97 | 'id' => $image_id,
98 | 'url' => $image_details[0]
99 | );
100 | }
101 | }
102 |
103 | // put all the values into an array and return it as json
104 | $array = array(
105 | 'title' => $post->post_title,
106 | 'excerpt' => $excerpt,
107 | 'image' => $image
108 | );
109 | echo json_encode($array);
110 | exit;
111 |
112 | } // end public function load_content_from_relationship
113 |
114 | public function enqueue_script() {
115 | // enqueue acf extenstion
116 |
117 | // only enqueue the script on the post page where it needs to run
118 | /* *** THIS IS IMPORTANT
119 | ACF uses the same scripts as well as the same field identification
120 | markup (the data-key attribute) if the ACF field group editor
121 | because of this, if you load and run your custom javascript on
122 | the field group editor page it can have unintended side effects
123 | on this page. It is important to alway make sure you're only
124 | loading scripts where you need them.
125 | */
126 | global $post;
127 | if (!$post ||
128 | !isset($post->ID) ||
129 | get_post_type($post->ID) != 'post') {
130 | return;
131 | }
132 |
133 | // the handle should be changed to your own unique handle
134 | $handle = 'my-dynamic-repeater-on-relationship';
135 |
136 | // I'm using this method to set the src because
137 | // I don't know where this file will be located
138 | // you should alter this to use the correct functions
139 | // to get the theme, template or plugin path
140 | // to set the src value to point to the javascript file
141 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/dynamic-fields-on-relationship.js';
142 | // make this script dependent on acf-input
143 | $depends = array('acf-input');
144 |
145 | wp_register_script($handle, $src, $depends);
146 |
147 | // localize the script with the current post id
148 | // we will need the current post ID to get existing
149 | // values from the post
150 |
151 | // you should change this object name to something unique
152 | // will will also need to change this object name in the JS file
153 | $object = 'my_acf_extension_object';
154 |
155 | $data = array('post_id' => $post->ID);
156 | wp_localize_script($handle, $object, $data);
157 |
158 | wp_enqueue_script($handle);
159 | } // end public function enqueue_script
160 |
161 | } // end class my_dynmamic_field_on_relationship
162 |
163 | ?>
164 |
--------------------------------------------------------------------------------
/dynamic-fields-on-relationship/group_57d9e88cbd4ed.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57d9e88cbd4ed",
3 | "title": "Dynamic Field on Relationship Example",
4 | "fields": [
5 | {
6 | "key": "field_57d9e8a359b75",
7 | "label": "relationship",
8 | "name": "relationship",
9 | "type": "relationship",
10 | "instructions": "This is the relationship field. When you select a related post in this field the title, excerpt and featured image from that post will be inserted into the fields below.",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "post_type": [
19 | "post"
20 | ],
21 | "taxonomy": [],
22 | "filters": [
23 | "search",
24 | "post_type",
25 | "taxonomy"
26 | ],
27 | "elements": "",
28 | "min": "",
29 | "max": 1,
30 | "return_format": "object"
31 | },
32 | {
33 | "key": "field_57d9e8d659b76",
34 | "label": "relationship title",
35 | "name": "relationship_title",
36 | "type": "text",
37 | "instructions": "The title of the related post will be put here",
38 | "required": 0,
39 | "conditional_logic": 0,
40 | "wrapper": {
41 | "width": "",
42 | "class": "",
43 | "id": ""
44 | },
45 | "default_value": "",
46 | "placeholder": "",
47 | "prepend": "",
48 | "append": "",
49 | "maxlength": ""
50 | },
51 | {
52 | "key": "field_57d9e8f559b77",
53 | "label": "relationship excerpt",
54 | "name": "relationship_excerpt",
55 | "type": "textarea",
56 | "instructions": "The excerpt from the related post will be put there",
57 | "required": 0,
58 | "conditional_logic": 0,
59 | "wrapper": {
60 | "width": "",
61 | "class": "",
62 | "id": ""
63 | },
64 | "default_value": "",
65 | "placeholder": "",
66 | "maxlength": "",
67 | "rows": "",
68 | "new_lines": "wpautop"
69 | },
70 | {
71 | "key": "field_57d9e92159b78",
72 | "label": "relationship image",
73 | "name": "relationship_image",
74 | "type": "image",
75 | "instructions": "The featured image from the related post will go here",
76 | "required": 0,
77 | "conditional_logic": 0,
78 | "wrapper": {
79 | "width": "",
80 | "class": "",
81 | "id": ""
82 | },
83 | "return_format": "array",
84 | "preview_size": "thumbnail",
85 | "library": "all",
86 | "min_width": "",
87 | "min_height": "",
88 | "min_size": "",
89 | "max_width": "",
90 | "max_height": "",
91 | "max_size": "",
92 | "mime_types": ""
93 | }
94 | ],
95 | "location": [
96 | [
97 | {
98 | "param": "post_type",
99 | "operator": "==",
100 | "value": "post"
101 | }
102 | ]
103 | ],
104 | "menu_order": 0,
105 | "position": "normal",
106 | "style": "default",
107 | "label_placement": "top",
108 | "instruction_placement": "label",
109 | "hide_on_screen": "",
110 | "active": 1,
111 | "description": "",
112 | "modified": 1473956949
113 | }
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/README.md:
--------------------------------------------------------------------------------
1 | #Dynamic Repeater Base on Term
2 |
3 | ***This example only works in ACF5***
4 |
5 | In this example we will create a repeater field on a Post that is dynamically populated based on
6 | the selection of a term in a taxonomy. The content of the repeater will be taken from a repeater
7 | that attached to the term.
8 |
9 | The repeater on the term allows you to add a list of "Features". On the post these "Features" are
10 | dynamically added and require adding a "Value" for each "Feature"
11 |
12 | See the comments in the file "extras.php" for more information on how the post type and taxonomy
13 | are set up. Please make sure you read the important note in this file about removing the standard
14 | WP taxonomy meta box when registering a taxonomy.
15 |
16 | See comments in other files to see how it works.
17 |
18 | Please not, like all my examples in this repo, I use generic names for all files, functions and classes.
19 | These files, functions and classes should be renamed so that they do not conflict with other code.
20 | There are specific things that must be edited to match your use, for example, field keys and such.
21 |
22 | If you want to try out this example you will need to create some terms in the taxonomy and populate
23 | the "Features" repeater with some content.
24 |
25 | This example has a lot of things in it that people might find useful.
26 | * How to dynamically delete all the rows of a repeater
27 | * How to dynamically add rows to a repeater
28 | * How to dynamically populate the sub fields on each row of a repeater
29 | * and of course, how to do an AJAX request based on a taxonomy field
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/acf-json/group_57c9938e52fab.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57c9938e52fab",
3 | "title": "Term Repeater for Example",
4 | "fields": [
5 | {
6 | "key": "field_57c99408fefa1",
7 | "label": "Features",
8 | "name": "features",
9 | "type": "repeater",
10 | "instructions": "",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "collapsed": "",
19 | "min": "",
20 | "max": "",
21 | "layout": "table",
22 | "button_label": "Add Row",
23 | "sub_fields": [
24 | {
25 | "key": "field_57c99428fefa2",
26 | "label": "Feature",
27 | "name": "feature",
28 | "type": "text",
29 | "instructions": "",
30 | "required": 0,
31 | "conditional_logic": 0,
32 | "wrapper": {
33 | "width": "",
34 | "class": "",
35 | "id": ""
36 | },
37 | "default_value": "",
38 | "placeholder": "",
39 | "prepend": "",
40 | "append": "",
41 | "maxlength": ""
42 | }
43 | ]
44 | }
45 | ],
46 | "location": [
47 | [
48 | {
49 | "param": "taxonomy",
50 | "operator": "==",
51 | "value": "sprocket-category"
52 | }
53 | ]
54 | ],
55 | "menu_order": 0,
56 | "position": "normal",
57 | "style": "default",
58 | "label_placement": "top",
59 | "instruction_placement": "label",
60 | "hide_on_screen": "",
61 | "active": 1,
62 | "description": "",
63 | "modified": 1472828549
64 | }
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/acf-json/group_57c9948e8080e.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57c9948e8080e",
3 | "title": "Features for Example",
4 | "fields": [
5 | {
6 | "key": "field_57c994a6d8ee5",
7 | "label": "Category",
8 | "name": "category",
9 | "type": "taxonomy",
10 | "instructions": "",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "taxonomy": "sprocket-category",
19 | "field_type": "select",
20 | "allow_null": 0,
21 | "add_term": 1,
22 | "save_terms": 1,
23 | "load_terms": 1,
24 | "return_format": "id",
25 | "multiple": 0
26 | },
27 | {
28 | "key": "field_57c994f2d8ee6",
29 | "label": "Features",
30 | "name": "features",
31 | "type": "repeater",
32 | "instructions": "",
33 | "required": 0,
34 | "conditional_logic": 0,
35 | "wrapper": {
36 | "width": "",
37 | "class": "",
38 | "id": ""
39 | },
40 | "collapsed": "",
41 | "min": "",
42 | "max": "",
43 | "layout": "table",
44 | "button_label": "Add Row",
45 | "sub_fields": [
46 | {
47 | "key": "field_57c99507d8ee7",
48 | "label": "Feature",
49 | "name": "feature",
50 | "type": "text",
51 | "instructions": "",
52 | "required": 0,
53 | "conditional_logic": 0,
54 | "wrapper": {
55 | "width": "",
56 | "class": "",
57 | "id": ""
58 | },
59 | "default_value": "",
60 | "placeholder": "",
61 | "prepend": "",
62 | "append": "",
63 | "maxlength": ""
64 | },
65 | {
66 | "key": "field_57c99515d8ee8",
67 | "label": "Value",
68 | "name": "value",
69 | "type": "text",
70 | "instructions": "",
71 | "required": 1,
72 | "conditional_logic": 0,
73 | "wrapper": {
74 | "width": "",
75 | "class": "",
76 | "id": ""
77 | },
78 | "default_value": "",
79 | "placeholder": "",
80 | "prepend": "",
81 | "append": "",
82 | "maxlength": ""
83 | }
84 | ]
85 | }
86 | ],
87 | "location": [
88 | [
89 | {
90 | "param": "post_type",
91 | "operator": "==",
92 | "value": "sprocket"
93 | }
94 | ]
95 | ],
96 | "menu_order": 0,
97 | "position": "normal",
98 | "style": "default",
99 | "label_placement": "top",
100 | "instruction_placement": "label",
101 | "hide_on_screen": "",
102 | "active": 1,
103 | "description": "",
104 | "modified": 1472828708
105 | }
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/acf-json/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Untitled Document
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/dynamic-repeater-on-category.js:
--------------------------------------------------------------------------------
1 |
2 | jQuery(document).ready(function($){
3 | // make sure acf is loaded, it should be, but just in case
4 | if (typeof acf == 'undefined') { return; }
5 |
6 | // extend the acf.ajax object
7 | // you should probably rename this var
8 | var myACFextension = acf.ajax.extend({
9 | events: {
10 | // this data-key must match the field key taxonomy field
11 | // we want to trigger the change
12 | // in this case, the taxonomy field is a Select2 field
13 | // and we want the value of a hidden field and not the select field
14 | 'change [data-key="field_57c994a6d8ee5"] input[type="hidden"]': '_change_term',
15 | // this entry is to cause the field to update on page load
16 | 'ready [data-key="field_57c994a6d8ee5"] input[type="hidden"]': '_change_term',
17 |
18 | // for this example we also want to hide all of the "add row" buttons
19 | // because we only want the user to be able to set existing values
20 | // and not add new ones so we'll add another ready function
21 | // that will do this maintenance, this is our own special action
22 | // setup is not a real action, we're creating a new one
23 | 'setup [data-key="field_57c994a6d8ee5"] input[type="hidden"]': '_setup',
24 | },
25 |
26 | // this is our function that will perform the
27 | // ajax request when the state value is changed
28 | _change_term: function(e){
29 |
30 | // get the value of the taxonomy field
31 | $value = e.$el.val();
32 |
33 | // remove any existing rows of the repeater, except the clone row
34 | // by triggering each row's remove-row click event
35 | // the data-key is the field key of the repeater
36 | $('div[data-key="field_57c994f2d8ee6"] tr a[data-event="remove-row"]').not('div[data-key="field_57c994f2d8ee6"] tr.acf-clone a[data-event="remove-row"]').trigger('click');
37 |
38 |
39 |
40 | // I assume this tests to see if there is already a request
41 | // for this and cancels it if there is
42 | if( this.request) {
43 | this.request.abort();
44 | }
45 |
46 | // I don't know exactly what it does
47 | // acf does it so I copied it
48 | var self = this,
49 | data = this.o;
50 |
51 | // set the ajax action that's set up in php
52 | data.action = 'load_features_from_term';
53 | // set the term id value to be submitted
54 | data.term_id = $value;
55 |
56 | // this gets the post ID that we set when localizing the script
57 | // you should change this object name to something unique
58 | // will will also need to change this object name in the PHO file
59 | // where this script is enqueued
60 | data.post_id = my_acf_extension_object.post_id
61 |
62 | // this is another bit I'm not sure about
63 | // copied from ACF
64 | data.exists = [];
65 |
66 | // this the request is copied from ACF
67 | this.request = $.ajax({
68 | url: acf.get('ajaxurl'),
69 | data: acf.prepare_for_ajax(data),
70 | type: 'post',
71 | dataType: 'json',
72 | async: true,
73 | success: function(json){
74 |
75 | if (!json) {
76 | return;
77 | }
78 | // add enough rows to the repeater to hold the values
79 | var len = json.length;
80 | for (i=0; i=0; i-=1) {
100 | // get the ID of this item
101 | var id = feature_list[i].getAttribute('data-id');
102 | // populate the featur
103 | $('div[data-key="field_57c994f2d8ee6"] tr[data-id="'+id+'"] td[data-key="field_57c99507d8ee7"] input').val(json[json_item]['field_57c99507d8ee7']);
104 | // populate the value
105 | $('div[data-key="field_57c994f2d8ee6"] tr[data-id="'+id+'"] td[data-key="field_57c99515d8ee8"] input').val(json[json_item]['field_57c99515d8ee8']);
106 | // decrease json item
107 | json_item-=1;
108 | }
109 | // last thing to do is trigger setup to make sure we keep the add/remvove buttons hidden
110 | $('[data-key="field_57c994a6d8ee5"] input').trigger('setup');
111 | }
112 | });
113 | }, // end _change_term
114 |
115 | // this function will hide the add/remove rows buttons
116 | _setup: function(e) {
117 | // hide all add/delete row links
118 | // this data key is the key for the repeater field
119 | $('div[data-key="field_57c994f2d8ee6"] a[data-event="remove-row"]').css('display', 'none');
120 | $('div[data-key="field_57c994f2d8ee6"] a[data-event="add-row"]').css('display', 'none');
121 | }, // end _setup
122 | });
123 |
124 | // triger the setup action on page load
125 | $('[data-key="field_57c994a6d8ee5"] input').trigger('setup');
126 | });
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/dynamic-repeater-on-category.php:
--------------------------------------------------------------------------------
1 | get_sub_field('feature'),
76 | 'field_57c99515d8ee8' => get_sub_field('value')
77 | );
78 | } // end while have rows
79 | } // end if have rows
80 | } // end if is current term
81 | //print_r($values); exit;
82 | if (count($values)) {
83 | // values from post, return them
84 | echo json_encode($values);
85 | exit;
86 | }
87 | // no existing value
88 | // get features from term
89 | if (have_rows('features', $taxonomy.'_'.$term_id)) {
90 | while (have_rows('features', $taxonomy.'_'.$term_id)) {
91 | the_row();
92 | $values[] = array(
93 | // key value pairs for fields to be updated
94 | // by callback function
95 | 'field_57c99507d8ee7' => get_sub_field('feature'),
96 | 'field_57c99515d8ee8' => ''
97 | );
98 | } // end while have rows
99 | } // end if have rows
100 |
101 | echo json_encode($values);
102 | exit;
103 |
104 | } // end public function load_features_from_term
105 |
106 | public function enqueue_script() {
107 | // enqueue acf extenstion
108 |
109 | // only enqueue the script on the post page where it needs to run
110 | /* *** THIS IS IMPORTANT
111 | ACF uses the same scripts as well as the same field identification
112 | markup (the data-key attribute) if the ACF field group editor
113 | because of this, if you load and run your custom javascript on
114 | the field group editor page it can have unintended side effects
115 | on this page. It is important to alway make sure you're only
116 | loading scripts where you need them.
117 | */
118 | global $post;
119 | if (!$post ||
120 | !isset($post->ID) ||
121 | get_post_type($post->ID) != 'sprocket') {
122 | return;
123 | }
124 |
125 | // the handle should be changed to your own unique handle
126 | $handle = 'my-acf-extension';
127 |
128 | // I'm using this method to set the src because
129 | // I don't know where this file will be located
130 | // you should alter this to use the correct fundtions
131 | // to set the src value to point to the javascript file
132 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/dynamic-repeater-on-category.js';
133 | // make this script dependent on acf-input
134 | $depends = array('acf-input');
135 |
136 | wp_register_script($handle, $src, $depends);
137 |
138 | // localize the script with the current post id
139 | // we will need the current post ID to get existing
140 | // values from the post
141 |
142 | // you should change this object name to something unique
143 | // will will also need to change this object name in the JS file
144 | $object = 'my_acf_extension_object';
145 |
146 | $data = array('post_id' => $post->ID);
147 | wp_localize_script($handle, $object, $data);
148 |
149 | wp_enqueue_script($handle);
150 | } // end public function enqueue_script
151 |
152 | } // end class my_dynamic_acf_extension
153 |
154 | ?>
--------------------------------------------------------------------------------
/dynamic-repeater-on-category/extras.php:
--------------------------------------------------------------------------------
1 | 'Sprockets',
52 | 'singular_name' => 'Sproket',
53 | );
54 | $taxonomies = array(
55 | // this is the taxonomy we'll set up below
56 | 'sprocket-category'
57 | );
58 | $args = array(
59 | 'label' => 'Sprockets',
60 | 'labels' => $labels,
61 | 'description' => '',
62 | 'public' => true,
63 | 'show_ui' => true,
64 | 'show_in_rest' => false,
65 | 'rest_base' => '',
66 | 'has_archive' => true,
67 | 'show_in_menu' => true,
68 | 'exclude_from_search' => false,
69 | 'capability_type' => 'post',
70 | 'map_meta_cap' => true,
71 | 'hierarchical' => false,
72 | 'rewrite' => array(
73 | 'slug' => 'blunt-product',
74 | 'with_front' => true
75 | ),
76 | 'query_var' => true,
77 | 'supports' => array(
78 | 'title',
79 | 'editor',
80 | 'thumbnail',
81 | 'excerpt',
82 | 'custom-fields',
83 | 'comments',
84 | 'revisions',
85 | 'author',
86 | 'page-attributes',
87 | 'post-formats'
88 | ),
89 | 'taxonomies' => $taxonomies,
90 | );
91 | register_post_type($post_type, $args );
92 |
93 |
94 | $labels = array(
95 | 'name' => 'Sprocket Categories',
96 | 'singular_name' => 'Sprocket Category',
97 | );
98 | $args = array(
99 | 'label' => 'Sprocket Categories',
100 | 'labels' => $labels,
101 | 'public' => true,
102 | 'hierarchical' => true,
103 | 'label' => 'Sprocket Categories',
104 | 'show_ui' => true,
105 | 'query_var' => true,
106 | 'rewrite' => array(
107 | 'slug' => 'sprocket-category',
108 | 'with_front' => true
109 | ),
110 | 'show_admin_column' => true,
111 | 'show_in_rest' => false,
112 | 'rest_base' => '',
113 | 'show_in_quick_edit' => true,
114 |
115 | /*
116 | *** IMPORTANT NOTE ***
117 | setting meta_box_cb to an empty string
118 | causes WP to not show the standard meta box
119 | for selecting the terms of a taxonomy
120 | this is important for this example
121 | because the term must be selected using
122 | the ACF field and not the meta box
123 | if the term is selected in the meta box
124 | none of the code in this example will work
125 | */
126 | 'meta_box_cb' => ''
127 | );
128 | register_taxonomy('sprocket-category', array($post_type), $args);
129 |
130 | } // end public function register
131 |
132 | } // end class my_dynamic_acf_extension_extras
133 |
134 | ?>
--------------------------------------------------------------------------------
/dynamic-select-example/README.md:
--------------------------------------------------------------------------------
1 | # ACf Dynamic AJAX Select Example
2 |
3 | ***This example only works in ACF5***
4 |
5 | This repo is an example of how to dynamically load an ACF select field based on the selection made in
6 | another ACF select field. This example can be used as the basis of understanding how to extend ACF to
7 | use AJAX for the creation of dyanmic fields. Every field type in ACF will need slightly different code
8 | to get this to work. This is presented in the hope that it will help others to better understand how
9 | to extend the functionality of ACF.
10 |
11 | See the comments in the files for comments about the code and what it is doing. Please note that the
12 | PHP in this example uses OOP rather than simple functions.
13 |
14 | In this example I have set up two custom post types. The first custom post type is named 'State' and it
15 | is used to populate a list of US States. The second custom post type is named 'City' and it is used to
16 | populate a list of cities that are located in each state. The City post type includes a select
17 | field to select a stated from the list of states.
18 |
19 | The file cpt-ui-export.txt is an export from the plugin
20 | [Custom Post Type UI](https://github.com/WebDevStudios/custom-post-type-ui) which is what I generally
21 | use to set up custom post types. It can be used if you want to test out this example.
22 |
23 | The JSON files are exports from ACF of the three field groups I used for this excercise. You can import these
24 | if you want to test this example.
25 |
26 | If you want to test this you'll need to add some states and cities yourself
27 |
28 | The goal is then to add two select fields to the "Post" post type. A user will select a state in the state
29 | select field and this will cause the cities in that state to by dynamically populated with its related cities.
30 |
31 | For the rest of the explanation see the comments in the PHP and JS files
32 |
--------------------------------------------------------------------------------
/dynamic-select-example/cpt-ui-export.txt:
--------------------------------------------------------------------------------
1 | {"state":{"name":"state","label":"States","singular_label":"State","description":"","public":"true","show_ui":"true","show_in_nav_menus":"true","show_in_rest":"false","rest_base":"","has_archive":"true","has_archive_string":"","exclude_from_search":"false","capability_type":"post","hierarchical":"false","rewrite":"true","rewrite_slug":"","rewrite_withfront":"true","query_var":"true","query_var_slug":"","menu_position":"","show_in_menu":"true","show_in_menu_string":"","menu_icon":"","supports":["title","editor","thumbnail","excerpt","custom-fields","revisions"],"taxonomies":[],"labels":{"menu_name":"","all_items":"","add_new":"","add_new_item":"","edit_item":"","new_item":"","view_item":"","search_items":"","not_found":"","not_found_in_trash":"","parent":"","featured_image":"","set_featured_image":"","remove_featured_image":"","use_featured_image":"","archives":"","insert_into_item":"","uploaded_to_this_item":"","filter_items_list":"","items_list_navigation":"","items_list":""},"custom_supports":""},"city":{"name":"city","label":"Cities","singular_label":"City","description":"","public":"true","show_ui":"true","show_in_nav_menus":"true","show_in_rest":"false","rest_base":"","has_archive":"true","has_archive_string":"","exclude_from_search":"false","capability_type":"post","hierarchical":"false","rewrite":"true","rewrite_slug":"","rewrite_withfront":"true","query_var":"true","query_var_slug":"","menu_position":"","show_in_menu":"true","show_in_menu_string":"","menu_icon":"","supports":["title","editor","thumbnail","excerpt","custom-fields","revisions","page-attributes"],"taxonomies":[],"labels":{"menu_name":"","all_items":"","add_new":"","add_new_item":"","edit_item":"","new_item":"","view_item":"","search_items":"","not_found":"","not_found_in_trash":"","parent":"","featured_image":"","set_featured_image":"","remove_featured_image":"","use_featured_image":"","archives":"","insert_into_item":"","uploaded_to_this_item":"","filter_items_list":"","items_list_navigation":"","items_list":""},"custom_supports":""}}
--------------------------------------------------------------------------------
/dynamic-select-example/dynamic-select-on-select.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | updated JS file for use with ACF >= 5.7.0
4 | */
5 |
6 |
7 | jQuery(document).ready(function($){
8 | if (typeof acf == 'undefined') { return; }
9 |
10 | /*
11 | In ACF >= 5.7.0 the acf.ajax object no longer exists so we can't extend it
12 | Instead we need to attach out event the old fashioned way
13 | and we need to do it using $(document).on because the element may
14 | be added dynamically and this is the only way to add events
15 | */
16 |
17 | $(document).on('change', '[data-key="field_579376f522130"] .acf-input select', function(e) {
18 | // we are not going to do anyting in this anonymous function
19 | // the reason is explained below
20 | // call function, we need to pass the event and jQuery object
21 | update_cities_on_state_change(e, $);
22 | });
23 | $('[data-key="field_579376f522130"] .acf-input select').trigger('ready');
24 | });
25 |
26 | // the actual function is separate from the above change function
27 | // the reason for this is that "this" has no real meaning in an anonymous
28 | // function as each call is a new JS object and we need "this" in order
29 | // to be to abort previous AJAX requests
30 | function update_cities_on_state_change(e, $) {
31 | if (this.request) {
32 | // if a recent request has been made abort it
33 | this.request.abort();
34 | }
35 |
36 | // get the city select field, and remove all exisiting choices
37 | var city_select = $('[data-key="field_5793770922131"] select');
38 | city_select.empty();
39 |
40 | // get the target of the event and then get the value of that field
41 | var target = $(e.target);
42 | var state = target.val();
43 |
44 | if (!state) {
45 | // no state selected
46 | // don't need to do anything else
47 | return;
48 | }
49 |
50 | // set and prepare data for ajax
51 | var data = {
52 | action: 'load_city_field_choices',
53 | state: state
54 | }
55 |
56 | // call the acf function that will fill in other values
57 | // like post_id and the acf nonce
58 | data = acf.prepareForAjax(data);
59 |
60 | // make ajax request
61 | // instead of going through the acf.ajax object to make requests like in <5.7
62 | // we need to do a lot of the work ourselves, but other than the method that's called
63 | // this has not changed much
64 | this.request = $.ajax({
65 | url: acf.get('ajaxurl'), // acf stored value
66 | data: data,
67 | type: 'post',
68 | dataType: 'json',
69 | success: function(json) {
70 | if (!json) {
71 | return;
72 | }
73 | // add the new options to the city field
74 | for(i=0; i'+json[i]['label']+'';
76 | city_select.append(city_item);
77 | }
78 | }
79 | });
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/dynamic-select-example/group_city_fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57937686d8c23",
3 | "title": "City Fields",
4 | "fields": [
5 | {
6 | "key": "field_579376941cecc",
7 | "label": "State",
8 | "name": "state",
9 | "type": "select",
10 | "instructions": "",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "choices": {
19 | "this will be dynamically generated": "this will be dynamically generated"
20 | },
21 | "default_value": [],
22 | "allow_null": 0,
23 | "multiple": 0,
24 | "ui": 0,
25 | "ajax": 0,
26 | "placeholder": "",
27 | "disabled": 0,
28 | "readonly": 0
29 | }
30 | ],
31 | "location": [
32 | [
33 | {
34 | "param": "post_type",
35 | "operator": "==",
36 | "value": "city"
37 | }
38 | ]
39 | ],
40 | "menu_order": 0,
41 | "position": "normal",
42 | "style": "default",
43 | "label_placement": "top",
44 | "instruction_placement": "label",
45 | "hide_on_screen": "",
46 | "active": 1,
47 | "description": "",
48 | "modified": 1469282192
49 | }
--------------------------------------------------------------------------------
/dynamic-select-example/group_post_fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_579376df468e7",
3 | "title": "Post Fields for City\/State",
4 | "fields": [
5 | {
6 | "key": "field_579376f522130",
7 | "label": "State",
8 | "name": "state",
9 | "type": "select",
10 | "instructions": "",
11 | "required": 1,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "choices": {
19 | "this will be dynamically generated": "this will be dynamically generated"
20 | },
21 | "default_value": [],
22 | "allow_null": 0,
23 | "multiple": 0,
24 | "ui": 0,
25 | "ajax": 0,
26 | "placeholder": "",
27 | "disabled": 0,
28 | "readonly": 0
29 | },
30 | {
31 | "key": "field_5793770922131",
32 | "label": "City",
33 | "name": "city",
34 | "type": "select",
35 | "instructions": "",
36 | "required": 1,
37 | "conditional_logic": 0,
38 | "wrapper": {
39 | "width": "",
40 | "class": "",
41 | "id": ""
42 | },
43 | "choices": {
44 | "this will be dynamically generated": "this will be dynamically generated"
45 | },
46 | "default_value": [],
47 | "allow_null": 0,
48 | "multiple": 0,
49 | "ui": 0,
50 | "ajax": 0,
51 | "placeholder": "",
52 | "disabled": 0,
53 | "readonly": 0
54 | }
55 | ],
56 | "location": [
57 | [
58 | {
59 | "param": "post_type",
60 | "operator": "==",
61 | "value": "post"
62 | }
63 | ]
64 | ],
65 | "menu_order": 0,
66 | "position": "normal",
67 | "style": "default",
68 | "label_placement": "top",
69 | "instruction_placement": "label",
70 | "hide_on_screen": "",
71 | "active": 1,
72 | "description": "",
73 | "modified": 1469282196
74 | }
--------------------------------------------------------------------------------
/dynamic-select-example/group_state_fields.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57937657a201f",
3 | "title": "State Fields",
4 | "fields": [
5 | {
6 | "key": "field_5793766ab8167",
7 | "label": "Sate Abbreiviation",
8 | "name": "sate_abbreiviation",
9 | "type": "text",
10 | "instructions": "",
11 | "required": 1,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "default_value": "",
19 | "placeholder": "",
20 | "prepend": "",
21 | "append": "",
22 | "maxlength": "",
23 | "readonly": 0,
24 | "disabled": 0
25 | }
26 | ],
27 | "location": [
28 | [
29 | {
30 | "param": "post_type",
31 | "operator": "==",
32 | "value": "state"
33 | }
34 | ]
35 | ],
36 | "menu_order": 0,
37 | "position": "normal",
38 | "style": "default",
39 | "label_placement": "top",
40 | "instruction_placement": "label",
41 | "hide_on_screen": "",
42 | "active": 1,
43 | "description": "",
44 | "modified": 1469282194
45 | }
--------------------------------------------------------------------------------
/dynamic-select-example/my-acf-extension.js:
--------------------------------------------------------------------------------
1 |
2 | jQuery(document).ready(function($){
3 | // make sure acf is loaded, it should be, but just in case
4 | if (typeof acf == 'undefined') { return; }
5 |
6 | // extend the acf.ajax object
7 | // you should probably rename this var
8 | var myACFextension = acf.ajax.extend({
9 | events: {
10 | // this data-key must match the field key for the state field on the post page where
11 | // you want to dynamically load the cities when the state is changed
12 | 'change [data-key="field_579376f522130"] select': '_state_change',
13 | // this entry is to cause the city field to be updated when the page is loaded
14 | 'ready [data-key="field_579376f522130"] select': '_state_change',
15 | },
16 |
17 | // this is our function that will perform the
18 | // ajax request when the state value is changed
19 | _state_change: function(e){
20 |
21 | // clear the city field options
22 | // the data-key is the field key of the city field on post
23 | var $select = $('[data-key="field_5793770922131"] select');
24 | $select.empty();
25 |
26 | // get the state selection
27 | var $value = e.$el.val();
28 |
29 | // a lot of the following code is copied directly
30 | // from ACF and modified for our purpose
31 |
32 | // I assume this tests to see if there is already a request
33 | // for this and cancels it if there is
34 | if( this.state_request) {
35 | this.state_request.abort();
36 | }
37 |
38 | // I don't know exactly what it does
39 | // acf does it so I copied it
40 | var self = this,
41 | data = this.o;
42 |
43 | // set the ajax action that's set up in php
44 | data.action = 'load_city_field_choices';
45 | // set the state value to be submitted
46 | data.state = $value;
47 |
48 | // this is another bit I'm not sure about
49 | // copied from ACF
50 | data.exists = [];
51 |
52 | // this the request is copied from ACF
53 | this.state_request = $.ajax({
54 | url: acf.get('ajaxurl'),
55 | data: acf.prepare_for_ajax(data),
56 | type: 'post',
57 | dataType: 'json',
58 | async: true,
59 | success: function(json){
60 | // function to update the city field choices
61 |
62 | // get the city field
63 | // the city field key that we want to update
64 | var $select = $('[data-key="field_5793770922131"] select');
65 |
66 | // add options to the city field
67 | for (i=0; i'+json[i]['label']+'';
69 | $select.append($item);
70 | }
71 | }
72 | });
73 | },
74 | });
75 |
76 | // triger the ready action on page load
77 | $('[data-key="field_579376f522130"] select').trigger('ready');
78 | });
--------------------------------------------------------------------------------
/dynamic-select-example/my-acf-extension.php:
--------------------------------------------------------------------------------
1 | ID) ||
29 | (get_post_type($post->ID) != 'post' && get_post_type($post->ID) != 'city')) {
30 | return $field;
31 | }
32 |
33 | // get states
34 | $args = array(
35 | 'post_type' => 'state',
36 | 'posts_per_page' => -1,
37 | 'orderby' => 'title',
38 | 'order' => 'ASC'
39 | );
40 | $query = new WP_Query($args);
41 | $choices = array('' => '-- State --');
42 | if (count($query->posts)) {
43 | // populate choices
44 | foreach ($query->posts as $state) {
45 | $choices[$state->ID] = $state->post_title;
46 | }
47 | }
48 | $field['choices'] = $choices;
49 | return $field;
50 | } // end public function load_state_field_choices
51 |
52 | public function load_city_field_choices($field) {
53 | // this function dynamically loads city field choices
54 | // based on the currently saved state
55 |
56 | // I only want to do this on Posts
57 | global $post;
58 | if (!$post ||
59 | !isset($post->ID) ||
60 | get_post_type($post->ID) != 'post') {
61 | return $field;
62 | }
63 | // get the state post id
64 | // I generally use get_post_meta() instead of get_field()
65 | // when building functionality, but get_field() could be
66 | // subsitited here
67 | $state = intval(get_post_meta($post->ID, 'state', true));
68 | $cities = $this->get_cities($state);
69 | $field['choices'] = $cities;
70 | return $field;
71 | } // end public funciton load_city_field_choices
72 |
73 | public function enqueue_script() {
74 | // enqueue acf extenstion
75 |
76 | // only enqueue the script on the post page where it needs to run
77 | /* *** THIS IS IMPORTANT
78 | ACF uses the same scripts as well as the same field identification
79 | markup (the data-key attribute) if the ACF field group editor
80 | because of this, if you load and run your custom javascript on
81 | the field group editor page it can have unintended side effects
82 | on this page. It is important to alway make sure you're only
83 | loading scripts where you need them.
84 | */
85 | global $post;
86 | if (!$post ||
87 | !isset($post->ID) ||
88 | get_post_type($post->ID) != 'post') {
89 | return;
90 | }
91 |
92 | $handle = 'my-acf-extension';
93 | $version = acf_get_setting('version');
94 | if (version_compare($acf_version, '5.7.0', '<')) {
95 | // I'm using this method to set the src because
96 | // I don't know where this file will be located
97 | // you should alter this to use the correct fundtions
98 | // to set the src value to point to the javascript file
99 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/my-acf-extension.js';
100 | } else {
101 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/dynamic-select-on-select.js';
102 | }
103 | // make this script dependent on acf-input
104 | $depends = array('acf-input');
105 |
106 | wp_enqueue_script($handle, $src, $depends);
107 | } // end public function enqueue_script
108 |
109 | public function ajax_load_city_field_choices() {
110 | // this funtion is called by AJAX to load cities
111 | // based on state selecteion
112 |
113 | // we can use the acf nonce to verify
114 | if (!wp_verify_nonce($_POST['nonce'], 'acf_nonce')) {
115 | die();
116 | }
117 | $state = 0;
118 | if (isset($_POST['state'])) {
119 | $state = intval($_POST['state']);
120 | }
121 | $cities = $this->get_cities($state);
122 | $choices = array();
123 | foreach ($cities as $value => $label) {
124 | $choices[] = array('value' => $value, 'label' => $label);
125 | }
126 | echo json_encode($choices);
127 | exit;
128 | } // end public function ajax_load_city_field_choices
129 |
130 | private function get_cities($post_id) {
131 | // $post_id is post ID of state post
132 | // get all cities related to the state
133 | $args = array(
134 | 'post_type' => 'city',
135 | 'posts_per_page' => -1,
136 | 'orderby' => 'title',
137 | 'order' => 'ASC',
138 | 'meta_query' => array(
139 | array(
140 | 'key' => 'state',
141 | 'value' => $post_id
142 | )
143 | )
144 | );
145 | $query = new WP_Query($args);
146 | $choices = array('' => '-- City --');
147 | if (count($query->posts)) {
148 | // populate choices
149 | foreach ($query->posts as $post) {
150 | $choices[$post->ID] = $post->post_title;
151 | }
152 | }
153 | return $choices;
154 | } // end private function get_cities
155 |
156 | } // end class my_acf_extension
157 |
158 | ?>
159 |
--------------------------------------------------------------------------------
/dynamic-text-based-on-user-select/README.md:
--------------------------------------------------------------------------------
1 | # ACf Text Fields based on User field
2 |
3 | ***This example only workds in ACF5***
4 |
5 | This repo is an example of how to dynamically load some ACF text fields based on the selection made in
6 | another user type field. This is another example can be used as the basis of understanding how to
7 | extend ACF to use AJAX for the creation of dyanmic fields. Every field type in ACF will need slightly
8 | different code to get this to work. This is presented in the hope that it will help others to better
9 | understand how to extend the functionality of ACF.
10 |
11 | Please note that this will only work with a user field that allows a single selection.
12 |
13 | See the comments in the files for comments about the code and what it is doing. Please note that the
14 | PHP in this example uses OOP rather than simple functions.
15 |
16 | In this example I have set up a field group that includes a User Type field. The field group is attached
17 | to the "Post" post type. The JSON file is an export of this field group from ACF. You can import this
18 | if you want to test this example. You'll need to populate some users on your site to see it in action.
19 |
20 | For the rest of the explanation see the comments in the PHP and JS files
21 |
--------------------------------------------------------------------------------
/dynamic-text-based-on-user-select/dynamic-text-based-on-user-select.js:
--------------------------------------------------------------------------------
1 |
2 | jQuery(document).ready(function($){
3 | // make sure acf is loaded, it should be, but just in case
4 | if (typeof acf == 'undefined') { return; }
5 |
6 | // extend the acf.ajax object
7 | // you should probably rename this var
8 | var myUserFieldextension = acf.ajax.extend({
9 | events: {
10 | // this data-key must match the field key for the user field on the post page where
11 | // you want to dynamically load additional user information
12 | 'change [data-key="field_57ab9d5905e4f"] input': '_update_user_fields',
13 | // this entry is to cause the city field to be updated when the page is loaded
14 | 'ready [data-key="field_57ab9d5905e4f"] input': '_update_user_fields',
15 | },
16 |
17 | // this is our function that will perform the
18 | // ajax request when the state value is changed
19 | _update_user_fields: function(e){
20 |
21 | // get the user selection
22 | var $value = e.$el.val();
23 |
24 | // a lot of the following code is copied directly
25 | // from ACF and modified for our purpose
26 |
27 | // I assume this tests to see if there is already a request
28 | // for this and cancels it if there is
29 | if( this.update_user_request) {
30 | this.update_user_request.abort();
31 | }
32 |
33 | // I don't know exactly what it does
34 | // acf does it so I copied it
35 | var self = this,
36 | data = this.o;
37 |
38 | // set the ajax action that's set up in php
39 | data.action = 'load_user_details';
40 | // set the user id value to be submitted
41 | data.user_id = $value;
42 |
43 | // this is another bit I'm not sure about
44 | // copied from ACF
45 | data.exists = [];
46 |
47 | // this the request is copied from ACF
48 | this.update_user_request = $.ajax({
49 | url: acf.get('ajaxurl'),
50 | data: acf.prepare_for_ajax(data),
51 | type: 'post',
52 | dataType: 'json',
53 | async: true,
54 | success: function(json){
55 | // function to populate fields
56 | // loop through the values returned
57 | // and insert into fields
58 | for (i=0; iID) ||
33 | get_post_type($post->ID) != 'post') {
34 | return;
35 | }
36 |
37 | $handle = 'myUserFieldextension';
38 |
39 | // I'm using this method to set the src because
40 | // I don't know where this file will be located
41 | // you should alter this to use the correct fundtions
42 | // to set the src value to point to the javascript file
43 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/dynamic-text-based-on-user-select.js';
44 | // make this script dependent on acf-input
45 | $depends = array('acf-input');
46 |
47 | wp_enqueue_script($handle, $src, $depends);
48 | } // end public function enqueue_script
49 |
50 | public function ajax_load_user_details() {
51 | // this funtion is called by AJAX to get the user information
52 | // based on user selection
53 |
54 | // we can use the acf nonce to verify
55 | if (!wp_verify_nonce($_POST['nonce'], 'acf_nonce')) {
56 | die();
57 | }
58 | $user = 0;
59 | if (isset($_POST['user_id'])) {
60 | $user = intval($_POST['user_id']);
61 | }
62 | $values = array();
63 | $user = get_user_by('id', $user);
64 | if ($user) {
65 | $values= array(
66 | // these are the field keys that match were we want to show the values
67 | array(
68 | 'key' => 'field_57ab9d8305e50',
69 | 'value' => $user->data->user_login
70 | ),
71 | array(
72 | 'key' => 'field_57ab9de105e54',
73 | 'value' => $user->data->display_name
74 | ),
75 | array(
76 | 'key' => 'field_57ab9df305e56',
77 | 'value' => $user->data->user_email
78 | )
79 | );
80 | }
81 | echo json_encode($values);
82 | exit;
83 | } // end public function ajax_load_user_details
84 |
85 | } // end class myUserFieldextension
86 |
87 | ?>
88 |
--------------------------------------------------------------------------------
/dynamic-text-based-on-user-select/group_57ab9d41279f5.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57ab9d41279f5",
3 | "title": "Dynamic User Select Example",
4 | "fields": [
5 | {
6 | "key": "field_57ab9d5905e4f",
7 | "label": "user",
8 | "name": "user",
9 | "type": "user",
10 | "instructions": "Select a User to see their information dynamically populated in the fields below.",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "role": "",
19 | "allow_null": 0,
20 | "multiple": 0
21 | },
22 | {
23 | "key": "field_57ab9d8305e50",
24 | "label": "Username",
25 | "name": "username",
26 | "type": "text",
27 | "instructions": "",
28 | "required": 0,
29 | "conditional_logic": 0,
30 | "wrapper": {
31 | "width": "",
32 | "class": "",
33 | "id": ""
34 | },
35 | "default_value": "",
36 | "placeholder": "",
37 | "prepend": "",
38 | "append": "",
39 | "maxlength": "",
40 | "readonly": 0,
41 | "disabled": 0
42 | },
43 | {
44 | "key": "field_57ab9de105e54",
45 | "label": "Display Name",
46 | "name": "display_name",
47 | "type": "text",
48 | "instructions": "",
49 | "required": 0,
50 | "conditional_logic": 0,
51 | "wrapper": {
52 | "width": "",
53 | "class": "",
54 | "id": ""
55 | },
56 | "default_value": "",
57 | "placeholder": "",
58 | "prepend": "",
59 | "append": "",
60 | "maxlength": "",
61 | "readonly": 0,
62 | "disabled": 0
63 | },
64 | {
65 | "key": "field_57ab9df305e56",
66 | "label": "Email",
67 | "name": "email",
68 | "type": "email",
69 | "instructions": "",
70 | "required": 0,
71 | "conditional_logic": 0,
72 | "wrapper": {
73 | "width": "",
74 | "class": "",
75 | "id": ""
76 | },
77 | "default_value": "",
78 | "placeholder": "",
79 | "prepend": "",
80 | "append": ""
81 | }
82 | ],
83 | "location": [
84 | [
85 | {
86 | "param": "post_type",
87 | "operator": "==",
88 | "value": "post"
89 | }
90 | ]
91 | ],
92 | "menu_order": 0,
93 | "position": "normal",
94 | "style": "default",
95 | "label_placement": "top",
96 | "instruction_placement": "label",
97 | "hide_on_screen": "",
98 | "active": 1,
99 | "description": "",
100 | "modified": 1470867204
101 | }
--------------------------------------------------------------------------------
/repeater-ajax-load-more/README.md:
--------------------------------------------------------------------------------
1 | # ACf Repeater Load More Example
2 |
3 | This example shows the basics of adding load more functionality for a repeater field.
4 |
5 | Read over all the files for explanation
6 |
--------------------------------------------------------------------------------
/repeater-ajax-load-more/group_57cae2b099966.json:
--------------------------------------------------------------------------------
1 | {
2 | "key": "group_57cae2b099966",
3 | "title": "Repeater Load More Example",
4 | "fields": [
5 | {
6 | "key": "field_57cae2c4c1227",
7 | "label": "load_more_example_repeater",
8 | "name": "load_more_example_repeater",
9 | "type": "repeater",
10 | "instructions": "",
11 | "required": 0,
12 | "conditional_logic": 0,
13 | "wrapper": {
14 | "width": "",
15 | "class": "",
16 | "id": ""
17 | },
18 | "collapsed": "",
19 | "min": "",
20 | "max": "",
21 | "layout": "table",
22 | "button_label": "Add Row",
23 | "sub_fields": [
24 | {
25 | "key": "field_57cae2e1c1228",
26 | "label": "sub field",
27 | "name": "sub_field",
28 | "type": "text",
29 | "instructions": "",
30 | "required": 0,
31 | "conditional_logic": 0,
32 | "wrapper": {
33 | "width": "",
34 | "class": "",
35 | "id": ""
36 | },
37 | "default_value": "",
38 | "placeholder": "",
39 | "prepend": "",
40 | "append": "",
41 | "maxlength": ""
42 | }
43 | ]
44 | }
45 | ],
46 | "location": [
47 | [
48 | {
49 | "param": "post_type",
50 | "operator": "==",
51 | "value": "post"
52 | }
53 | ]
54 | ],
55 | "menu_order": 0,
56 | "position": "normal",
57 | "style": "default",
58 | "label_placement": "top",
59 | "instruction_placement": "label",
60 | "hide_on_screen": "",
61 | "active": 1,
62 | "description": "",
63 | "modified": 1473011412
64 | }
--------------------------------------------------------------------------------
/repeater-ajax-load-more/repeater-ajax-load-more-template-code.php:
--------------------------------------------------------------------------------
1 |
15 |
32 |
39 | style="display: none;">Show More
44 |
47 |
83 |
--------------------------------------------------------------------------------
/repeater-ajax-load-more/repeater-ajax-load-more.php:
--------------------------------------------------------------------------------
1 | $count) {
66 | $more = true;
67 | }
68 | // output our 3 values as a json encoded array
69 | echo json_encode(array('content' => $content, 'more' => $more, 'offset' => $end));
70 | exit;
71 | } // end function my_repeater_show_more
72 |
73 | // this will load the example field group included
74 | // you only need this when setting up this example
75 | // it should be removed if you're using your own field group
76 | add_action('acf/include_fields', 'load_repeater_more_example_group');
77 | function load_repeater_more_example_group() {
78 | $file = dirname(__FILE__).'/group_57cae2b099966.json';
79 | $json = file_get_contents($file);
80 | $group = json_decode($json, true);
81 | acf_add_local_field_group($group);
82 | } // end function load_repeater_more_example_group
83 |
84 | ?>
--------------------------------------------------------------------------------
/unique-repeater-checkbox/README.md:
--------------------------------------------------------------------------------
1 | # ACF Unique Repeater True/False Field
2 |
3 | ***This examples only works in ACF5***
4 |
5 | This example shows how to create a true/false field in a repeater that will only allow the field to be
6 | checked in one row of the repeater. It also prevents the currently checked item from being unchecked.
7 | This basically turns the true/false field in the repeater into a multirow radio field.
8 |
9 | To use this example create a repeater that includes a true/false field and then use the field key
10 | to modify the JavaScript file in this example.
11 |
12 | Please note that this does not force one of the true/false fields in one of rows to be checked until
13 | one of them has actually been checked. When I use this if no rows are check then I add code to my PHP
14 | to assume that the field in the first row is checked.
15 |
16 | ***Note: I do not know if this will work with the UI Toggle added to the True/False field in version 5.5.0 of ACF. I have not tested it. My guess is that it will not.**
17 |
--------------------------------------------------------------------------------
/unique-repeater-checkbox/unique-repeater-checkbox-acf57.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | Updated JS file for ACF >= 5.7.0
4 | See comments in the original file for things not included here
5 | The comments here mainly cover the differences between
6 | ACF < 5.7.0 and ACF >= 5.7.0
7 | */
8 |
9 |
10 | /*
11 | The first main difference is that we cannot extend
12 | the acf.ajax object because it no longer exists
13 | we have to add our own function on jQuery(document).ready
14 | */
15 | jQuery(document).ready(function($) {
16 | /*
17 | The next difference is how we target and detect changes to the field
18 | Since a repeater can be dynamically added we must target the document
19 | and include the selector for the checkbox field
20 | */
21 | $(document).on('change', '[data-key="field_5b6c628cb8088"] .acf-input input', function(e) {
22 | /*
23 | The final difference is how we get the checkbox that was actually clicked
24 | */
25 | var target = $(e.target);
26 | /*
27 | From this point on the logic is identical to the original JS
28 | */
29 | var checked = target.prop('checked');
30 | if (!checked) {
31 | return;
32 | }
33 | var id = target.prop('id');
34 | var key = target.closest('.acf-field').attr('data-key');
35 | var list = $('[data-key="'+key+'"] input').not('[data-key="'+key+'"] input[type="hidden"]').not('.acf-clone [data-key="'+key+'"] input');
36 | if (list.length == 1) {
37 | return;
38 | }
39 | for (var i=0; iID) ||
24 | get_post_type($post->ID) != 'post') {
25 | return;
26 | }
27 |
28 | $handle = 'acf-unique-repeater-checkbox';
29 |
30 | // I'm using this method to set the src because
31 | // I don't know where this file will be located
32 | // you should alter this to use the correct fundtions
33 | // to set the src value to point to the javascript file
34 | $version = acf_get_setting('version');
35 | if (version_compare($acf_version, '5.7.0', '<')) {
36 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/unique-repeater-checkbox.js';
37 | } else {
38 | $src = '/'.str_replace(ABSPATH, '', dirname(__FILE__)).'/unique-repeater-checkbox-acf57.js';
39 | }
40 | // make this script dependent on acf-input
41 | $depends = array('acf-input');
42 |
43 | wp_enqueue_script($handle, $src, $depends);
44 |
45 | } // end function unique_repeater_checkbox_enqueue_script
46 |
47 | ?>
48 |
--------------------------------------------------------------------------------