I have been working on some custom code for a sugar module and am fairly unclear where to put my javascript code to be called in module.
Currently I have put my custom JS in include/javascript/popup_parent_helper.js
This works fine in developer mode but does not work when that is turned off and unfortunately dveloper mode runs SUPER slow
I have done a lot of research and I am getting some conflicting results.
Some tell me that I should include it in:
/modules/[ModuleName]/
Others say that it should to in:
/custom/modules/[ModuleName/
and some further in adding js as a directory
Please help me clarify proper structure for this and where I need to make my proper include statement
Clarifications:
We are using SugarCrm 6.5x
In this case the JS is only being used for one module.
It is being used in the Quick Create View and the Edit View
If the javascript should be accessible by any module, you can create a new JSGrouping and pull in your custom js file using the following technique:
http://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_7.6/Extension_Framework/JSGroupings/#Creating_New_JSGroupings
It sounds like you want it to be isolated to your custom module, so you should probably extend the view desired. If you are extending the record view, create a new file called record.js at custom/modules/-your_module-/clients/base/views/record/
({
extendsFrom: 'RecordView',
initialize: function(options) {
this._super('initialize', [options]);
this.doSomething();
},
doSomething: function(){
console.log("Help you I will");
},
...
})
https://developer.sugarcrm.com/2014/02/10/extending-sugar-7-record-view/
I had also faced similar issue (JS should be work for edit and quickcreate form) but after making some RnD I achieved it as per following way:
\custom\modules\<modulename>\views\view.edit.php
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/MVC/View/views/view.edit.php');
class {moduleName}ViewEdit extends ViewEdit {
public function __construct() {
parent::ViewEdit();
$this->useForSubpanel = true; // this variable specifies that these changes should work for subpanel
// / $this->useModuleQuickCreateTemplate = true; // quick create template too
}
function display(){ ?>
<?php
$jsscript = <<<EOQ
<script>
Your JS code
</script>
EOQ;
parent::display();
echo $jsscript; //echo the script
}
}
?>
Related
I am trying to update a Rails 3 app to Rails 6 and I have problems with the now default webpacker since my Javascript functions are not accessible.
I get: ReferenceError: Can't find variable: functionName for all js function triggers.
What I did is:
create an app_directory in /app/javascript
copied my development javascript file into the app_directory and renamed it to index.js
added console.log('Hello World from Webpacker'); to index.js
added import "app_directory"; to /app/javascript/packs/application.js
added to /config/initializers/content_security_policy.rb:
Rails.application.config.content_security_policy do |policy|
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
end
I get 'Hello World from Webpacker' logged to console, but when trying to access a simple JS function through <div id="x" onclick="functionX()"></div> in the browser I get the reference error.
I understand that the asset pipeline has been substituted by webpacker, which should be great for including modules, but how should I include simple JS functions? What am I missing?
Thanks in advance?
For instructions on moving from the old asset pipeline to the new webpacker way of doing things, you can see here:
https://www.calleerlandsson.com/replacing-sprockets-with-webpacker-for-javascript-in-rails-5-2/
This is a howto for moving from the asset pipeline to webpacker in Rails 5.2, and it gives you an idea of how things are different in Rails 6 now that webpacker is the default for javascript. In particular:
Now it’s time to move all of your application JavaScript code from
app/assets/javascripts/ to app/javascript/.
To include them in the JavaScript pack, make sure to require them in
app/javascript/pack/application.js:
require('your_js_file')
So, create a file in app/javascript/hello.js like this:
console.log("Hello from hello.js");
Then, in app/javascript/packs/application.js, add this line:
require("hello")
(note that the extension isn't needed)
Now, you can load up a page with the browser console open and see the "Hello!" message in the console. Just add whatever you need in the app/javascript directory, or better yet create subdirectories to keep your code organized.
More information:
This question is cursed. The formerly accepted answer is not just wrong but grotesquely wrong, and the most upvoted answer is still missing the mark by a country mile.
anode84 above is still trying to do things the old way, and webpacker will get in your way if you try that. You have to completely change the way you do javascript and think about javascript when you move to webpacker. There is no "scoping issue". When you put code in a web pack it's self-contained and you use import/export to share code between files. Nothing is global by default.
I get why this is frustrating. You're probably like me, and accustomed to declaring a function in a javascript file and then calling it in your HTML file. Or just throwing some javascript at the end of your HTML file. I have been doing web programming since 1994 (not a typo), so I've seen everything evolve multiple times. Javascript has evolved. You have to learn the new way of doing things.
If you want to add an action to a form or whatever, you can create a file in app/javascript that does what you want. To get data to it, you can use data attributes, hidden fields, etc. If the field doesn't exist, then the code doesn't run.
Here's an example that you might find useful. I use this for showing a popup if a form has a Google reCAPTCHA and the user hasn't checked the box at the time of form submission:
// For any form, on submit find out if there's a recaptcha
// field on the form, and if so, make sure the recaptcha
// was completed before submission.
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('form').forEach(function(form) {
form.addEventListener('submit', function(event) {
const response_field = document.getElementById('g-recaptcha-response');
// This ensures that the response field is part of the form
if (response_field && form.compareDocumentPosition(response_field) & 16) {
if (response_field.value == '') {
alert("Please verify that you are not a robot.");
event.preventDefault();
event.stopPropagation();
return false;
}
}
});
});
});
Note that this is self-contained. It does not rely on any other modules and nothing else relies on it. You simply require it in your pack(s) and it will watch all form submissions.
Here's one more example of loading a google map with a geojson overlay when the page is loaded:
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('.shuttle-route-version-map').forEach(function(map_div) {
let shuttle_route_version_id = map_div.dataset.shuttleRouteVersionId;
let geojson_field = document.querySelector(`input[type=hidden][name="geojson[${shuttle_route_version_id}]"]`);
var map = null;
let center = {lat: 36.1638726, lng: -86.7742864};
map = new google.maps.Map(map_div, {
zoom: 15.18,
center: center
});
map.data.addGeoJson(JSON.parse(geojson_field.value));
var bounds = new google.maps.LatLngBounds();
map.data.forEach(function(data_feature) {
let geom = data_feature.getGeometry();
geom.forEachLatLng(function(latlng) {
bounds.extend(latlng);
});
});
map.setCenter(bounds.getCenter());
map.fitBounds(bounds);
});
});
When the page loads, I look for divs with the class "shuttle-route-version-map". For each one that I find, the data attribute "shuttleRouteVersionId" (data-shuttle-route-version-id) contains the ID of the route. I have stored the geojson in a hidden field that can be easily queried given that ID, and I then initialize the map, add the geojson, and then set the map center and bounds based on that data. Again, it's self-contained except for the Google Maps functionality.
You can also learn how to use import/export to share code, and that's really powerful.
So, one more that shows how to use import/export. Here's a simple piece of code that sets up a "watcher" to watch your location:
var driver_position_watch_id = null;
export const watch_position = function(logging_callback) {
var last_timestamp = null;
function success(pos) {
if (pos.timestamp != last_timestamp) {
logging_callback(pos);
}
last_timestamp = pos.timestamp;
}
function error(err) {
console.log('Error: ' + err.code + ': ' + err.message);
if (err.code == 3) {
// timeout, let's try again in a second
setTimeout(start_watching, 1000);
}
}
let options = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 14500
};
function start_watching() {
if (driver_position_watch_id) stop_watching_position();
driver_position_watch_id = navigator.geolocation.watchPosition(success, error, options);
console.log("Start watching location updates: " + driver_position_watch_id);
}
start_watching();
}
export const stop_watching_position = function() {
if (driver_position_watch_id) {
console.log("Stopped watching location updates: " + driver_position_watch_id);
navigator.geolocation.clearWatch(driver_position_watch_id);
driver_position_watch_id = null;
}
}
That exports two functions: "watch_position" and "stop_watching_position". To use it, you import those functions in another file.
import { watch_position, stop_watching_position } from 'watch_location';
document.addEventListener("turbolinks:load", function() {
let lat_input = document.getElementById('driver_location_check_latitude');
let long_input = document.getElementById('driver_location_check_longitude');
if (lat_input && long_input) {
watch_position(function(pos) {
lat_input.value = pos.coords.latitude;
long_input.value = pos.coords.longitude;
});
}
});
When the page loads, we look for fields called "driver_location_check_latitude" and "driver_location_check_longitude". If they exist, we set up a watcher with a callback, and the callback fills in those fields with the latitude and longitude when they change. This is how to share code between modules.
So, again, this is a very different way of doing things. Your code is cleaner and more predictable when modularized and organized properly.
This is the future, so fighting it (and setting "window.function_name" is fighting it) will get you nowhere.
Looking at how webpacker "packs" js files and functions:
/***/ "./app/javascript/dashboard/project.js":
/*! no static exports found */
/***/ (function(module, exports) {
function myFunction() {...}
So webpacker stores these functions within another function, making them inaccessible. Not sure why that is, or how to work around it properly.
There IS a workaround, though. You can:
1) change the function signatures from:
function myFunction() { ... }
to:
window.myFunction = function() { ... }
2) keep the function signatures as is, but you would still need to add a reference to them as shown here:
window.myFunction = myFunction
This will make your functions globally accessible from the "window" object.
Replace the code in your custom java Script file
from
function function_name() {// body //}
to
window.function_name = function() {// body //}
From the official rails app guide:
Where to Stick Your JavaScript
Whether you use the Rails asset
pipeline or add a tag directly to a view, you have to make a
choice about where to put any local JavaScript file.
We have a choice of three locations for a local JavaScript file:
The app/assets/javascripts folder,the lib/assets/javascripts folder and the vendor/assets/javascripts folder
Here are guidelines for selecting
a location for your scripts:
Use app/assets/javascripts for JavaScript you create for your
application.
Use lib/assets/javascripts for scripts that are shared by many
applications (but use a gem if you can).
Use vendor/assets/javascripts for copies of jQuery plugins, etc., from
other developers. In the simplest case, when all your JavaScript files
are in the app/assets/javascripts folder, there’s nothing more you
need to do.
Add JavaScript files anywhere else and you will need to understand how
to modify a manifest file.
More reading:
http://railsapps.github.io/rails-javascript-include-external.html
I am a newbie in the ASP.NET technology and also I did not find any answer on the forum (however if there is one please let me know)
I am writing ASP.NET MVC app and I use a lot of Ajax there.
At the moment I am rewriting "free" Javascript into classes and their methods, so the code will be more readable.
And here is the problem:
For such a case:
#Ajax.ActionLink("Text", "SomeAction", "Controller", new AjaxOptions() { UpdateTargetId="content", HttpMethod="Post", OnComplete="javascriptActionStatedBelow"})
<script>
function javascriptActionStatedBelow()
{
// do stuff here
}
</script>
Everyting works perfectly fine. However when I try to input the function into the class and then call it, it does not work.
I am doing this in the following way:
<script>
// is in seperate file and here is added reference to it
class myClass {
javascriptActionStatedBelow() {
// do stuff here
}
}
// this is on the view page
var someObj = new myClass();
</script>
I initilize the object in the header and then in the body I put the following.
#Ajax.ActionLink("Text", "SomeAction", "Controller", new AjaxOptions() { UpdateTargetId="content", HttpMethod="Post", OnComplete= "someObj.javascriptActionStatedBelow" })
The second idea does not work.
What am I doing wrong ? Is there a better solution for managing javascript content in ASP.NET MVC applications while using AJAX ?
I know about $.ajax type of solution but actions like OnComplete, OnBegin,.. etc worked perfectly for now.
It's important to note that there are no classes in JavaScript. Functions can be used to somewhat simulate classes, but in general JavaScript is a class-less language. However from ECMAScript 2015, classes are introduced which are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. For your requirement, should defined the java script function as IIFE. If your javascript is not targeted for ECMAScript 2015, then you can write the code as follows for IIFE:
Define the IIFE method as
var myClass = (function () {
// Object that's returned from the IIFE.
return {
javascriptActionStatedBelow: function() {
}
};
}());
Now in view's script section you can use it as
// this is on the view page
var someObj = myClass.javascriptActionStatedBelow();
Javascript isn’t strictly a “class-based” object-oriented language, hence the same patterns that apply to conventional back-end object-oriented languages don’t apply there effectively. However, there is a number of ways to make javascript much more convenient and manageable.
One of the ways that I highly recommend is through the use of “Module / Revealing module pattern”, or simply put through making javascript “namespaces” and calling them wherever needed. This reduces the clutter in View files and mimics the design we are used to seeing.
In a separate js file (conventionally named similar to the view file), you will define your javascript as follows:
var className = (function() {
// private variables and functions
var javacriptField1 = "some field";
var javascriptFunction1 = function() {
//function logic
}
var javascriptFunction2 = function() {
//function logic
}
// public API
return {
javascriptField1 : javascriptField1,
javascriptFunction1: javascriptFunction1,
javascriptFunction2: javascriptFunction2
};
})();
In your view you would include your js file:
<script src="~/Scripts/myfile.js"></script>
And use it within the javascript on your view as follows:
var obj = className.javascriptFunction1();
Following is a very nice tutorial you should go through:
https://www.dcaric.com/blog/asp-net-mvc-and-revealing-module-pattern
Here’s another example:
How to use javascript namespaces correctly in a View / PartialView
I'm relatively new to javascript development and have tried to construct my own framework. I've been trying to convert my standard javascript functions to a framework. However I've been stuck at a relatively simple (I think), but nowhere explained issue.
In my HTML I call on the settings I want to use in my JavaScript (the user has to be able to edit them in html, not in js as it will be minified).
<script>
$(document).ready(function(){
var settings = {authenticFilter: 'on',
randomizeHeaders: {state: 'on', topHeader: 'h1', bottomHeader: 'h2'},
};
var papery = new Papery(settings);
});
</script>
But I can't seem to figure out how to use the settings I have given to my framework in the javascript file.
I know I can call upon this.settings = settings; in my javascript file, but in an if statement below, I want make use of specific settings in the given settings. However this option and several other ways I've tried to use the settings won't work. Can I even use the settings I give to the prototype from my html in an array?
var Papery = function (settings) {
Papery.authenticFilter = function() {
if (Papery.authenticFilter == 'on'){
$("img").addClass("authenticFilter");
$(".authenticFilter").css({"filter": "sepia(80%) grayscale(1) contrast(1) opacity(0.7)", "-webkit-filter": "sepia(80%) contrast(1) opacity(0.7)"});
}
}
}
You are using Papery.authenticFilter instead of settings.authenticFilter. Keep in mind that in usual libraries, a bunch of default settings are set in initialization in case they are not defined on the settings passed on the constructor.
I am new to zend framework. I have created "models" folder in application directory. Inside models folder I created a class Application_Models_Albums which extends Zend_Db_Table_Abstract.
Now when I use the following code in IndexController. I get error:
$albums = new Application_Models_Albums();
$this->view->albums = $albums->fetchAll();
Error: Fatal error: Class 'Application_Models_Albums' not found in
H:\Documents\IIS Server
Root\localhost\learning\zf1\application\controllers\IndexController.php
on line 13
Please help. How can I load models in zend framework?
You need to name your model Albums. and save in models/albums.php
The auto-loader will figure out where to load it from by the name. so even though the model is named albums, you call it by new Application_Models_Albums()
The autoloader find it in application/models/albums.php
similarly the Zend_Db_Table_Abstract class is located in Zend/Db/Table/Abstract.php
edit
IF you cant get the autoloader to work, you can do it the quick and easy way by just adding models/ to your include path. I've done this before and it works fine.
Something like set_include_path(get_include_path().PATH_SEPERATOR."models/") in your index.php
Your model class name should be Application_Model_Albums - with Model in singular.
See http://framework.zend.com/manual/en/zend.loader.autoloader-resource.html for more info on Zend Autoloader.
you need to require your model class-
require_once 'PathToModel\Application_Models_Albums.php';
some people create autoloader classes to deal with this, YMMV
It sounds like your Bootstrap or application.ini needs to specify the namespace as 'Application_'.
In Bootstrap.php:
protected function _initAutoloader()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'basePath' => APPLICATION_PATH,
'namespace' => 'Kwis_',
));
return $autoloader;
}
Alernatively, in configs/application.ini:
appnamespace = "Application_"
You could also extend Zend_Controller_Action and add a method like this:
protected $_tables = array();
protected function _getTable($table)
{
if (false === array_key_exists($table, $this->_tables)) {
require_once(APPLICATION_PATH.'/modules/'.$this->_request->getModuleName().'/models/'.$table.'.php');
$this->_tables[$table] = new $table();
}
return $this->_tables[$table];
}
Hi not sure if this is possible or not but I want to programaticaly update the <body> tags to change the onload function in my zend framework application.
The App is using layouts so the body tag currently looks like this <body class="trandra">
However in one of my views I have a map from google being loaded and it needs the following in the body tag this particular view <body onload="initialize()" onunload="GUnload()">
As you can understand I don't want this to be hardcoded in my layout as this will cause all matter of nightmares with the different views.
How can this be done programaticaly, if at all it is possible? Im using the headScript functions to add the javascript so is there an equivalant for the body tag?
Thanks in advance...
Approach one - Use a layout variable
One idea would be the following:
<body class="trandra" <?php echo $this->layout()->bodyScripts ?>>
And in your view:
<?php
$this->layout->bodyScripts =
'onload="initialize()" onunload="GUnload()"';
Approach two - Additional JS-file that adds event handlers
Another approach, which is less obtrusive and doesn't affect the HTML whatsoever is to add an additional JS-file in the view that requires the onload- and onunload-handlers. It could look something like this:
<?php
$this->headScript()->appendScript(
'/path/to/javascripts/loadGMaps.js');
In your loadGMaps.js (using prototype)
Event.observe(window, 'load', function onLoadHandler() {
// Code for initializing Google maps here
});
Event.observe(window, 'unload', function onUnloadHandler() {
// Code for unloading Google maps here
});
Instead of putting your Javascript directly in the code, you could also use an non-obstrusive approch : plugging in the javascript when the page is fully loaded.
Have a look, for instance, at a function called addOnLoadEvent (can be found on many websites ^^ )
If you are using a JS Framework, it certainly has that kind of feature :
for jQuery : http://docs.jquery.com/Events/ready#fn
for prototype : http://www.prototypejs.org/api/event/observe
If you register the "plugging-in" with headScript, there should be no need to modify the tag directly.
Developed something like this recently, I've blogged about it here: http://www.evilprofessor.co.uk/311-zend-framework-body-tag-view-helper/
Demo on site and code is available via github.
I'm no expert on the Zend framework, so I don't know if there is any build in functions for this, but you could do something like this:
In layout-file:
body_params?>>
And then in your controller, you set or add to the body_params:
$this->view->body_params='onload="initialize()" onunload="GUnload()"';
I know this is an old thread, but I was looking through some of the suggested solutions and came up with one of my own playing off of some of the ideas I had seen. What I did was I extended Zend_View in my own library files (I'm using a vanilla MVC layout but similar things can be done using a bootstrap.php rather than the Bootstrap class described below)
class Custom_View extends Zend_View
{
protected $bodyAttrs = array();
public function _setBodyAttr($attrName,$attrValue=null) {
$attrName = strtolower(strval($attrName));
if(!(in_array($attrName, HTML::getValidBodyAttrs()))) {
throw new Zend_Exception(__METHOD__." attrName '$attrName' is not a valid BODY attribute!");
}
$this->bodyAttrs[$attrName] = strval($attrValue);
}
public function _getBodyAttrsAsString() {
$bodyAttrs = "";
if(count($this->bodyAttrs) > 0) {
$attrs = array();
foreach($this->bodyAttrs as $_k => $_v) {
array_push($attrs,sprintf("%s=\"%s\"", $_k, $_v));
}
$bodyAttrs = " " . implode(" ", $tags);
}
return $bodyAttrs;
}
}
// some useful tag definitions for HTML
class HTML
{
// HTML attributes as described by W3C
public static $BODY_ATTRIBUTES = array('alink','background','bgcolor','link','text','vlink');
public static $GLOBAL_ATTRIBUTES = array('accesskey','class','contenteditable','contextmenu','dir','draggable','dropzone','hidden','id','lang','spellcheck','style','tabindex','title');
public static $WINDOW_EVENT_ATTRIBUTES = array('onafterprint','onbeforeprint','onbeforeunload','onerror','onhaschange','onload','onmessage','onoffline','ononline','onpagehide','onpageshow','onpopstate','onredo','onresize','onstorage','onundo','onunload');
public static $MOUSE_EVENT_ATTRIBUTES = array('onclick','ondblclick','ondrag','ondragend','ondragenter','ondragleave','ondragover','ondragstart','ondrop','onmousedown','onmousemove','onmouseout','onmouseover','onmouseup','onmousewheel','onscroll');
public static $KEYBOARD_EVENT_ATTRIBUTES = array('onkeydown','onkeypress','onkeyup');
public static $FORM_EVENT_ATTRIBUTES = array('onblur','onchange','oncontextmenu','onfocus','onformchange','onforminput','oninput','oninvalid','onreset','onselect','onsubmit');
public static $MEDIA_EVENT_ATTRIBUTES = array('onabort','oncanplay','oncanplaythrough','ondurationchange','onemptied','onended','onerror','onloadeddata','onloadedmetadata','onloadstart','onpause','onplay','onplaying','onprogress','onratechange','onreadystatechange','onseeked','onseeking','onstalled','onsuspend','ontimeupdate','onvolumechange','onwaiting');
public static function getValidBodyAttrs() {
return array_merge(self::$BODY_ATTRIBUTES,self::$GLOBAL_ATTRIBUTES,self::$WINDOW_EVENT_ATTRIBUTES,self::$MOUSE_EVENT_ATTRIBUTES,self::$KEYBOARD_EVENT_ATTRIBUTES);
}
}
after creating this file I added a method _initView to the Bootstrap.php file pointed to by the index.php and application.ini at the root of the application directory:
protected function _initView()
{
// Custom_View extends Zend_View
$view = new Custom_View();
// Add it to the ViewRenderer
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper( 'ViewRenderer' );
$viewRenderer->setView($view);
return $view;
}
The new, extended Zend_View now allows adding your body tags along with some simple checking for validity. Modify your layout's body tag to get the attributes:
<body<?= $this->_getBodyAttrs(); ?>>
Once you have this set up you can add your body tags to any given view
in the controller with
$this->view->_setBodyAttr('key','val');
or in the view with
$this->_setBodyAttr('key','val');