I'm new in SuiteCRM world. I'm trying to include a new .js file in a module but it seems doesn't work.
I seen that in Sugar version 6.5 for include a .js file it's enough to do something like that
$viewdefs[$module_name]['EditView']['templateMeta']['includes'] =
array (
array (
'file' => 'modules/demo_demo_form/demo.js',
),
);
I also read that in the newer version(7>) it's changed the way to include a .js file.
I tried different ways* but seems doesn't work.
$js_groupings[$module_name]['modules/demo_demo_form/demo.js'] =
'include/modules/demo_demo_form/demo.js';
Any suggestions?
I think my previous answer to another similar question will help you
First, include your custom JS in the editviewdefs.php (example for Accounts module)
'includes' =>
array (
0 =>
array (
'file' => 'modules/Accounts/Account.js',
'file' => 'custom/modules/Accounts/myCustomFile.js',
),
),
Create the custom JS file custom/modules/Accounts/myCustomFile.js .
function yourCustomFunction(formElement){
console.log(formElement);
}
Then update the field you want to monitor for changes (contractsigned_c in the example) using the following code in the editviewdefs.php:
array (
'name' => 'contractsigned_c',
'label' => 'LBL_CONTRACTSIGNED',
'displayParams' =>
array (
'updateCallback' => 'yourCustomFunction(this)',
),
),
Now do a Repair and Rebuild inside the Admin/Repair section and voilĂ it should work :)
You can add the JS function on the function display() if you want, its the samething, the function will be called right after native combo update. It will look like this combo_contractsigned_c.update(); yourCustomFunction(this)
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'm writing Prestashop 1.7.2.1 module.
In that module when I want to register a javascript file I connect to the hook actionFrontControllerSetMedia and use registerJavascript like so:
$this->context->controller->registerJavascript('module-tuxinmodcartype-carsearch-js','modules/'.$this->name.'/js/displaytop.js');
this loads the javascript properly but I can't use smarty template engine in those javascript files.
is there a way to do that ? :)
if not... should I just add all my javascript files inline ?
update
so I added this to my hook function:
Media::addJsDef(['tuxinmodcartype'=>array(
'car_companies'=>$this->tuxDb->getCompanyNamesArray()
)]);
and this my js file:
$(function() {
var options = {
data: tuxinmodcartype.car_companies,
list: {
match: {
enabled: true
}
}
};
$('#company-name-input').easyAutocomplete(options);
});
and I get the error ReferenceError: tuxinmodcartype is not defined
For accessing variables in javascript you can assign them in your controllers with:
Media::addJsDef(array(
'mymodule' => array(
'var1' => 'yes',
'var2' => 'no'
)
));
Then you can use them in your javascript or through console:
let var1 = mymodule.var1;
let var2 = mymodule.var2;
Building javascript files with smarty... I guess it's better to split javascript into more files and load them through controller based on conditions. Or use the above definition for variables to control execution path in your javascript.
Safer way to declare your vars without mymodule array,
Media::addJsDef(array('var1' => $myphpvar));
Media::addJsDef(array('var2' => $myphpvartwo));
this way you DONT need
let var1 = mymodule.var1;
Why this better? Because 'let var1' cause error in iphone safari browser.
You can simply do this in the tpl file:
<script>var = '{$var}';</script>
and use your var in the javascript file.
Source: https://www.prestashop.com/forums/topic/589645-unknown-tag-addjsdef-error/?tab=comments#comment-2490210
I am currently developing a web application to edit some custom file formats (a small editor offering an interface to edit configuration files).
A key element of this app is an object called "FileReader" that is a wrapper who triggers the right interface according to the detected type of config file. This way I just spawn a new FileReader( src ) - src being an url or a blob and it keeps clean.
Once the type of the config file is retrieved, the wrapper checks if it has an interface associated to it and simply use it. Each interface (for each different config type) is defined in a separate JS file. Once the wrapper is fully loaded, I hook all the events and the execution of app begins.
Here is the HTML skeleton I used with the relevant elements :
<!doctype html>
<html><head></head>
<body>
<div id="UI"></div>
<script src="js/fileReader/fileReader.js" >App.FileReader = function(){ ... }</script>
<script src="js/fileReader/configType1.js" >App.FileReader.registerFileType("config1",object)</script>
<script src="js/fileReader/configType2.js" >App.FileReader.registerFileType("config2",object)</script>
<script src="js/fileReader/configType3.js" >App.FileReader.registerFileType("config3",object)</script>
<script src="js/main.js" >App.run();</script>
</body>
</html>
Now, the app grew up and I decided to convert my app to use requirejs.
My problem are mostly about "what's the best organisation" to deal with those modules because I can only consider that the FileReader is ready to use once all the file-type modules are loaded but I can't load them first because they need the mainWrapper to register their type.
The best structure I could come up with is :
main.js :
require(['require','FileReader','./fileReader/config1','./fileReader/config2','./fileReader/config3'],
function( require ) {
require(['require','./core/app'], function( require , App ) {
App.run( window ) ;
});
});
});
fileReader.js :
define(function () {
...
return FileReader ;
});
config1.js :
define(['FileReader'], function ( FileReader ) {
var Interface = ... ;
...
App.FileReader.registerFileType("config1",Interface)
return FileReader ;
});
app.js :
define(['FileReader'], function ( FileReader ) {
var App = function(){} ;
App.run = function(){ FileReader.openFile( ... ) ; ... } ;
return App ;
});
What's the point of my problem ?
To get sure that the FileReader object contains the right Interface objects, I first force requirejs manually to load all the FileReader related files and THEN load the main app file. This way, once the App object requests for the FileReader object, it already contains the right Interfaces registred.
What I'd like to achieve is just to only request for
"./core/app" in the main file
"./fileReader" in the core/app file
and then during it's first load, it would be fine if it could load the config-type modules IN the fileReader file, register all the stuff and only then returns the answer.
What I tried was : (in FileReader)
fileReader.js :
define(["require"],function ( require ) {
...
require(["config1","config2","config3"],
function(){
//occurs asynchronously, after the App.run() is triggered because of the synchronous return
callback(FileReader) ; //unfortunately I did not find any callback of this sort
//if it existed I could asynchronously find the list of the modules in the directory, load the them, and only then fire this fictive "ready" event
}
return FileReader ; //the first return occurs synchronously, so before the the fileReader is ready : modules are missing
});
So what is the best way to load this sort of modules (with some sort of circular dependency between the the config-type files and the the main FileReader) ? What'if I'd like to force it to be asynchronous so I can read what's available in the config-type directory ? With the simple HTML scripts list they were loaded in the right order and I had an easy access to the modules lists, now I'd like to be sure that everyting is ready before the app starts.
It seems to me you could parcel off the functionality that records what file types are available into a new module, maybe named FileTypeRegistry. So your config files could be like this:
define(['FileTypeRegistry'], function ( FileTypeRegistry ) {
var Interface = ... ;
...
FileTypeRegistry.registerFileType("config1", Interface);
});
These modules don't need to return a value. registerFileType just registers a type with the registry.
FileReader.js could contain:
define(["FileTypeRegistry", "config1", "config2", "config3"], function ( FileTypeRegistry ) {
var FileReader = {};
var FileReader.openFile = function (...) {
var impl = FileTypeRegister.getFileTypeImplementation(...);
};
return FileReader;
});
getFileTypeImplementation retrieves an implementation (an Interface previously registered) on the basis of a type name.
Louis' answer was interesting but it just moved the problem creating a new module, and even if it placed the dependencies in the right context, I tried to search for some real asynchronous module definition (not just loading).
The best I could finally come up with was to edit the require.js source file to add the behaviour I expected. I registered a new handler (a special dependency behaviour like "require" or "exports" ) called "delay" that provided a callback function for the module definition :
basically, this works this way :
define(["delay"], function ( delay ) {
var Module = ... ;
setTimeout(function(){ //Some asynchronous actions
delay(Module) ; //This does actually returns the Module's value and triggers the "defined" event.
//It's actually transparent so the other modules just "feel" that the network's load time was a bit longer.
},1000);
return Module ; //This does not do anything because delay is active
});
Thanks to this asynchronous definition, I can now scan the directory (an asynchronous request) and then transparently load all the modules.
The modifications represents only about 10 lines so if someone is interested, I post it there :
https://github.com/jrburke/requirejs/pull/1078/files
Apparently it's not the first request for asynchronous exports but it's still in debate so I just use this for personal projects.
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")')
)
);
I am trying to disable the JavaScript when the user is using IE. Is there a way to accomplish this in template.php or a custom module?
As alternative to handling the content of $vars['scripts'], which is a string containing the HTML to output in the <head> tag, you could use the value returned from drupal_add_js(NULL, NULL, 'header'), which is similar to the following one:
$header_javascript = array(
'core' => array(
'misc/jquery.js' => array(
'cache' => TRUE,
'defer' => FALSE,
'preprocess' => TRUE,
),
'misc/drupal.js' => array(
'cache' => TRUE,
'defer' => FALSE,
'preprocess' => TRUE,
),
),
'module' => array(),
'theme' => array(),
'setting' => array(
array('basePath' => base_path()),
),
'inline' => array(),
);
The "module" index contains the reference to the JavaScript files added from the modules, "settings" contains all the JavaScript settings generally added by the modules, and "inline" contains inline JavaScript code.
It could help if you need to distinguish between the different JavaScript files, and (for example) not touch any JavaScript file that has been marked as "core."
The counterbalance is that to populate the content of $vars['scripts'] you need to duplicate part of the code used from drupal_get_js(). As you would need a customized code, you would not duplicate all the code of that function, but you still would duplicate part of it.
In Drupal 7, the variable $vars['scripts'] is not passed anymore to template_preprocess_page() (and similar preprocess functions implemented by modules or themes); it is passed to template_preprocess_html().
You can use the preprocess_page() hook in template.php.
function YOUR_THEME_preprocess_page(&$vars) {
if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false)) {
$vars['scripts'] = 'get a mac!';
}
}
Obviously you should do something more intelligent with the $vars['scripts'] content :)