NetSuite Online HTML Form - link to list within NetSuite - html

I have an online html form that uses a select field to choose a record from an existing record list (postcodes specifically) which then auto-populates other fields on the form with the rest of the address. This all works fine except that the dropdown list on the form only goes up so far. I need to know if this a limit on the html dropdown or how many records can be passed to the list in the first place?
Also, ideally, it would be great if there was a way to do it via auto-complete so as you start typing the postcode, it only shows those beginning with said characters - is this possible?

I would set up a custom html template for your online form. You can then hide your standard field and include a custom text input field. Attach an event handler to the custom field to do the lookup via a suitelet. If the auto-population is already working then your event handler can update the hidden standard field once a match is made so Netsuite's built-in sourcing works.

A sample suitelet. The get... functions return anything that can be JSON used by your page.
function service(request, response) {
var obj = {
success: true
};
try {
var step = request.getParameter('step') || 'start';
switch (step) {
case 'start':
obj.choices = getChoiceRoots(request.getParameter('itemid'), request.getParameter('treeName'));
break;
case 'other':
obj.choices = getChoiceChildren(request.getParameterValues('choiceIds[]') || request.getParameterValues('choiceIds'));
break;
default:
throw "Unexpected step: " + step;
}
} catch (e) {
obj.success = false;
obj.message = e.message || e.toString();
nlapiLogExecution('ERROR', "error getting choices", (e.message || e.toString()) + "<br> " + request.getURL() + (e.getStackTrace ? "<br> " + e.getStackTrace().join("<br> ") : ''));
}
_sendJS(request, response, obj);
function _sendJS(request, response, respObject) {
response.setContentType('JAVASCRIPT'); //'application/json');
var callbackFcn = request.getParameter("jsoncallback") || request.getParameter('callback');
if (callbackFcn) {
response.writeLine(callbackFcn + "(" + JSON.stringify(respObject) + ");");
} else response.writeLine(JSON.stringify(respObject));
}
}
Then on your page you'd have a script that uses the suitelet. (the suitelet needs to be available without login and the audience needs to be All.
The code on your custom template or associated script file would look something like:
//suiteletURL will look like https://forms.netsuite.com. RegExp makes that root relative to the domain you are on.
//This is not actually necessary when using JSONP but it can be useful in some circumstances so I left it in this example.
$.getJSON(suiteletURL.replace(new RegExp(".*://[^/]+/"), '/')+"&"+$.param(params) +"&callback=?",
function(d, txtStatus,xhr){
if(d.errorCode){
if(fail){
fail(xhr, txtStatus, null);
}else{
showMessage("#appMessage", d.errorMessage || ("Request Failed with code: "+xhr.responseJSON.errorCode));
}
}else{
success(d, txtStatus, xhr); // your custom success handler. d is already an object; no parsing necessary.
}
});

Related

Double processing after context menu click

I have an extension that inserts text into textarea and similar.
The user can choose text from the popup or the contextmenu.
I have "all_frames" : true so that my content script can see all the iframes - in case a textarea is in an iframe.
After a save to the database (more on this in a bit), if the user uses the contextmenu to add text (handled via messaging from background.js to content.js) to a text area the text is added twice.
If the user uses the popup.html mechanism, which passes text to the same content.js, the text is only entered once (the desired outcomes!).
The message is only passed once by background.js.
For instance, if there are five iframes on a page, and therefore 5 instances of content.js, the contextmenu listener script runs 10 times (not 5, as it should).
If I remove "all_frames" : true from the manifest then the text only adds once.
But I need to keep this setting so I can reach textareas in iFrames. And the popup insert works beautifully everywhere.
The save to the database is what triggers the behavior - and that process is a simple json write to a php file. I wipe the contextmenu as part of that process - but again, it isn't a question of the background.js sending 2 messages. One message is sent, and for some reason the code runs through the messaging routine 2x.
I've tried creating flags to mark once a single successful insertion has happened, but everything is asynch and I don't think this would be helpful even if I could get it to work in some cases.
I feel like I need to kill some old content.js processes or something...
The kicker: if I Reload the Extension (in developer mode) the contextmenu works properly and inserts selected text only once. It's only after I save to the db that the problem occurs.
I'm very close to throwing out contextmenus as a feature and just using the elegant popup.html. But this seems like some weird edge case that would be worth understanding.
Many thanks for taking a look. I've included some code and also a screenshot of the console showing the 10x repeats of the content.js code when inserting into a Google Doc.
background.js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "insertComment", comment: tempText}, function(response) {
// console.log(response.status);
});
});
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.method == 'insertComment'){
var sValue = request.comment;
var currentEltag = document.activeElement.tagName.toLowerCase(); //Checking for currently selected area
console.log("currentEltag before logic: " + currentEltag + " / id: " + document.activeElement.id);
console.log("activeTag: " + activeTag);
// console.log("activeEl: " + activeEl.id + ' / name: ' + activeEl.name + ' /value: ' + activeEl.value);
if (activeTag === undefined || activeTag === null){
console.log('currentEltag in logic: ' + currentEltag);
if (currentEltag === 'iframe'){
activeTag = 'iframe';
console.log('Making activeTag equal iframe');
}
}
var sField = activeEl;
if (activeTag === 'input' || activeTag === 'textarea'){
console.log('Dealing with plain input/textarea - yes! sField is: ' + sField);
var nStart = sField.selectionStart;
var nEnd = sField.selectionEnd;
if (nStart || nEnd == '0'){
console.log("Inside insert sub with starting point: " + nStart + ' and end ' + nEnd + ' with value ' + sValue);
console.log('Print and increment...');
sField.value = sField.value.substring(0, nStart) + sValue + sField.value.substring(nEnd, sField.value.length);
sField.selectionStart = nStart + sValue.length;
sField.selectionEnd = nStart + sValue.length;
console.log('Printed value1...and flag is now: ');
chrome.storage.sync.get("flag", function(data) {
console.log("Flag", data.flag);
});
}
else {
sField.value = sValue;
console.log('Printed value2...');
}
} //End if input or textarea

dynamically get options of a dropdown menu in angular

The main problem is that I have a dropdown menu whose options should be updated dynamically.
The workflow is as follows:
I have an input element connected to an ng-model called toSubmit that when longer than 3 characters should fire an http.get call to fetch the list that should populate the dropdown menu.
So this list will change everytime the toSubmit variable changes. Let's call this list database (in the controller it is $scope.database.
What I am trying right now is a very simple solution that doesn't work most probably because the html DOM that contains the dropdown list is loaded at the very beginning and does not keep track of the changes in the options.
In my controller I have the following part which watches over toSubmit:
$scope.toSubmit = '';
$scope.$watch('toSubmit',function(query){
if (query.length >= 3){
getQueryDatabases.companyNameService({'field':'name','query':query,'numberOfHits':'10'},'CIK').prom.then(
function(dataObject){
$scope.database = dataObject;
// dataObject.forEach(function(item){
// $scope.databaseString.push(item.cik + ' ' + item.companyName);
});
});
}
});
And my html looks like the following:
<label for="nameCompany">Name:</label>
<input type="text" ng-model="toSubmit"></input>
<select ng-model="database" ng-options="line in database"></select>
Now my take was take by binding database with ng-Model I would get the result but I am most likely wrong. Can someone please help me?
I recommend you to use select2 that'll handle things like limiting input before server request and have great look and extendibility.
You need to add angular-ui-select2 to your project.
Here is code for you:
Html:
<input class='form-control' data-ng-model='position.company' data-ng-required data-placeholder='Company:' data-ui-select2='employerSelect2Options' id='company_name' type='hidden'>
JavaScript:
$scope.employerSelect2Options = {
minimumInputLength: 2,
query: function (query) {
var _query = query;
var companies = Restangular.all('companies').getList({query: query.term});
companies.then(function(data) {
var results = {results: []};
_.each(data, function(element, index, list) {
results.results.push({id: element.id, text: element.name});
})
if(!_.contains(_.map(data, function(element){ return element.name; }), _query.term)) {
results.results.push({id: _query.term , text: 'Create company "' + _query.term + '"'});
}
_query.callback(results);
})
}
};
My example also contains logic for add "create company" if zero results returned. In this case position.company will contain text of non found company name in id field and you can check it on server side and create one before assigning id.
This logic in
if(!_.contains
condition.

mvc3 entity framework - convert comma separated list of strings into list<string> in viewmodel, allow users to remove items from list

I am struggling to figure out how to do this with MVC,
I have an entity framework object that has a comma separated list from the db, (can't change the fact that its a horrible csl in the db). I can easily display the list and let them edit it manually. This is rather error prone and would like to split them up and display a list of them in the view. Then allow the user to click a link / button and have them removed from the string and db and the page refreshed to reflect this.
My first thought was to use JQuery to do a ajax json post to do a delete for each item the click an #Html.ActionLink for. I could get it to do the async post back and it would delete the item and would send back a string representing the new string list which I could update the UL with. The second time they clicked a link it would give me a 404, the script I used is:
<script type="text/javascript">
$(document).ready(function () {
$('.viewSeasonsLink').click(function () {
var data =
{
item: $(this).parents('li').first().find('.flagName').text(),
deploymentId: #Model.Id
};
$.post(this.href, data, function (result) {
var list = $("#testme");
list.empty();
var items = result.split(",");
$(items).each(function(index) {
// /* var link = '"' + #Html.ActionLink("Remove", "RemoveItemFromList", "Deployment", null, new { #class = "viewSeasonsLink" }) + '"'; */
var link = '<a class="viewSeasonsLink" href="/SAMSite/Deployment/RemoveItemFromList">Remove</a>';
list.append('<li><span class="flagName">' + items[index] + '</span> - ' + link + ' </li>');
/* list.append('<li><span class="flagName">' + items[index] + '</span> - ' + '\'' + #Html.ActionLink("Remove", "RemoveItemFromList", "Deployment", null, new { #class = "viewSeasonsLink" }) + '\'</li>'); */
});
}, "json");
return false;
});
});
</script>
I could not get the action link to work with the jquery script, so tried hard coding it, still not success.
I then thought I would just try and do a simple actionlink back to a method to remove it and return the normal view, again this posts and will update the db, but will not refresh the webpage at all.
<ul id="testme2">
#foreach (string flag in ViewBag.FeatureFlags)
{
<li><span class="flagName">#flag</span> - #Html.ActionLink("Remove", "RemoveItemFromListTest", "Deployment", null, new { #class = "viewSeasonsLink" })</li>
}
</ul>
public ActionResult RemoveItemFromListTest(string item, int deploymentId)
{
Deployment deployment = db.Deployments.Single(d => d.Id == deploymentId);
ViewBag.CustomerId = new SelectList(db.Customers, "Id", "Name", deployment.CustomerId);
List<string> featureFlags = deployment.FeatureFlags.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
featureFlags.Remove(item);
deployment.FeatureFlags = ConvertBackToCommaList(featureFlags);
ViewBag.FeatureFlags = featureFlags;
//db.SaveChanges();
return View("Edit", deployment);
}
EDIT
released I was being a bit daft at one point:
The second test to get it to do a full post back and do the update was still getting caught by the jquery, (also was not passing in the values). I changed the line to this:
<li><span class="flagName">#flag</span> - #Html.ActionLink("Remove", "RemoveItemFromListTest", "Deployment", new { item = #flag, deploymentId = Model.Id }, null)</li>
which does work, but is a bit naff, it would mean any changes made to the form before the remove link clicked would be lost.
I think I see two issues. One is the initial .Post on the viewSeasonsList click event. You are posting back to the Action that loaded the page, not the Action that will handle the delete. I doesn't seem to me that they would be the same Action base on the approach you described.
var url = '/SAMSite/Deployment/RemoveItemFromList';
then
$.post(url, data, function (result) {
Second, in the Ajax response, when you are rebuilding the list, you are including an href attribute for the links. Why? you are not navigating with those links, you are initiating an Ajax request, which has already been set up.
var link = '<a class="viewSeasonsLink">Remove</a>';
ultimately I had one main problem with the jquery solution. When I added a new LI element it was not being hooked up to the ajax call as this was just happening at document.ready. I now replaced the simple .click with a delegate that will also hook up all elements that are added after the ready event, credit to this page for help with it:
$('#featureflaglist').delegate('.removeflaglink', 'click', RemoveFlagFromList);

Populating dropdown list with JSON data using 2 views/documents

I'm having some trouble populating a dropdownlist with some JSON data, i suspect that the error occurs because of the way im appending the $.post within the #stuff div, but i've tried this a couple of ways and just wont get the hang of it.
The select id="" tag & the div lies within another view (it's not part of this particular document) , is that a problem for populating the dropdown-list this way?
Ive tried to alert out the "listItems" and i've got the option values etc... dont get it why it wont populate.
Any help would be appreciated.
Json-response from the $.post =
{"childrendata":[{"id":"42","parent":"1","fName":"hej","lName":"nisse","birthdate":"2011-10-21"}]}
The jQuery/js:
$("#stuff").append(function(){
$.post("show_relations", {},
function(data)
{
$("#stuff").empty();
json = eval('('+data+')');
if(data == '{"childrendata":[]}')
{
$("#stuff").append("No relations registered.");
}
else
{
var listItems= "";
for (var i = 0; i < json.childrendata.length; i++)
{
listItems+= "<option value='" + json.childrendata[i].fName + "'>" + json.childrendata[i].lName + "</option>";
}
$("#child_list").html(listItems);
}
});
});
});
Edit: Based on your comment, I'll assume your problem is purely single-page.
The problem with that code would appear to be the fact that you're trying to use .append() with a function (which is valid jQuery), but that the function doesn't return anything that jQuery can append to the 'stuff' node; $.post makes an Ajax call, which returns immediately.
Instead, try something like the following (modifying the URL to the Ajax call as required):
$.post("url/to/post/to", {},
function(data) {
$("#stuff").empty(); //Clear your stuff div
var children = data.childrendata; //jQuery automatically unserializes json
if(children.length == 0) {
$("#stuff").append("No relations registered.");
}
else {
$('#stuff').append('<select id="child_list"></select>');
$.each(children,
function(index, value) {
//Append each option to the selectbox
$("#child_list").append("<option value='" + value.fName + "'>" + value.lName + "</option>");
}
);
}
},
'json'
);
$.each() is the generic jQuery iterator, which helps de-clutter the code.
What this does is make an Ajax post to the provided URL, which responds with the serialized json object. The callback takes that response (which jQuery has already unserialized by itself), adds a new select to the '#stuff' div, and then adds the dynamically-created options to the new select.
Endnote: My apologies for not posting the link to the $.each documentation, StackOverflow only allows me to post 2 hyperlinks in a single post currently.

How to localStorage properly with one name?

I want to use localStorage["something"] = item; but the script is loaded in every page, which creates files like:
http_www.google.bg_0.localstorage
http_stackoverflow.com_0.localstorage`
I want the current webpage data to be stored in one file, just like the normal extensions do. But I found that this is possible only while localStorage is accessed from popups.html. Then the outputted file is like:
chrome-extension_cognfheolmcnfppokallnahdaibbaabe_0.localstorage
Does somebody knows how to do that? Or maybe how to use the localStorage from popup.html, without showing it?
EDIT:
When I try to use a function from the background.html like:
chrome.extension.getBackgroundPage().setItem("currentWord",s);
Nothing gets called, whu could this happen?
Here's my background.html:
<html><head><script>
var logging = false;
function setItem(key, value) {
alert("Saving setting");
try {
log("Inside setItem:" + key + ":" + value);
window.localStorage.removeItem(key);
window.localStorage.setItem(key, value);
} catch(e) {
alert("Error inside setItem: " + e);
}
}
function clear() {
window.localStorage.clear();
}
function log(txt) {
if(logging) console.log(txt);
}
</script></head></html>
You need to put values to a local storage inside a background page. In order to do that you need to pass your value there from a content script using message passing.