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.
Related
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
Im new to backbone and I'm looking to a very simple 2 view configuration page usig backbone.
I have the following code;
define(
["backbone","...","..."],
function(Backbone, ... , ... ) {
var PopupView = Backbone.View.extend({
initialize: function initialize() {
Backbone.View.prototype.initialize.apply(this,arguments);
},
events: {
"click .save_conf_button": "save_conf",
},
render: function() {
this.el.innerHTML = this.get_popup_template();
return this;
},
save:conf: function save_conf() {
//get the field values from popup_template
//var items = jquery(....);
});
var ExampleView = Backbone.View.extend({
//Starting view
initialize: function initialize() {
Backbone.View.prototype.initialize.apply(this, arguments);
},
events: {
"click .setup_button": "trigger_setup", //Triggers final setup
"click .create_conf_button": "trigger_popup_setup", //This is the conf popup
},
render: function() {
this.el.innerHTML = this.get_start_html();
return this;
},
trigger_popup_setup: function trigger_popup_setup() {
console.log("Pop up");
//this.el.innerHTML = this.get_popup_template();
PopupView.render();
...
},
}); //End of exampleView
return ExampleView;
} // end of require asynch
); // end of require
E.g. The ExampleView is the starting view with a couple of fields and 2 buttons; create popup and save. Upon pressing the create_conf_button I want to render the popup view, however this does not seem to work as I expected. (Uncaught TypeError: PopupView.render is not a function)
I'm not sure how to proceed and additionally what the "best practice" is for generating these types of dialogs?
Additionally, keeping the values filled in on the previous page after returning from the popupview would be preferential.
Thanks for any help
try
new PopupView.render()
you have to create an instance to call the methods this way
#ashish is correct, you have to instantiate an instance of the PopupView before calling its render method. Currently, you have defined a blueprint for a view called PopupView, which will act as a constructor for newly created PopupView view instances. In order to use this defined view I would suggest storing it in ExampleView's render or initialize method:
// Example View's initialize method
initialize: function initialize() {
this.popUpView = new PopupView();
Backbone.View.prototype.initialize.apply(this, arguments);
},
then referencing it in your trigger_popup_setup function as follows:
trigger_popup_setup: function trigger_popup_setup() {
console.log("Pop up");
//this.el.innerHTML = this.get_popup_template();
this.popUpView.render();
...
},
As for storing state Backbone models are used for that :)
In general to nest subviews within a master view in Backbone you can do the following:
initialize : function () {
//...
},
render : function () {
this.$el.empty();
this.innerView1 = new Subview({options});
this.innerView2 = new Subview({options});
this.$('.inner-view-container')
.append(this.innerView1.el)
.append(this.innerView2.el);
}
In this example the master view is creating instances of it's subviews within its render method and attaching them to a corresponding DOM element.
I have the shell view that contains the navbar and the map. To this view is rendered other views that use the map previous rendered. When I'll go to the view perfil the map is removed, but the navbar is maintained, so far so good. My problem is when turn back to the home, the map doesn't appear only appears the div that contains the map. Bellow show the example:
View Shell and view Home:
go to view Perfil:
turn back to home:
heres my code:
app.js
var ev = new Application();
ev.Router = Backbone.Router.extend({
routes: {
"": "home",
"evento/:id" : "evento",
"criarevento" : "criarevento",
"perfil" : "perfil"
},
home: function(){
setTimeout(function(){
$('#rightcolumn').html(new ev.views.Home(ev.shell.map).el);
}, 0);
},
... // other views
perfil: function(){
setTimeout(function(){
$('#home').html(new ev.views.Perfil(ev.shell.template).el);
}, 0);
}
});
$(document).on('ready', function() {
ev.user = new ev.models.Person(); // Holds the authenticated Facebook user
// Load HTML templates for the app
ev.templateLoader.load(['shell', 'home', 'search_keyword', 'evento', 'login', 'quemvai', 'criar_evento', 'home_criar_evento', 'perfil'], function () {
ev.shell = new ev.views.Shell({el: "#shell", model: ev.user});
ev.router = new ev.Router();
Backbone.history.start();
});
});
perfil.js
ev.views.Perfil = Backbone.View.extend({
initialize: function(temp, model){
var that = this;
that.template = _.template(ev.templateLoader.get('perfil'));
that.template2 = temp;
//console.log(this.view);
ev.router.on("route", function(route, params) {
that.$el.html(that.template2());
});
that.render();
},
render: function(map){
this.$el.html(this.template());
return this;
}
});
So far I created an event that when route changes, the shell template that I step to the view perfil is called. But it's not working. What I'm doing wrong?
EDITS:
I change my constructor in view perfil, so that when route changes only fire once and call the render function of ev.shell
ev.views.Perfil = Backbone.View.extend({
initialize: function(){
var that = this;
that.template = _.template(ev.templateLoader.get('perfil'));
ev.router.once("route", function(route, params) {
ev.shell.render();
});
that.render();
},
render: function(map){
this.$el.html(this.template());
return this;
}
});
It looks like you have a document ready even that loads the shell including the map. When you go to the profile page you replace the contents of the #home element. Then when you go back to home you replace the contents of the #rightcolumn element. You never re-render the map.
I think you need to put the map rendering code into the home function of the router as well.
As a side note I noticed you are using setTimeout function. If you are using this so that something renders because it's waiting on something else to load then you should probably get rid of it and listen to an event.
I have this strange issue where a view doesn't show up when I go to the page. However, if I refresh the page, it'll appear.
In my router, I tried to render 2 views like so:
tags: function(tags) {
self = this;
self.multipleTags = tags.split('/');
self.tagsArray = $.grep(self.multipleTags, function(item,index) {
return (item != '');
});
var browseHeader = new BrowseHeader;
var content = new tagsView({query:self.tagsArray});
},
I'm having trouble with my BrowseHeader though but the tagsView works fine. I did try removing my tagsView to see if maybe they were conflicting. However, even with a single view rendering, the header still wouldn't show up until I refresh the page.
Here is what I'm doing in my BrowseHeader view:
var browseHeader = Backbone.View.extend({
initialize: function() {
this.render();
},
template: function() {
dust.render('dust/browseHeader','', function(error, output) {
$('#wrapper').append(output);
});
},
render: function() {
this.template();
},
el: '#wrapper',
events: {
'click .academy_filter' : "click_filter"
},
click_filter: function(event) {
target = event.target;
$('.academy_filter').removeClass('active');
$(target).addClass('active');
EventBus.trigger('header:click_filter', target);
}
});
When I console.log the output, it does display the html for the output despite it not being shown on the page. So I know my dust template is working. When I simplify my BrowseHeader render function to just $('#wrapper').append("this"); I still experience the same issue.
Any ideas?
Update: Apparently it has something to do with browser and pushState because when I changed my router to the following, it worked fine.
Backbone.history.start({pushState: true});
As far as I can tell, there's nothing wrong with your view. This is most likely a timing issue. Your view is probably being initialized (and therefore rendered) before #wrapper exists in the DOM. My guess is that if you try the following, the output will be 0:
dust.render('dust/browseHeader','', function(error, output) {
console.log($('#wrapper').length);
$('#wrapper').append(output);
});
Make sure the view is being created after the DOM has finished loading, like so:
$(document).ready(function() {
var header = new browseHeader();
});
In my app, the <body> tag contains just a single <script type="text/x-handlebars> tag which contains all my views. Sproutcore 2.0 nicely adds a jQuery on-document-ready handler that parses those templates and renders them back into the DOM.
I'd like to call a function on one of the views as soon as it's rendered. The problem is that the re-insertion happens asynchronously, so I don't know when the view is available.
Example
Page
<body>
<script type="text/x-handlebars">
...
{{view "MyApp.TweetInputView"}}
...
</script>
</body>
View:
MyApp.TweetInputView = SC.View.extend({
init: function() {
// act like a singleton
MyApp.TweetInputView.instance = this;
return this._super();
},
focus: function() {
...
this.$().focus();
}
});
Initializer
// if the URL is /tweets/new, focus on the tweet input view
$(function() {
if (window.location.pathname === '/tweets/new') {
// doesn't work, because the view hasn't been created yet:
MyApp.TweetInputView.instance.focus();
}
});
I've also tried SC.run.schedule('render', function() { MyApp.TweetInputView.instance.focus(); }, 'call'); in the hopes that Sproutcore would run that after all the view rendering and insertion, but that does not seem to be the case.
Try this:
MyApp.TweetInputView = SC.View.extend({
didInsertElement: function() {
console.log("I've been rendered!");
}
});