Is it possible to trigger a function when the Enter key is pressed in the character field.?
The onchange function is not working as inside the function it returns another form.
From js i want to call function get_details of model customer.status.search on clicking enter button
class CustomerStatusSearch(models.TransientModel):
_name = 'customer.status.search'
def get_details(self):
print("enter")
for li in self:
customer = self.env['res.partner'].search([('id', '=', 23)], limit=1)
return {
'view_type': 'form',
'view_mode': 'form',
'view_id': self.env.ref('operation.view_registration_student_form').id,
'res_model': 'res.partner',
'target': 'current',
'res_id': customer.id,
'type': 'ir.actions.act_window'
}
so is there any Javascript code for that?
You can define a new Char field to handle the Enter key event.
Example:
odoo.define('Module_Name.FieldChar', function (require) {
"use strict";
var FieldChar = require('web.basic_fields').FieldChar;
var registry = require('web.field_registry');
var FieldCharCustom = FieldChar.extend({
_onKeydown: function (ev) {
if (ev.which === $.ui.keyCode.ENTER) {
// this._someFunction();
}
this._super.apply(this, arguments);
},
});
registry.add('char_custom', FieldCharCustom);
});
To load the js file, inherit the web.assets_backed:
<template id="assets_backend" inherit_id="web.assets_backend" name="assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/Module_Name/static/src/js/field_char.js"></script>
</xpath>
</template>
Then you have just to set the widget attribute of a char field in the view arch:
<field name="name" widget="char_custom"/>
Update
You can use this.rpc to call get_details on customer.status.search model like they did when clicking a button to validate the inventory
Example:
this._rpc({
model: 'customer.status.search',
method: 'get_details',
args: [record_id]
}).then(function (res) {
});
You can find an example in init method on how they get the inventory id from context
Edit:
When you click for the first time in the custom char field the wizard record id will not be available and you can't call the get_details function because it depends on self. The get_details function does only return an action (open the partner form) which is also possible to do using the js code, just call this.do_action to execute the window action.
The fields values will be available in this.recordData variable if you use it to search for a particular partner.
If you try to open the partner form before saving, Odoo will show the following warning:
The record has been modified, your changes will be discarded. Do you want to proceed?
To avoid that you can simulate the click on the save button using:
$(".o_form_button_save").click();
Example:
odoo.define('MODULE_NAME.FieldChar', function (require) {
"use strict";
var core = require('web.core');
var _t = core._t;
var FieldChar = require('web.basic_fields').FieldChar;
var registry = require('web.field_registry');
var FieldCharCustom = FieldChar.extend({
_onKeydown: function (ev) {
this._super.apply(this, arguments);
if (ev.which === $.ui.keyCode.ENTER) {
var self = this;
var customer_id = 14;
$(".o_form_button_save").click();
this._rpc({
model: 'ir.model.data',
method: 'xmlid_to_res_model_res_id',
args: ["base.view_partner_form"],
}).then(function (data) {
self.do_action(
{
name: _t('Customer Status'),
type: 'ir.actions.act_window',
view_mode: 'tree,form',
res_model: 'res.partner',
target: 'current',
res_id: customer_id,
views: [[data[1], 'form'], [false, 'list']],
});
});
}
},
});
registry.add('char_custom', FieldCharCustom);
});
You use addEventListener 'keypress' then check if the keycode is the Enter key
document.getElementById('inputField').addEventListener('keypress', function(event) {
if (event.keyCode == 13) {
//Run a function here
}
});
Related
I try to learn SAPUI5 with Samples frpm Demo kit Input - Checked. I get an error message: oInput.getBinding is not a function
I have a simple input field xml:
<Label text="Name" required="false" width="60%" visible="true"/>
<Input id="nameInput" type="Text" enabled="true" visible="true" valueHelpOnly="false" required="true" width="60%" valueStateText="Name must not be empty." maxLength="0" value="{previewModel>/name}" change= "onChange"/>
and my controller:
_validateInput: function(oInput) {
var oView = this.getView().byId("nameInput");
oView.setModel(this.getView().getModel("previewModel"));
var oBinding = oInput.getBinding("value");
var sValueState = "None";
var bValidationError = false;
try {
oBinding.getType().validateValue(oInput.getValue());
} catch (oException) {
sValueState = "Error";
bValidationError = true;
}
oInput.setValueState(sValueState);
return bValidationError;
},
/**
* Event handler for the continue button
*/
onContinue : function () {
// collect input controls
var that = this;
var oView = this.getView();
var aInputs =oView.byId("nameInput");
var bValidationError = false;
// check that inputs are not empty
// this does not happen during data binding as this is only triggered by changes
jQuery.each(aInputs, function (i, oInput) {
bValidationError = that._validateInput(oInput) || bValidationError;
});
// output result
if (!bValidationError) {
MessageToast.show("The input is validated. You could now continue to the next screen");
} else {
MessageBox.alert("A validation error has occured. Complete your input first");
}
},
// onChange update valueState of input
onChange: function(oEvent) {
var oInput = oEvent.getSource();
this._validateInput(oInput);
},
Can someone explain to me how I can set the Model?
Your model is fine and correctly binded.
The problem in your code is here, in the onContinue function
jQuery.each(aInputs, function (i, oInput) {
bValidationError = that._validateInput(oInput) || bValidationError;
});
aInput is not an array, so your code is not iterating on an array element.
To quickly fix this, you can put parentheses around the declaration like this:
var aInputs = [
oView.byId("nameInput")
];
Also, you could remove the first two lines of the _validateInput method since they are useless...
Usually, we set the model once the view is loaded, not when the value is changed. For example, if you would like to set a JSONModel with the name "previewModel", you can do as mentioned below.
Note that onInit is called when the controller is initialized. If you bind the model properly as follows, then the oEvent.getSource().getBinding("value") will return the expected value.
onInit: function(){
var oView = this.getView().byId("nameInput");
oView.setModel(new sap.ui.model.json.JSONModel({
name : "HELLO"
}), "previewModel");
},
onChange: function(oEvent) {
var oInput = oEvent.getSource();
this._validateInput(oInput);
},
...
Also, for validating the input text, you can do the following:
_validateInput: function(oInput) {
var oBinding = oInput.getBinding("value");
var sValueState = "None";
var sValueStateText = "";
var bValidationError = false;
if(oBinding.getValue().length === 0){
sValueState = "Error";
sValueStateText = "Custom Error"
}
oInput.setValueState(sValueState);
if(sValueState === "Error"){
oInput.setValueStateText(sValueStateText);
}
return bValidationError;
},
Please note that the code above is not high quality and production ready as it's a quick response to this post :)
I am trying to create an sdk in javscript/jquery for creating templates based on user input, such as the type of templates - profile template, dialog template. These templates require data from an ajax call for their creation.
User Input should include some config param and type of templates.
Since I don't have much experience creating sdk's, I am trying to create a scalable and flexible sdk which can adopt some more functionalities and properties in future.
I am stuck on the problem that what is the basic and best way to create an javascript/jquery sdk?
var dialogTemplate , var = template2 I have added sample templates. The requirement is when user passes template/templates name in tmpConfig.config.type create that particular template/templates by fetching their data simultaneously for each template/templates.Suppose, when call 'dialog template' create dialog template. when call 'dialog template' and 'template2' create both and append it. These template name can be send in array in config.
Below is what I have tried:-
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="mySDK.js"></script>
</head>
<body>
// container for templates
<div id="tmpBox"></div>
</body>
<script type="text/javascript">
const tmpConfig = {
config: {
type: ['dialog','template2',...,...,....]
},
boxId: '#tmpBox'
};
var mySDK= new tmpSDK(tmpConfig );
mySDK.createtemplate(); // create template
</script>
</html>
mySDK.js
function tmpSDK(confg){
// implementation of sdk
this.config = confg;
}
tmpSDK.prototype.createtemplate= function(){
var idsToAppendTemplate = this.config.boxId;
var templateTypes = this.config.type;
// checking number of templates to create
for(var i=0 ; i < templateTypes.length; i++){
if(templateTypes === 'dialog'){
createDialog(idsToAppendTemplate )
}else if(templateTypes === 'template2'){
createTemplate2 (idsToAppendTemplate )
}
}
}
function getData(ajaxConfig){
$.ajax(){
return data;
}
}
// different templates html defined below:-
var dialogTemplate = function(data){
// play with data
var html = '<div class='dialog-template'>MY Dialog Template</div>';
return html;
}
var template2 = function(data){
// play with data
var html = '<div class='template2'>MY Template2</div>';
return html;
}
tmpSDK.prototype.createDialog = function(id){
var ajaxConfig = {
'url' : 'http://dialog endponts/',
....
}
var data = getData(ajaxConfig);
$(id).append(dialogTemplate(data)); // var dialogTemplate
}
tmpSDK.prototype.createTemplate2 = function(id){
var ajaxConfig = {
'url' : 'http://template2endponts/',
....
}
var data = getData(ajaxConfig);
$(id).append(template2(data) ); //// var template2
}
Please, consider to create your sdk as jQuery module with Class using.
(function ( $ ) {
$.fn.mySdk = function(options) {
const element = $(this);
const sdk = new MySdk(options, element);
element.data('plugin-my-sdk', sdk);
return $(this);
};
$.fn.getMySdk = function() {
const element = $(this);
return element.data('plugin-my-sdk');
};
class MySdk {
constructor(options, element) {
this.element = element;
this.settings = $.extend({
type: 'dialog',
}, options );
this.fetchTemplate().done(this.applyTemplate.bind(this));
}
fetchTemplate() {
return $.post({
url: `${document.location.origin}/your-endpoint-for-getting-template`,
data: {
'id': this.element.attr('id'),
'type': this.settings.type
}
});
}
applyTemplate(template) {
this.element.html(template);
}
apiMethod() {
const yourElement = this.element;
const yourElementId = this.element.attr('id');
const yourType = this.settings.type;
}
}
}( jQuery ));
// This snippet - is example of using your sdk
jQuery(function ($) {
const element = $('#tmpBox');
element.mySdk({
type: 'dialog'
});
const sdk = element.getMySdk();
sdk.apiMethod();
});
What this snippet do?
Wrap jQuery function for creating a not global scope and for avoiding jQuery conflict with $ function name
Uses MySdk class for the element.
This works for the case when there is only one jquery element in collection taking by the selector. In this case - const element = $('#tmpBox'); is taking only one element.
This snippet
this.settings = this.settings = $.extend({
type: 'dialog',
}, options );
merges default options with your options. If there is no option in your options object - then default option will be used
If you need to use jquery collection
For example, your sdk element is $('.tmpBox') where is there are more than 1 element - please, consider to use in mySdk each function for init every element.
const elements = $(this);
element.each(function(){
// init for every element;
})
I am using bootstrap typeahead (GitHub) to create search forms. After user inputs something I am doing elasticsearch query using elasticsearch.js and returning results. The problem is that results displayed in typeahead are always one character behind, not suggesting correct values.
Typeahead input:
<input type="text" data-provide="typeahead" class="form-control typeahead" id="searchInputId" placeholder="Search" autocomplete="off">
Here is my code:
var elasticsearchAddress = "exampleserver.com:9200";
var elasticsearchClient = createElasticsearchClient(elasticsearchAddress);
var data = [];
$("#searchInputId").typeahead({ source:data, items:10, fitToElement:true });
$("#searchInputId").on("input", function(){
var searchTerm = $("#searchInputId").val();
elasticsearchMathPhrasePrefixSearch(elasticsearchClient, searchTerm, function () {
$("#searchInputId").data('typeahead').source = getElasticsearchSearchResultsArray();
});
});
elasticsearchMathPhrasePrefixSearch() function
function elasticsearchMathPhrasePrefixSearch(client, searchPhrase, callback) {
console.log("Searching for: " + searchPhrase);
client.search({
body: {
"query": {
"match_phrase_prefix": {
"accountName": searchPhrase
}
}
}
}, function (error, response) {
if (error) {
console.trace('ELASTICSEARCH: Search query failed');
} else {
console.log('ELASTICSEARCH: Search query OK');
var doc = response.hits.hits;
elasticsearchSearchResultsArray = getDocs(doc);
}
callback();
});
}
getDocs() function
function getDocs(doc){
var searchResultsArray=[];
for(var i = 0; i < doc.length; i++){
searchResultsArray.push(doc[i]._source.accountName);
}
return searchResultsArray;
getElasticsearchSearchResultsArray() function
function getElasticsearchSearchResultsArray(){
return elasticsearchSearchResultsArray;
}
elasticsearchSearchResultsArray is a global array that holds the results. Because of the JS async nature I had no other idea to make it work.
EDIT:
Ok, I modified my code so the source is updated correctly with help of this Issue #1997. But now I have got another problem. The typeahead dropdown is not displayed when I type.
My new code:
var empty = [];
$("#searchInputId").typeahead({ source:empty, items:10, fitToElement:true });
$("#searchInputId").on("keyup", function(ev){
ev.stopPropagation();
ev.preventDefault();
//filter out up/down, tab, enter, and escape keys
if( $.inArray(ev.keyCode,[40,38,9,13,27]) === -1 ){
var self = $(this);
//set typeahead source to empty
self.data('typeahead').source = [];
//active used so we aren't triggering duplicate keyup events
if( !self.data('active') && self.val().length > 0){
self.data('active', true);
//Do data request. Insert your own API logic here.
var searchTerm = self.val();
elasticsearchMathPhrasePrefixSearch(elasticsearchClient, searchTerm, function() {
//set this to true when your callback executes
self.data('active',true);
//set your results into the typehead's source
self.data('typeahead').source = getElasticsearchSearchResultsArray();
//trigger keyup on the typeahead to make it search
self.trigger('keyup');
//All done, set to false to prepare for the next remote query.
self.data('active', false);
});
}
}
});
Try to use on("keyup") instead. input is 1 character behind.
Ok, I resolved it myself. I switched from bootstrap3-typeahead to jQuery UI Autocomplete. It is working great and the script is much smaller.
New code:
$("#searchInputId").on("keydown", function () {
$("#searchInputId").autocomplete({
source: function(request, response) {
var searchTerm = $("#searchInputId").val();
elasticsearchMathPhrasePrefixSearch(elasticsearchClient, searchTerm, function (){
response(getElasticsearchSearchResultsArray());
});
}
});
});
I want to use a select x-editable in my Meteor application. My goal is to assign users to groups. This should be reactive, so when you assign a user, other clients should see the changes. The current problem is that the assignment works (data-value changes), but only the user who made the change is able to see the new value.
Here is my code:
Template.userGroup.rendered = function() {
var groupId = this.data._id;
var sourceUsers = [];
Users.find().forEach(function(user) {
sourceUsers.push({value: user._id, text: user.username});
});
Tracker.autorun(function() {
$('.assign-user').editable("destroy").editable({
emptytext: "Empty",
source: sourceUsers,
success: function(response, result) {
if (result) {
Groups.update({_id: groupId}, {$set: {adminId: result}});
}
}
});
});
};
<template name="userGroup">
</template>
I already tried to "destroy" the stale x-editable and put it inside the Tracker.autorun function, but unfortunately, this does not work.
Any help would be greatly appreciated.
I don't use Tracker.autorun but I use x-editable for inline editing like this:
(also used it for group assigments - just like your case, but found it too clumsy on the UI side). Anyway, here's my code:
Template
<template name="profileName">
<td valign='top'>
<div id="profileNameID" class="editable" data-type="text" data-rows="1">{{profile.name}}</div>
</td>
</template>
And on the JS side
Template.profileName.rendered = function () {
var Users = Meteor.users;
var container, grabValue, editableColumns, mongoID,
_this = this;
var container = this.$('#profileNameID');
var editableColumns = container.size();
grabValue = function () {
var gValue = $.trim(container.html());
return gValue;
};
$.fn.editable.defaults.mode = 'inline';
return container.editable({
emptytext: 'Your name goes here',
success: function (response, newValue) {
var mongoID = removeInvisibleChars($(this).closest("tr").find(".mongoid").text());
var editedUser = _users.findOne({
_id: mongoID
});
Meteor.users.update(mongoID, {
$set: {
"profile.name": newValue
}
});
return container.data('editableContainer').formOptions.value = grabValue;
}
});
Update happens immediately on all subscribed authorized clients.
Extending the example found at Autosave in MVC (ASP.NET), I wanted to create a partial to reuse in my application. I have one view with a tabbed layout, and each tab has its own form, and this is causing problems, namely that every form tries to submit every time, and only the first timestamp in the document updates. I understand why this is happening, but I don't know how I can fix it.
Partial's cshtml:
<div class="form-group">
<label class="control-label col-lg-2" for=""> </label>
<div class="col-lg-10">
<span class="help-block" id="autosaveTime">Not Autosaved</span>
</div>
</div>
#{
var autosaveString = "'" + #ViewData["autosaveController"] + "'";
if (ViewData["autosaveAction"] != null && ViewData["autosaveAction"] != "")
autosaveString += ", '" + ViewData["autosaveAction"] + "'";
}
<script type="text/javascript">
$(document).ready(function () {
autosave(#Html.Raw(autosaveString));
});
</script>
Javascript:
//methodName is optional-- will default to 'autosave'
function autosave(controllerName, methodName)
{
methodName = typeof methodName !== 'undefined' ? methodName : 'autosave'
var dirty = false;
$('input, textarea, select').keypress(function () {
dirty = true;
});
$('input, textarea, select').change(function () {
dirty = true;
});
window.setInterval(function () {
if (dirty == true) {
var form = $('form');
var data = form.serialize();
$.post('/' + controllerName + '/' + methodName, data, function () {
$('#autosaveTime').text("Autosaved at " + new Date);
})
.fail(function () {
$('#autosaveTime').text("There was a problem autosaving, check your internet connection and login status.");
});
dirty = false;
}
}, 30000); // 30 seconds
}
I have 2 ideas on how to fix it, but not sure which is more maintainable/workable:
Give each form an id, and pass that to the partial/autosave function. Add the name to the autosavetime text block for updates, and to determine which form to serialize/submit.
Somehow use jquery's closest function to find the form where the autosave block was placed, and use that to do what I was doing explicitly with #1.
First, make the URL using your Razor helper's Html extension (dynamically piecing URLs like this in JavaScript is unnecessarily risky). Take that, and stuff it in a data attribute on the tab control like so:
<div class="tab autosave" data-action-url='#Html.Action("Action", "Controller")'>
<form>
<!-- Insert content here -->
</form>
</div>
Then, you'll want something like this ONCE -- do not include it everywhere, and remove the javascript from your partial completely:
$(function() {
// Execute this only once, or you'll end up with multiple handlers... not good
$('.autosave').each(function() {
var $this = $(this),
$form = $this.find('form'),
dirty = false;
// Attach event handler to the tab, NOT the elements--more efficient, and it's always properly scoped
$this.on('change', 'input select textarea', function() {
dirty = true;
});
setInterval(function() {
if(dirty) {
// If your form is unobtrusive, you might be able to do something like: $form.trigger('submit'); instead of this ajax
$.ajax({
url : $this.data('action-url'),
data : $form.serialize()
}).success(function() {
alert("I'm awesome");
dirty = false;
});
}
}, 30 * 1000);
});
});