Halo, I using Yii2 and I get error while call a function in dropdown list.
This is my dropdown :
<?= $form->field($model, "jenis_manifest")->dropDownList([ 'Berangkat' => 'Berangkat', 'Pulang' => 'Pulang'], ['onchange' => 'fungsiUtama();', 'class' => 'form-control', 'prompt' => '-- Manifest --']) ?>
And this is my script :
<?php
$script = <<<JS
function fungsiUtama()
{
alert("Hello");
}
JS;
$this->registerJs($script);
?>
I also try with JsExpression base on this question, but still not working.
This is the error what I get :
Use
$this->registerJs($script, \yii\web\View::POS_END);
The last part means - add this script straightforward at the end of page.
Without the second function argument the default one is called -
\yii\web\View::POS_READY - which means - add this script wrapped in jQuery(document).ready() method that forces scripts to wait untill the page's DOM is fully loaded. Before that your script is unavailable and that is probably why you've got this error.
Related
I have an old wordpress plugin built to show latest posts of choice. The plugin uses a shortcode with options. Now I am converting the plugin little bit so that it can be used as a gutenberg block. I kept the php code intact and trying to create a block which will have some settings (right side setting box for block). I don't want to show the posts in block editor like other blocks instead I will just show the data in frontend. So in admin, nothing will be visible rather than a placeholder text and the settings. In plugin's init.php I have this code (Please ignore coding mistakes here, I just put some part to get the idea):
final class Dcposts {
function dcposts_block_assets() {
register_block_type(
'dc/block-dcposts', array(
'style' => 'dcposts-style-css',
'editor_script' => 'dcposts-block-js',
'editor_style' => 'dcposts-block-editor-css',
'render_callback' => array($this, 'mytest')
)
);
}
public function mytest($attributes) {
return '[some_shortcode]'; // I will generate a dynamic shortcode with $attributes
}
}
This works fine. If I add the block, it shows the posts front-end. But I get an error message in admin: "Updating failed. The response is not a valid JSON response."while saving the page with the block; and also the shortcode executes in admin. How can I prevent this? Is this the right approach I am on? Please give me some idea.
The error message "Updating failed. The response is not a valid JSON response." occurs when your render_callback function returns different content than the Gutenberg Edit() function. For dynamic blocks, the save() function should return null.
The ServerSideRender block is ideal for migrating/integrating existing PHP functions into a Gutenberg block.
You are on the right path with registering your block dc/block-dcposts in PHP. Next, add a ServerSideRender component in the Edit function of your block declaration in JavaScript, eg:
index.js
import { registerBlockType } from '#wordpress/blocks';
import { __ } from '#wordpress/i18n';
import ServerSideRender from '#wordpress/server-side-render';
/**
* Registering our block in JavaScript
*/
registerBlockType('dc/block-dcposts', {
title: __('DCPosts', 'dc'),
description: __(
'What DCPosts does..',
'dc'
),
category: 'layout',
icon: 'star-filled',
edit: () => {
return (
<ServerSideRender
block="dc/block-dcposts"
/>
);
},
save: () => { return null } // Dynamic blocks should return null to avoid validation error.
});
plugin.php (simplified example)
function dc_block_dcposts($attributes){
return '<div>Dynamically rendered content</div>';
}
function dc_block_dcposts_init(){
register_block_type( 'dc/block-dcposts', array(
'render_callback' => 'dc_block_dcposts',
) );
};
add_action( 'init', 'dc_block_dcposts_init' );
Once you have the basic setup working, the next step could be migrating any attributes your PHP function requires. The attributes need to be defined in the block setup and then passed in via the ServerSideRender component to be recieved by your PHP function.
I have the following minify function:
public static function minify($buffer)
{
$search = array(
'/\>[^\S ]+/s',
'/[^\S ]+\</s',
'/(\s)+/s'
);
$replace = array(
'>',
'<',
'\\1'
);
$buffer = preg_replace($search, $replace, $buffer);
return $buffer;
}
This function is used with ob_start() in order to minify my html output. The function is taken from here: https://stackoverflow.com/a/6225706/4601581
This function seems to destroy some of my JS. Example: A button i click to execute some JS:
<button type="button" class="btn btn-<?= Config::$ELEMENT_COLOR ?> btn-block" onclick="addLastInput(<?= $mask->getId() ?>)">Do it</button>
The $id i give to addLastInput is simply a number, taken from $mask->getId().
Also i have this little JS code to populate some data array:
<script>lastSubmissionData[<?=$mask->getId()?>] = '<?=json_encode($lastSubmissionData)?>';</script>
And finally my JS function i actually want to execute with my given param (the id) and on the data array:
function addLastInput(maskId) {
var data = lastSubmissionData[maskId];
for (var i = 0; i < data.length; i++) {
// TODO
}
}
Whenever i only load the page, it simply tells me Uncaught SyntaxError: Unexpected end of input in the Chrome console. Whenever i do so with not using the minify function and outcommenting the ob_start() completely, it just works and i can use it.
Conclusion: The minify function seems to brake my inline JS as the comments on the linked SO answer suggest. Question: How can i fix that? I even googled for other minify solutions like this one: https://gist.github.com/tovic/d7b310dea3b33e4732c0
All of them seem to not work by just breaking my site. What's the best solution here to simply let me use my JS code and still minify my html output?
Don't use this minify() function. It's broken.
This function ends up removing some semantically meaningful newlines in inline Javascript code, such as ones appearing at the end of a single-line comment. (For example, it converts your example Javascript function to a single line ending in { // TODO } }.)
This function will break some HTML constructs as well. In particular, it will destroy the content of <pre> tags, as it does not recognize that whitespace is significant in that context.
(Both of these caveats are mentioned in highly upvoted comments on the post you got this code from, by the way!)
I have read a half-dozen examples of how to pass parameters to javascript and they all describe the process in a very similar manner but don't speak to one event: how does the javascript code actually get called? I have seen several examples where the code is tested in $document.ready, but can't get them to work.
Here is my php code:
$base_id = 'id_'.wp_create_nonce("postlister");
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$parms = array(
'url' => $url,
'action' => $action,
'base_id' => base_id );
wp_localize_script(self::slug.'-postlister', 'postlister_parms', $parms);
And my jQuery code:
jQuery(document).ready(function (postlister_parms) {
if (postlister_parms !== undefined) {
var parms = postlister_parms;
$.diagnostic('postlister loaded');
}
});
When the page loads, there is a div generated were my code writes some additional html:
echo '<div id="'.$base_id.'"></div>';
produces:
<div id="id_c8aca14643"></div>
The footer contains:
<script type="text/javascript" src="http://localhost/mkpusa.org/nw/wp-content/plugins/rhj4-postlister/js/postlister.js?ver=3.9.2"></script>
When the jQuery code executes, postlister_parms is a function:
postlister_parms: function (a,b){return new n.fn.init(a,b)}
But postlister_parms.url, .action and .base_id are all undefined.
Please help me spot what I am missing.
The resulting code should look something like this:
var postlister_parms = {"url":"the_url","action":"the_action","base_id":"the_baseid"};
Make sure that the script with the handle self::slug.'-postlister' is enqueued before you call wp_localize_script
Have you read the Notes section of the documentation The call to wp_enqueue_script and wp_localize_script should be in a wp_enqueue_scripts action and the script should be enqueued before it is localized. That being true, it should work
Please forgive me if this has ever been asked before, it's very hard to search for since the operative word is 'this'.
I have a click event bound to a set of divs with the same class, each one needs the same behavior. In jQuery, I would simply use $(this) to reference the clicked element. I can't seem to figure out how to get CakePHP to use $(this) as the update element.
My code: (the 'update' param is the one in question)
$this->Js->get('.bookmark')->event('click',
$this->Js->request(
array('controller' => 'bookmarks', 'action' => 'add', $event['Event']['id']),
array('async' => true, 'update' => 'this')
)
);
Thank you
My personal opinion - don't use the CakePHP JsHelper. Don't get me wrong, I love CakePHP and am a CakePHP fanboy, but - in some cases it's just easier to use the "normal" way.
As an example, the HTML helper can create divs too, but I've never found the reason to use it over just typing <div>.
I've used this before for select lists (I modified it for your example, so I'm not 100% sure it works), I don't see why it wouldn't work for click
Put this at the bottom of the view
$this->Js->get('#addBookmarkDiv')->event('click', //put the div name that contains the input you're checking to see if it's clicked
$this->Js->request(array(
'controller'=>'bookmarks',
'action'=>'add'
), array(
'update'=>'#putdivhere', //div you want to update, possibly a div that displays bookmarks?
'async' => true,
'method' => 'post',
'dataExpression'=>true,
'data'=> $this->Js->serializeForm(array(
'isForm' => false,
'inline' => true
))
))
);
Now, in your controller do what you have to do, and add the following
function add(){
//do what ever it is you want to do with the sent data
$this->set('bookmarks',$bookmarks);
$this->layout = 'ajax';
}
Then, create a view for add with something like.
//add.ctp
<?php foreach ($bookmarks as $bookmark): ?>
<li><?php echo $bookmark; ?></li
<?php endforeach; ?>
Well, it looks like I was overlooking that fact that I could embed javascript in the JsHelper functions. That and the fact that $(this) is out of scope inside the $.ajax function. So, this is the solution that worked for me. As you can see, the second parameter to the event method is a mixture of javascript and PHP, and it's strung together in the first instance using concatenation.
$this->Js->get('.bookmark')->event('click',
'var el = $(this);'.
$this->Js->request(
array('controller' => 'bookmarks', 'action' => 'add', $event['Event']['id']),
array('async' => true, 'complete' => '$(el).addClass("bookmarked")')
)
);
In an attempt to keep my scripts maintainable, I'm going to move each into their own file, organised by controller and action:
// scripts which only apply to /views/posts/add.ctp
/app/webroot/js/page/posts/add.js
// scripts which only apply to /view/users/index.ctp
/app/webroot/js/page/users/index.js
That's all cool, however I'd like for these to be automatically added by the Controller, since it obviously knows the name of both the controller and action.
I figure the best place for this is in AppController::beforeRender(). (yes?)
The only problem is that I don't know how to actually add this into the $scripts_for_layout variable. I thought that getting a reference to the javascript helper object would work, but I can't find it from the controller!
class AppController extends Controller {
var $helpers = array("javascript", "html", "form");
function beforeRender() {
// ???
}
}
Very easy to do in your default.ctp layout file:
An example to automatically include .css files per controller and/or controller/action (because I had this lying around, easily adaptable to .js files):
<head>
...
<?php
if (is_file(WWW_ROOT . 'css' . DS . $this->params['controller'] . '.css')) {
echo $html->css($this->params['controller']);
}
if (is_file(WWW_ROOT . 'css' . DS . $this->params['controller'] . DS . $this->params['action'] . '.css')) {
echo $html->css($this->params['controller'] . '/' . $this->params['action']);
}
?>
...
</head>
Like deceze is saying, we do it using the layout, although I find our solution a bit more elegant :)
In default.ctp:
if(isset($cssIncludes)){
foreach($cssIncludes as $css){
echo $html->css($css);
}
}
if(isset($jsIncludes)){
foreach($jsIncludes as $js){
echo $javascript->link($js);
}
}
Then, in our controller actions, we define these arrays:
$this->set('cssIncludes',array('special')); // this will link to /css/special.css
$this->set('jsIncludes',array('jquery')); // this will link to /js/jquery.js
For files that need to be loaded in each view, we simply add the same type of link "statically" to the top of the layout, like:
echo $javascript->link('global');
echo $html->css('global');
This works really well for us. Good luck!
Kinda new to this, but after reading this added the following to my layout:
if ($handle = opendir(WWW_ROOT . 'js' . DS . $this->params['controller'] . DS . $this->params['action']))
{
while (false !== ($entry = readdir($handle)))
{
$entry = str_replace(".js", "", $entry);
echo $this->Html->script($entry);
}
closedir($handle);
}
I just had to do page specific inclusion, but I've found a neater way to do it in the documentation. You can just load it into some script block in your default.ctp. And in corresponding view just use HTML helper to push a script:
You can append the script tag to a specific block using the block
option:
echo $this->Html->script('wysiwyg', array('block' => 'scriptBottom'));
Which appends <script type="text/javascript" href="/js/wysiwyg.js"></script> to a block.
In your layout you can output all the script tags added to
‘scriptBottom’:
echo $this->fetch('scriptBottom');
The best way I can think of is to create your own custom AppView and have all your controllers use that:
class myController extends AppController {
var view = 'AppView';
...
}
Then, somewhere in your AppView, you'd want to do something like:
function __construct(&$controller, $register){
parent::__construct($controller,$register);
$this->addScript('<script type="text/javascript" src="/path/to/js/' . $this->controller . '/' . $this->action . '.js"></script>');
}
But I'd take a step back and think about a few things, first.
How big are your scripts, on average? Is the overhead of an external script call (before the script is cached by the client) better than adding a few hundred bytes to your main output stream (by just sticking the script into the page, inline)?
Perhaps you'd be better of somewhere in the middle -- split your scripts up by controller, but not action. That way, after the first visit to any action, the client has all scripts for all actions. This way, you avoid a big initial download for all the application's script, but you avoid adding N http round-trips (where N is the number of actions a brand new user visits).
Another way to approach the problem is to do it all in javascript. Just figure out a lazy-loading scheme. So your app just loads a very small loader.js, and that script figures out which other javascript sources to pull in.
Note: I've never tested my extend-the-view hack, but I bet it'll work if you really want to do this.
There's a nuts and bolts of CakePHP blog post on Convention over Configuration – what's the big deal? - which uses a helper for specifying Javascript files:
<?php
class JsManagerHelper extends AppHelper {
var $helpers = array('Javascript');
//where's our jQuery path relevant to CakePHP's JS dir?
var $_jqueryPath = 'jquery';
//where do we keep our page/view relevant scripts?
var $_pageScriptPath = 'page_specific';
function myJs() {
return $this->Javascript->link($this->_jqueryPath . '/' .
$this->_pageScriptPath .'/' .
$this->params['controller'] . '_' .
$this->params['action'], false);
}
}
?>
And then you just have $jsManager->myJs(); in your view.
I have authored a Plugin for this exact issue.
If you have two files:
/app/View/Post/add.ctp
/app/View/Post/add.js
it will include the add.js into the script block of the page.
OR
app/View/Post/add.js
app/webroot/js/post/add.js
it will include /js/post/add.js into the script block of the page.
A few cute features are in there and it's simple as beans. You can even use PHP inside your .js files and use viewVars which you set in the action. Most of all, you don't need to hack the view or layout files to make it work, simply fetch the 'script' block in the layout. You can also simply write the .js to another view block.
You can check it out here: https://github.com/dizyart/cakephp-viewautoload
It's in the early stages, so make sure to comment and wishlist!
LI worked a little (very little) bit over W1ckd snippet and made it easier for sharing the same js for different actions:
if ( is_dir(WWW_ROOT . 'js' . DS . $this->params['controller'] ) && ( $handle = opendir( WWW_ROOT . 'js' . DS . $this->params['controller'] ) ) )
{
while ( false !== ( $entry = readdir( $handle ) ) )
{
if ( in_array( $this->params['action'], explode( '.', $entry ) ) ) {
$entry = str_replace( ".js", "", $entry );
echo $this->Html->script( $this->params['controller'].DS.$entry );
}
}
closedir( $handle );
}
This way you can have something like:
webroot/js/controller/view.edit.add.js
And this js will be included in those three actions (view, edit, add).
With Cake v3 you can do it like this. Add the scripts you want in the specific controller.
//YourController.php
public function beforeRender (Event $event)
{
array_push($this->scripts, 'Admin/gallery');
parent::beforeRender($event);
}
Set the default in intialize and render once
//AppController.php
public function initialize()
{
parent::initialize();
$this->scripts = [];
}
public function beforeRender (Event $event)
{
/* Define scripts in beforeRender of Child */
$this->set('scripts', $this->scripts);
/*-----------------------------------------*/
}
Now you can run this function once.
<?= $this->Html->script($scripts) ?>