Input with autocomplete jquery and Odoo rpc call don't show suggestions - javascript

I want use the jquery autocomplete for loading data from backend when the user still pressing keys.
I see that site for making autocomplete with ajax call.
Here the code that i'm using but i'm stucked with results.
I don't understand how the autocomplete take back odoo rpc result and show it in the input form
<label class="control-label" for="timesheet_user_id">Employee:
</label>
<input id="employee_name"/>
And jquery autocomplete
var employee_name = $('#employee_name');
employee_name.autocomplete({
source: function (request, response) {
ajax.jsonRpc("/employee_search", "call", {"name": employee_name.val()})
.then(function (data) {
response(data);
console.debug(response);
});
},
onSelect: function (suggestion) {
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
By console i see that i receive the array of objects but nothing happens on html.
Maybe is wrong the type of data or how i make the rpc call?
Backend function:
#http.route("/employee_search", type="json",
auth="public", website=True)
def employee_search(self, name):
employee_objs = request.env["hr.employee"] \
.sudo().search([("name", "ilike", name)])
if not employee_objs:
return {"fail": True}
suggestions = []
for employee in employee_objs:
employee_vals = {}
employee_vals['value'] = employee.id
employee_vals['data'] = employee.name
suggestions.append(employee_vals)
# make a dict of dicts with employees values
return suggestions
EDIT
Modified into jquery autocomplete the response = data; with
.then(function (data) {
response(data);
console.debug(response);
});
Now the input show two little boxes without text inside.
The received data from rpc call still not shown after input box
EDIT2
the problem is the source:, because if i pass an array of static object i can see them, but with this call nothing happends....where i'm wrong?
employee_name.autocomplete({
source: function (request, response) {
ajax.jsonRpc("/employee_search", "call", {"name": employee_name.val()})
.then(function (data) {
response(data);
console.debug(response);
});
}
});
Data contain the array of values but source don't catch them.

Ok i finally write a code that work for a real time search with suggestions:
Here the JS:
/*Autocomplete that suggest input*/
var serial_number = $('#searchSerial');
serial_number.autocomplete({
minLength: 3,
source: function (request, response) {
ajax.jsonRpc("/serial_search", "call", {"serial": serial_number.val()})
.then(function (data) {
console.debug(data);
if (data.fail) {
// if SN was not found in the db alert the user
// This is non blocking, the user can still compile the form
//alert(_t("Serial Number not found in the system, please fill the form manually"));
serial_number.addClass('error_input');
} else {
serial_number.removeClass('error_input');
response(data);
}
});
},
//If the record return is only one auto select it
response: function (event, ui) {
if (ui.content.length === 1) {
ui.item = ui.content[0];
serial_number.val(ui.item.value);
serial_number.data('ui-autocomplete')._trigger('select', 'autocompleteselect', ui);
serial_number.autocomplete('close');
}
},
select: function (event, ui) {
let brand_id = $("input[name='brand_id']");
let brand_name = $("input[name='brand_name']");
let product_id = $("input[name='product_id']");
let product_name = $("input[name='product_name']");
let serial_number = $("input[name='serial_number']");
brand_id.val(ui.item.brand_id);
brand_name.val(ui.item.brand_name);
product_id.val(ui.item.product_id);
product_name.val(ui.item.product_name);
serial_number.val(ui.item.serial_number);
}
});
The Javascript Code make search when user input at least 3 letters, if only one record match the case, it is auto selected by response:.
The selected: case populate other input field when user select a certain voice into suggestions list.
Here che controller that return suggestions data:
#http.route("/serial_search", type="json",
auth="public", website=True)
def serial_search(self, serial):
"""
Starting from a serial number (portal user input),
serach for the corresponding lot and then search
for product id/name and brand id/name.
Return them to the website form.
"""
serial_domain = [("name", "ilike", serial)]
serial_objs = request.env["stock.production.lot"] \
.sudo().search(serial_domain, limit=10)
if not serial_objs:
return {"fail": True}
suggestions = []
for serial_obj in serial_objs:
serial_vals = {}
serial_vals['data'] = serial_obj.product_id.id
serial_vals['value'] = serial_obj.name
serial_vals['product_id'] = serial_obj.product_id.id
serial_vals['product_name'] = '%s - %s' % (
serial_obj.product_id.default_code, serial_obj.product_id.name)
serial_vals['brand_id'] = serial_obj.product_id.product_brand_id.id
serial_vals['brand_name'] = serial_obj.product_id.product_brand_id.name
serial_vals['serial_number'] = serial_obj.name
suggestions.append(serial_vals)
# make a list of dicts with serial number values
return suggestions

Related

How can ajax based Select2 pre-population be formatted?

We've found several examples of pre-populating selected option for Select2, however none of them we could find deal with formatted list and selection options. We have a JS fiddle at https://jsfiddle.net/gpsx62de/26/ that illustrates the issue. In that fiddle, you can type and L or whatever into the select search and the data is returned, the list is formatted, and if you select something, the selection is formatted.
However if you click the button in that JS Fiddle which is intended to simulate pre-population per https://select2.org/programmatic-control/add-select-clear-items#preselecting-options-in-an-remotely-sourced-ajax-select2 the data is returned (you can uncomment the console.log to see it), but the formatted selection shows undefined for the intended values. Does anyone know of a way to get the formatted values for pre-populated data to display correctly?
// Set up the Select2 control
$('#mySelect2').select2({
ajax: {
url: '/api/students'
}
});
// Fetch the preselected item, and add to the control
var studentSelect = $('#mySelect2');
$.ajax({
type: 'GET',
url: '/api/students/s/' + studentId
}).then(function (data) {
// create the option and append to Select2
var option = new Option(data.full_name, data.id, true, true); //**** DOES IT MATTER WHAT IS PASSED HERE BECAUSE WE ARE NOT DISPLAY THE OPTION TEXT?? ***
studentSelect.append(option).trigger('change');
// manually trigger the `select2:select` event
studentSelect.trigger({
type: 'select2:select',
params: {
data: data //**** THIS DOES NOT SEEM TO SUPPORT FORMATTED SELECTIONS, SO HOW CAN THIS BE DONE? ***
}
});
});
The problem is in format_selection function. The format of the object it receives depends on how it was created. When you use new Option(text, value) it receives only the properties of this Option object, not your original object containing all user info.
A workaround is to check of either possible values in the fuction:
function format_selection(obj) {
let name = obj.name || obj.element.text;
let email = obj.email || obj.element.email;
return $(`<div><b>${name}</b></div><div>(${email})</div>`);
}
For this to work you should append the de property on you Option object:
var option = new Option(data.name, data.id, true, true);
option.email = data.email;
$('#sel').append(option).trigger('change');
The problem, in https://jsfiddle.net/gpsx62de/26/ is with the
function format_selection(obj) {
// Just add this to see the obj
console.log(obj);
return $(`<div><b>${obj.text}</b></div><div>(${obj.id})</div>`);
}
The obj object just contains the Option class data, so:
id: "1",
selected: true,
text: "Leanne Graham",
title: ""
So you have to find a way to pass "data.email" to the "format_selection" method
EDIT
This could be a solution
$('#btn').on('click', function() {
$.ajax({
type: 'GET',
url: 'https://jsonplaceholder.typicode.com/users/1'
})
.then(function(data) {
console.log(data)
// create the option and append to Select2
$('#sel').append($('<option />') // Create new <option> element
.val(data.id) // Set value
.text(data.name) // Set textContent
.prop('selected', true)
.attr('data-name', data.name) // Don't know why the .data(key, value) isn't working...
.attr('data-email', data.email))
.trigger('change');
}); //then
}); //click
And
function format_selection(obj) {
return $(`<div><b>${obj.element.dataset.name}</b></div><div>(${obj.element.dataset.email})</div>`);
}
This is the fiddle https://jsfiddle.net/947jngtu/

mdb_autocomplete drop down does not close on click event

In certain cases, usually after values have been cached from an initial search, I cannot get the mdb_autocomplete drop down to close on a single click. I often have to double click on my selection to get it to close. I am dynamically populating the values in the drop down via an Ajax call to a service method via and controller action method that searches the active directory for the first 30 name values. No call back occurs until a user enters at least 3 values into the mdb_autocomplete selection box. The callback and population of the drop down is working very well, but the click event following the selection of a value often will not close the dropdown. Additionally, we are being forced to use mdb boostrap over the normal bootstrap library. I have included the view, service, and typescript code that is being used to generate and populate the drop down.
View:
<div class="form-group">
<label class="font-weight-bold" for="RequestorSearchInput">Requestor</label>
<input type="text" id="RequestorSearchInput" name="RequestorSearchInput" class="form-control mdb-autocomplete" />
<span id="loading_data_icon"></span>
</div>
Service Method:
public List<string> GetRequestor(string aRequestor)
{
IEnumerable<Dictionary<string, string>> requestorNames;
using (ActiveDirectoryService service = new ActiveDirectoryService(<LDAP Domain Name>))
{
string[] propertiesToLoad = new string[] { "displayName" };
//requestorNames = service.SearchByPartialUsername(aRequestor, propertiesToLoad).Take(30).Union(service.SearchBySurname(aRequestor, propertiesToLoad).Take(30));
requestorNames = service.SearchBySurname(aRequestor, propertiesToLoad).Take(30);
}
return requestorNames.SelectMany(x => x.Values).Where(y => y.IndexOf(",", StringComparison.Ordinal) >= 0).Distinct().ToList();
}
TypeScript:
private handleRequestorSearch() {
let options: string[] = new Array();
// #ts-ignore
let searchval = $("#RequestorSearchInput").val().length;
if (searchval >= 3) {
$.ajax({
url: main.getControllerHREF("GetRequestor", "Search"),
method: "POST",
data: {
requestor: $("#RequestorSearchInput").val()
},
async: false,
dataType: "json",
success: function (names) {
$.each(names, function () {
options.push(this);
});
// #ts-ignore
$("#RequestorSearchInput").mdb_autocomplete({
data: options
}).select(function () {
$("#RequestorSearchInput").focus;
});
}
});
}
}

How do I populate a list field in a model from javascript?

I have a Kendo.MVC project. The view has a model with a field of type List<>. I want to populate the List from a Javascript function. I've tried several ways, but can't get it working. Can someone explain what I'm doing wrong?
So here is my model:
public class Dashboard
{
public List<Note> ListNotes { get; set; }
}
I use the ListNotes on the view like this:
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
}
This works if I populate Model.ListNotes in the controller when the view starts...
public ActionResult DashBoard(string xsr, string vst)
{
var notes = rep.GetNotesByCompanyID(user.ResID, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
Dashboard employee = new Dashboard
{
ResID = intUser,
Type = intType,
FirstName = user.FirstName,
LastName = user.LastName,
ListNotes = listNotes
};
return View(employee);
}
... but I need to populate ListNotes in a Javascript after a user action.
Here is my javascript to make an ajax call to populate ListNotes:
function getReminders(e)
{
var userID = '#ViewBag.CurrUser';
$.ajax({
url: "/api/WoApi/GetReminders/" + userID,
dataType: "json",
type: "GET",
success: function (notes)
{
// Need to assign notes to Model.ListNotes here
}
});
}
Here's the method it calls with the ajax call. I've confirmed ListNotes does have the values I want; it is not empty.
public List<Koorsen.Models.Note> GetReminders(int id)
{
var notes = rep.GetNotesByCompanyID(id, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
foreach (Koorsen.OpenAccess.Note note in notes)
{
Koorsen.Models.Note newNote = new Koorsen.Models.Note()
{
NoteID = note.NoteID,
CompanyID = note.CompanyID,
LocationID = note.LocationID,
NoteText = note.NoteText,
NoteType = note.NoteType,
InternalNote = note.InternalNote,
NoteDate = note.NoteDate,
Active = note.Active,
AddBy = note.AddBy,
AddDate = note.AddDate,
ModBy = note.ModBy,
ModDate = note.ModDate
};
listNotes.Add(newNote);
}
return listNotes;
}
If ListNotes was a string, I would have added a hidden field and populated it in Javascript. But that didn't work for ListNotes. I didn't get an error, but the text on the screen didn't change.
#Html.HiddenFor(x => x.ListNotes)
...
...
$("#ListNotes").val(notes);
I also tried
#Model.ListNotes = notes; // This threw an unterminated template literal error
document.getElementById('ListNotes').value = notes;
I've even tried refreshing the page after assigning the value:
window.location.reload();
and refreshing the panel bar the code is in
var panelBar = $("#IntroPanelBar").data("kendoPanelBar");
panelBar.reload();
Can someone explain how to get this to work?
I don't know if this will cloud the issue, but the reason I need to populate the model in javascript with an ajax call is because Model.ListNotes is being used in a Kendo Panel Bar control and I don't want Model.ListNotes to have a value until the user expands the panel bar.
Here's the code for the panel bar:
#{
#(Html.Kendo().PanelBar().Name("IntroPanelBar")
.Items(items =>
{
items
.Add()
.Text("View Important Notes and Messages")
.Expanded(false)
.Content(
#<text>
#RenderReminders()
</text>
);
}
)
.Events(e => e
.Expand("getReminders")
)
)
}
Here's the helper than renders the contents:
#helper RenderReminders()
{
if (Model.ListNotes.Count <= 0)
{
#Html.Raw("No Current Messages");
}
else
{
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
<br />
}
}
}
The panel bar and the helpers work fine if I populate Model.ListNotes in the controller and pass Model to the view. I just can't get it to populate in the javascript after the user expands the panel bar.
Perhaps this will do it for you. I will provide a small working example I believe you can easily extend to meet your needs. I would recommend writing the html by hand instead of using the helper methods such as #html.raw since #html.raw is just a tool to generate html in the end anyways. You can write html manually accomplish what the helper methods do anyway and I think it will be easier for you in this situation. If you write the html correctly it should bind to the model correctly (which means it won't be empty on your post request model) So if you modify that html using javascript correctly, it will bind to your model correctly as well.
Take a look at some of these examples to get a better idea of what I am talking about:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
So to answer your question...
You could build a hidden container to hold your list values like this (make sure this container is inside the form):
<div id="ListValues" style="display:none">
</div>
Then put the results your ajax post into a javascript variable (not shown).
Then in javascript do something like this:
$('form').off('submit'); //i do this to prevent duplicate bindings depending on how this page may be rendered futuristically as a safety precaution.
$('form').on('submit', function (e) { //on submit, modify the form data to include the information you want inside of your ListNotes
var data = getAjaxResults(); //data represents your ajax results. You can acquire and format that how you'd like I will use the following as an example format for how you could save the results as JSON data: [{NoteID ="1",CompanyID ="2"}]
let listLength = data.length;
for (let i = 0; i < listLength; i++) {
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].NoteID " value="' + data.NoteID +'" />')
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].CompanyID " value="' + data.CompanyID +'" />')
//for your ajax results, do this for each field on the note object
}
})
That should do it! After you submit your form, it should automatically model bind to you ListNotes! You will be able to inpsect this in your debugger on your post controller action.

How to add content via ajax using the popover boostrap

I tried to view different sources and also looked into the forums posting similar question, but it didnt quite help me with the issue that im facing.
I have a text input filed to which I'm adding a popover to show similar a list of names in the database. The inout field checks for validation, to see if the name entered is unique, if not it displays similar names available in the database that could be re-used.
here is the popover snippet:
$("#account_name_create").popover({
title: 'Twitter Bootstrap Popover',
content: function (process) {
this.accountCollection = new ipiadmin.collections.AccountCollection();
var newName = $("#new-account-form #account_name_create").val();
var userFilter = "accountName~'" + newName + "'";
this.accountCollection.fetch({
data: { "f": userFilter,
"sortby": null,
"type":"ipi",
"pageno":0,
"pagesize":2,
"reversesort" : true
},
cache: false,
success: function(model, response, options) {
var states = [];
map = {};
$.each(model.aDataSet, function (i, state) {
map[state.accountName] = state;
states.push(state.accountName);
});
process(states); //gives an error saying 'undefined is not a function (says process is undefined)'
},
error: function(model, response, options) {
console.log('error');
}
});
},
});
here is the html:
<input type="text" id="account_name_create" name="account_name" class="" size="40" />
I'm not sure how why it says 'process' as undefined. Also not sure if this would be the correct way of displaying the data in the popover.
Any ideas??
Thanks!
process doesn't have scope in the success function, only in the content function. If you want to call the process function from within the success function, you could define it somewhere outside of the jQuery call.

Trying to get Jquery working with dynamic select forms in Rails and Active Admin

I'm trying to update a select box based on another..
In my active admin resource, I did the following just for some test data:
controller do
def getcols
list = new Hash
list = {"OPTION1" => "OPTION1", "OPTION2" => "OPTION2"}
list.to_json
end
end
In active_admin.js I have the following
$('#worksheet_type').change(function() {
$.post("/admin/getmanifestcols/", { ws_type: $(this).val() }, function(data) {
populateDropdown($("#column_0"), data);
});
});
function populateDropdown(select, data) {
 select.html('');
alert('hi');
$.each(data, function(id, option) {
select.append($('<option></option>').val(option.value).html(option.name));
});      
}
The above is working in the sense that when my primary select box is changed, the jquery is called and I even get the alert box of 'hi' to be called. However, it's not replacing the contents of the select box with my test OPTION1 and OPTION2 data.
I think I'm passing in the JSON wrong or something, or it's not being read.
What am i missing?
It looks to me as if you're not properly iterating over the map.
What about:
$.each(data, function(value, name) {
select.append($('<option></option>').val(value).html(name));
});
?

Categories