I've made some new modules in my Odoo and now when each form is loading I need to manipulate the created XML elements according to its required model and my arbitrary changes or refer to some specific function for its data validation (I know there are other ways for data validation but I'm curious if it's possible to be done with jquery functions).
I've tried to add an HTML file in the view folder and write a simple script, to begin with, but I'm not sure if it's the right place or even the right piece of code.
<script>
$(document).ready(function()
{
$("input").keyup(function(event){
console.log('t');
});
});
</script>
I would be glad if anyone could offer some useful answers to my question.
You can customize an existing form view using the js_class attribute, its value will be the name of an extended form view.
To bind a new event to an input, you can customize the form controller and extend the events mapping.
Example:
1) Extend the form view:
odoo.define("MODULE_NAME.custom_form", function (require) {
"use strict";
var FormController = require('web.FormController');
var FormView = require('web.FormView');
var viewRegistry = require('web.view_registry');
var CustomController = FormController.extend({
events: _.extend({}, FormController.prototype.events, {
'keyup input': '_onInputKeyup',
}),
_onInputKeyup(ev) {
console.log('t');
},
});
var CustomFormView = FormView.extend({
config: _.extend({}, FormView.prototype.config, {
Controller: CustomController,
}),
});
viewRegistry.add('custom_form', CustomFormView);
return {
CustomController: CustomController,
CustomFormView: CustomFormView,
};
});
2) Add it to the assets entry in the manifest file
'assets': {
'web.assets_backend': [
'MODULE_NAME/static/src/js/custom_form_view.js'
],
},
3) Set the js_class attribute on the form view tag
<form js_class="custom_form">
For back-office javascript, consider using the "Odoo built-in js library":
In your custom module "my_custom_module":
Create a new file /my_custom_module/static/src/js/my_customization.js:
odoo.define('my_custom_module.switch_to_gantt', function(require) {
"use strict";
var core = require('web.core');
var _t = core._t;
var AbstractController = require('web.AbstractController');
AbstractController.include({
/**
* Original : Intercepts the 'switch_view' event to add the controllerID into the data, and lets the event bubble up.
* Included : On switching from Kanban to Gantt view : Remove all the GroupBy Filters because it caused Error
*/
_onSwitchView: function (ev) {
console.log(ev.data.view_type);
//debugger;
/* only on the specific view : gantt: */
if(ev.data.view_type == 'gantt')
{
/* only on the specific model : event.event: */
if(ev.target.modelName == 'event.event')
$('.o_searchview .o_facet_remove').click();
}
this._super.apply(this, arguments);
},
});
});
And declare this asset in the __manifest__.py file:
'assets': {'web.assets_qweb': ['/my_custom_module/static/src/js/my_customization.js']}
if you want to use jquery or any js library you need to put them in this file under this path /your_app/static/src/js/test.js
and the file should be like this :
$(document).ready(function()
{
$("input").keyup(function(event){
console.log('t');
});
});
and you need to add the assets for this work like that :
'assets': {'web.assets_qweb': ['/your_app/static/src/js/test.js']} #path of the file
this for jquery not for building js model in odoo
Related
I'm currently facing an issue on Oro Platform v.4.1.10.
I have a specific form page where I'm performing an ajax reload on a specific field change.
The thing is that everything is working well except that the CSS and JS are not applied to my ajax section when reloaded.
When I first load the page, everything is OK :
When the section is reload using Ajax :
An OroDateTimeType field is used in the reloaded section, and according to my issue, the datepicker doesn't init on it.
Some details about the way my Ajax call is performed :
define(function (require) {
'use strict';
let SinisterAjaxRepairman,
BaseView = require('oroui/js/app/views/base/view');
SinisterAjaxRepairman = BaseView.extend({
autoRender: true,
/**
* Initializes SinisterAjaxRepairman component
*
* #param {Object} options
*/
initialize: function (options) {
// assign options to component object
this.$elem = options._sourceElement;
delete options._sourceElement;
SinisterAjaxRepairman.__super__.initialize.call(this, options);
this.options = options;
},
/**
* Renders the view - add event listeners here
*/
render: function () {
$(document).ready(function() {
let sectionTrigger = $('input.repair-section-trigger');
let sectionTargetWrapper = $('.repair-section-content');
sectionTrigger.on('click', function(e) {
$.ajax({
url: sectionTrigger.data('update-url'),
data: {
plannedRepair: sectionTrigger.is(':checked') ? 1 : 0,
id: sectionTrigger.data('sinister-id') ? sectionTrigger.data('sinister-id') : 0
},
success: function (html) {
if (!html) {
sectionTargetWrapper.html('').addClass('d-none');
return;
}
// Replace the current field and show
sectionTargetWrapper
.html(html)
.removeClass('d-none')
}
});
});
});
return SinisterAjaxRepairman.__super__.render.call(this);
},
/**
* Disposes the view - remove event listeners here
*/
dispose: function () {
if (this.disposed) {
// the view is already removed
return;
}
SinisterAjaxRepairman.__super__.dispose.call(this);
}
});
return SinisterAjaxRepairman;
});
The loaded template just contains the form row to update in the related section :
{{ form_row(form.repairman) }}
{{ form_row(form.reparationDate) }}
I think that my issue is related to the page load events used by Oro to trigger the page-component events and update their contents but I'm stuck at this point, I don't find how to trigger programmatically this update on my Ajax success code, in order to have the same rendering of the fields on an initial Page load and an Ajax reload of the section.
Thank you for your help 🙂
The final fix, that I found thanks to Andrey answer, was to update the JS file like this, with the addition of content:remove and content:changed events on ajax response (success section) :
success: function (html) {
if (!html) {
sectionTargetWrapper
.trigger('content:remove')
.html('')
.trigger('content:changed')
.addClass('d-none');
return;
}
// Replace the current field and show
sectionTargetWrapper
.trigger('content:remove')
.html(html)
.trigger('content:changed')
.removeClass('d-none')
}
Hope it could help ! 🙂
This is my issue, I have an index.html page which loads the reCAPTCHA script explicitly:
<script src="https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit" async defer></script>
I have a template which contains the reCAPTCHA container element (div):
<form id="payment-<%= model.Id %>" class="form" action="" method="POST">
...
<div class="captcha-container"></div> //not being used yet
</form>
And I have a backbone view which injects the template into index.thml:
'use strict';
var Backbone = require('Backbone');
var Validation = require('backbone-validation');
Backbone.Validation = Validation;
Backbone.$ = $;
module.exports = BaseView.extend({
events: {},
formView: null,
initialize: function (options) {
var loadCaptcha = function() {
window.alert('captcha is ready');
};
},
render: function () {
// renders view using my form template
}
});
At this point I'm unable to even trigger the callback function (loadCaptcha), my suspicion is that the issue lies in the load order, the index page is loaded and the "onload=loadCaptcha" event occurs before the backbone view is initialized. I've tried removing the "async" property from the script tag, but no luck. Any ideas of how I can get this to work?
I figured out the solution, by pulling in the recaptcha script directly from the view which renders it. Hope this helps someone in the future.
loadCaptcha: function() {
var self = this;
var getRecaptchaResponse = function(response) {
self.captchaResponse = response;
};
var renderCaptcha = function() {
self.captchaWidgetId = grecaptcha.render('recaptcha-container-' + self.model.get('Id'), {
sitekey : service.settings.recaptchaSiteKey,
callback: getRecaptchaResponse
});
};
window.renderCaptcha = renderCaptcha;
$.getScript('https://www.google.com/recaptcha/api.js?onload=renderCaptcha&render=explicit', function() {});
},
I would put your captch script in a backbone view. Then put it in the render of your main view. Like that it's modular you have a main view and a subview containing the captcha which is loaded when the main view is rendered.
I'm using Dropzone without creating a dropzone form. It works great for me in this way.
But in this case I can not create another instance of Dropzone in my page.
var myDropzone1 = new Dropzone(
document.body,
{
url : "upload1"...
.
.
. some parameters
};
var myDropzone2 = new Dropzone(
document.body,
{
url : "upload'"...
.
.
. some parameters
};
When I do this, I'm getting the error Dropzone already attached.
It's possible, but you can't bind a second dropdzone on the same element, as you did. 2 Dropzones on one element makes no sense. 2x document.body in your solution atm. Try this...
HTML:
<form action="/file-upload" class="dropzone" id="a-form-element"></form>
<form action="/file-upload" class="dropzone" id="an-other-form-element"></form>
JavaScript:
var myDropzoneTheFirst = new Dropzone(
//id of drop zone element 1
'#a-form-element', {
url : "uploadUrl/1"
}
);
var myDropzoneTheSecond = new Dropzone(
//id of drop zone element 2
'#an-other-form-element', {
url : "uploadUrl/2"
}
);
I want to add something here because I have experienced problems with multiple dropzones on the same page.
In your init code you must remember to include var if putting a reference otherwise it isn't dealing with this instance of the dropzone rather trying to access/relate to the others.
Simple javascript but makes a big difference.
init: function(){
var dzClosure = this;
$('#Btn').on('click',function(e) {
dzClosure.processQueue();
});
Note: I'm not using Ajax in order to upload the image on server. For Ajax, One can use url. Attaching the doc DropzoneDoc. It's works like action.( Something like that. Check my solution you can get some idea to do it in your case.
When I working we dropzone, I faced a similar issue working with the Meteor framework.
In the case of Meteor Framework, dropzone is initialized in the below code. This will look different to you, it's the way to initialize in the Meteor framework.
In your case, you can find similarities when using the dropzone library.
Params are added as
params='{name: "Image1"}'
{{> dropzone url=getDropZoneImageUploadURL id='candidate-identity-photo-proof'
init=initFunction params='{name: "Image1"}'
acceptedMimeTypes= 'image/jpeg,image/png,image/jpg' maxFiles=1
success=uploadSuccessHandler maxFilesize=2 dictDefaultMessage= "Photo
ID Proof Photo"
previewsContainer='#upload-photo-id-holder'
previewTemplate=previewTemplateString}}
when the file is getting uploaded check "this" context as mentioned below code. You can use the params value to distinguish the image.
var initFunction = function () {
this.on("addedfile", function () {
if (this.files[1] != null) {
this.removeFile(this.files[0]);
}
});
this.on("sending", function (file, xhr, formData) {
console.log(this); // Here you can get the value
formData.append("type", "image");
});
this.on("error", function (fileInfo, errorMessage) {
var message = "ERROR";
showNotification("error", { message: message }, {});
});
};
I am new to javascript, but I've been hired to give maintenance to an application which is developed in Sencha ExtJS 4. One of the modules I've been asked to modify, is of a component in which I show a tooltip whenever I hover over it. This component can be present in more than one view, it is something like "Customer Details" that is present in many screens of the application. If I hover over this data, I need to show a tooltip, this tooltip shows information retrieved by server (REST). I implemented some logic, but this logic involves the use of many listeners in each of the components that will show the information. For instance, I added a listener in all of the views that requires showing the tooltip:
this.listeners = {
boxready: {
fn: this.onAfterRender,
scope: this
}
And I had to implement this method for every view as well, which is a mess and, for sure, a very bad practice:
/**
* This method is executed after panels are rendered in order to set ToolTip listeners on
* users and workgroups.
*
* #param {Object} scope
*/
onAfterRender: function(scope) {
Ext.defer(function() {
var usElements = Ext.get(Ext.query('.usertooltip', scope.el.dom));
usElements.on({
click: function (e) {
var item = Ext.get(e.target);
if (Ext.isEmpty(item.dom.innerHTML.trim())) {
item.removeCls('usertooltip');
return;
}
if (item.hasCls('usertooltip-clicked')) {
return;
}
item.addCls('usertooltip-clicked');
var user = item.getAttribute('data-info');
UserInfo.getUserInfo(user, false);
if (UserInfo.errorResponse) {
UserInfo.getWGroupInfo(user);
}
UserInfo.displayToolTip(this);
}
});
var wgElements = Ext.get(Ext.query('.wgtooltip', scope.el.dom));
wgElements.on({
click : function (e) {
var item = Ext.get(e.target);
if (Ext.isEmpty(item.dom.innerHTML.trim())) {
item.removeCls('wgtooltip');
return;
}
if (item.hasCls('wgtooltip-clicked')) {
return;
}
item.addCls('wgtooltip-clicked');
var wgroup = item.getattribute('data-info');
WGroupInfo.getWGroupInfo(wgroup, false);
if (UserInfo.errorResponse) {
WGroupInfo.getUserInfo(wgroup);
}
WGroupInfo.displayToolTip(this);
}
});
}, 1000, this);
},
What I do is simply detect if the item is selected based a css class, if so, I handle the events and proceed with logic. But I've been doing some research and I think this can be achieved using a "delegator" but I am not sure how to implement this for my scenario.
What I've been thinking of, so far is to create a "js" class which have a method like an "observer" and whenever listen to someone asking for this tooltip functionality, delegate it to the executing object. But since I am new to javascript and this Sencha ExtJS, my tries have been frustrated. If someone can help me I would really appreciate it.
Thanks in advance.
Best regards.
The best way would be to declare a plugin:
Ext.define('TipPlugin', {
alias: 'plugin.tip',
init: function(c) {
c.on('boxready', this.onBoxReady, this);
},
onBoxReady: function(c) {
var els = this.el.select('.usertooltip');
// Do stuff!
}
});
var c = new Ext.Component({
plugins: ['tip']
});
I'm trying to bind an onChange event of one FilteringSelect to populate another FilteringSelect.
// View
dojo.addOnLoad(function () {
dojo.connect(dijit.byId('filterselect1'), 'onChange', function () {
dijit.byId('filterselect2').store = new dojo.data.ItemFileReadStore(
{ url: "/test/autocomplete/id/" + dijit.byId("filterselect1").value }
);
});
});
The JSON is generated from what I can tell correctly from a Zend Action Controller using a autoCompleteDojo helper.
// Action Controller
public function autocompleteAction()
{
$id = $this->getRequest()->getParam('id');
$select = $this->_table->select()
->from($this->_table, array('id','description'))
->where('id=?',$id);
$data = new Zend_Dojo_Data('id', $this->_table->fetchAll($select)->toArray(), 'description');
$this->_helper->autoCompleteDojo($data);
}
I receive the JSON from the remote datastore correctly, but it does not populate the second FilteringSelect. Is there something else I need to do to push the JSON onto the FilteringSelect?
I couldn't believe this was causing the problem, but the whole issue boiled down to the fact that it appears that a dojo ItemFileReadStore REQUIRES the label property of the JSON to be "name". In the end this is all that it required to wire them together.
dojo.addOnLoad(function () {
dijit.byId('filtering_select_2').store = new dojo.data.ItemFileReadStore({url: '/site/url'});
dojo.connect(dijit.byId('filtering_select_1'), 'onChange', function (val) {
dijit.byId('filtering_select_2').query.property_1 = val || "*";
});
});
UPDATE: The property within Zend form has been fixed as of ZF 1.8.4
Try console.log() in the event to see if it is launched. Changing the store should work, however for other widgets like grid you have also to call refreshing methods.