I'm starting to review some requirements for processing a few HTML templates from within a suitelet using suitescript 1.0
I haven't been able to find any information on handlebars JS from within netsuite but I'm curious as to whether there is any requirements to configuring a suitelet that does the server side generation as I would prefer to use it compared to using the client side implementation
If anyone can share anything useful on how to get a suitelet prepared to make use of handlebars that would be greatly appreciated
Below is a sample from one of the Suitelets I use handlebars in. You setup the suitelet like any other by creating a form object and a html field to hold the rendered handlebar template.
var screenTitle = 'Form Title';
var form = nlapiCreateForm(screenTitle);
var pageTemplate = nlapiLoadFile('SuiteBundles/Bundle '+sitBundleId+'/src/sit_html_templates/template.html').getValue();
var hbTemplate = Handlebars.compile(pageTemplate);
Handlebars.registerHelper('if_eq',function(a,b,opts) { if(a == b) { return opts.fn(this); } else { return opts.inverse(this); } });
Handlebars.registerHelper('json', function(context) { return JSON.stringify(context); });
var data = {
"account_id":nlapiGetContext().getCompany(),
"bundle_id":sitBundleId,
"other_data":{}
};
var htmlValue = hbTemplate(data);
var templateField = form.addField('template_section','inlinehtml','',null,'main_group');
templateField.setDefaultValue(htmlValue);
response.writePage(form);
Thats the basics. Of course the Handlebar library must be added to the library sublist on the NetSuite Suitelet record.
Related
Based on the project tracker I have integrated a changelog into my app that relates my UserSettings model to a UserHistory model. The latter contains the fields FieldName, CreatedBy, CreatedDate, OldValue, NewValue.
The relation between both models works fine. Whenever a record is modified, I can see the changes in a changelog table. I now want add an "undo"-button to the table that allows the admin to undo a change he clicks on. I have therefore created a method that is handled by the widget that holds the changelog record:
function undoChangesToUserRecord(changelog) {
if (!isAdmin()) {
return;
}
var fieldName = changelog.datasource.item.FieldName;
var record = changelog.datasource.item.UserSettings;
record[fieldName] = changelog.datasource.item.OldValue;
}
In theory method goes the connection between UserHistory and UserSettings up to the field and rewrites its value. But when I click on the button, I get a "Failed due to circular reference" error. What am I doing wrong?
I was able to repro the issue with this bit of code:
google.script.run.ServerFunction(app.currentPage.descendants.SomeWidget);
It is kinda expected behavior, because all App Maker objects are pretty much complex and Apps Script RPC has some limitations.
App Maker way to implement it would look like this:
// Server side script
function undoChangesToUserRecord(key) {
if (!isAdmin()) {
return;
}
var history = app.models.UserHistory.getRecord(key);
if (history !== null) {
var fieldName = history.FieldName;
var settings = history.UserSettings;
settings[fieldName] = history.OldValue;
}
}
// Client side script
function onUndoClick(button) {
var history = widget.datasource.item;
google.script.run
.undoChangesToUserRecord(history._key);
}
I am binding a SELECT HTML tag with some dynamic values using knockout JS. Additionally, i am trying to set a selected choice which is failing. Please suggest where i am going wrong.
self.level1Choices.selectedChoice = ko.observable(2); - this line does not seem to work.
The JSFiddle for this code is at http://jsfiddle.net/oarp7gwj/7/
The dropdown is not loading in the JSFiddle for some reason. I dont think i have referenced the knockout JS correctly. In my local environment, I am able to load the select box with the values. However, i am not able to set the selected value.
#Wayne Ellery, #QBM5 - please advise since you know about this already :)
You should use var to declare your model object to avoid scoping issues
var viewModel = new DataModel();
The main issue was you need to add to the Datamodel by exposing it through the this variable in the Datamodel.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
};
Take a look at the Helo World example as to how to do this:
http://knockoutjs.com/examples/helloWorld.html
I've scoped this to self as it's a best practice to not worry about this referring to something else mentioned here: http://knockoutjs.com/documentation/computedObservables.html.
I moved the loadAllApprovers method inside the DataModel as this is where it belongs and so that it has access to populate the datamodel.
I added the mobile services client to the constructor so that it can be mocked for testing your model.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
var loadAllApprovers = function () {
var allAppprovers = client.getTable('TABLE');
var query = allAppprovers.select("ID", "FirstName").read().done(function (approverResults) {
self.level1Choices(approverResults);
}, function (err) {
console.log("Error: " + err);
});
};
loadAllApprovers();
};
You were also missing knockout in your jsfiddle.
http://jsfiddle.net/az4rox0q/6/
Template.prices.rendered = function() {
OrderFormContent = new Meteor.Collection(null);
var orderSubmission = function() {
//code that inserts stuff into the OrderFormContent collection
//the key **sqft** is assigned the value of **4000** };
orderSubmission();
};
Template.prices.helpers({
sqft: function() {
return OrderFormContent.findOne().sqft;
}
});
The code above doesn't load. Meteor tries to create the helper of {{sqft}} but can't because OrderFormContent does not get defined until after the page renders. It appears that Meteor tries to define the helper before the page is even rendered.
But I need to define this helper. And I need to have it defined only after the template is rendered (not created).
I cannot just nest Template.prices.helpers inside Template.prices.rendered.
Clarification:
If I comment out the Template.prices.helpers code the page will load. If I then run OrderFormContent.findOne().sqft manually in the console a value of 4000 is returned.
When I un-comment the Template.prices.helpers code the page fails to load and I get a Exception from Deps recompute function: ReferenceError: OrderFormContent is not defined error.
1) Defining global variables inside a function is against good practices of Javascript, and is invalid in strict mode (and thus will be invalid in the future when strict mode becomes a standard).
2) You can easily achieve your goal without defining helper after rendering. In fact, the error is not thrown when the helper is created, but when it's called. To fix this problem it's enough to include a simple check.
var OrderFormContent = null;
var orderFormContentDep = new Deps.Dependency();
Template.prices.rendered = function() {
OrderFormContent = new Meteor.Collection(null);
...
orderFormContentDep.changed();
};
Template.prices.helpers({
sqft: function() {
orderFormContentDep.depend();
if(!OrderFormContent) return null;
var item = OrderFormContent.findOne();
if(!item) return null;
return item.sqft;
});
});
When I got that error I moved the template helper to the client js and it went away. Only that didn't work for my purposes because it executed too often. So, I put it into an Iron Router route method to be rendered.
This is my very first library class that i am writing and i feel like i need to load up on that topic but cannot find the best sources. I have a web forms project that uploads a pdf and creates a qrcode for it and places it in the document. I need to create a library but don't know where to start or the exact structure. Every method it's own subclass in the library class? or can i have them all in one and what is a professional way of going about this.
This is party of my web forms application that i need to create a library for:
void UpdateStudentSubmissionGrid()
{
var usr = StudentListStep2.SelectedItem as User;
var lib = AssignmentListStep2.SelectedItem as Library;
if (usr == null || lib == null) return;
using (var dc = new DocMgmtDataContext())
{
var subs =
(from doc in dc.Documents
where doc.OwnedByUserID == usr.ID && doc.LibraryID == lib.ID
select new {DocID = doc.ID, Assignment = doc.Library.Name, Submitted = doc.UploadDT})
.OrderByDescending(c => c.Submitted)
.ToList();
StudentSubmissionGrid.DataSource = subs;
}
}
How do i start with this method?
By the looks of things you are using this function for a single webpage. You can call that function from any event i.e. user hits submit button. On the. Click the button and it will create a onclick event. Call the code from inside there UpdateStudentSubmissionGrid(); Just make sure the function is not nested inside another event or function. Webforms is already a class, you are just placing a function within the class.
I've just moved to web development and need to know how i can implement below requirement using asp.net and vb.net.
I have three fields in a form which are filled by users. Based on these three values, i need to auto-populate the 4th field. I have planned to implement this in the following way
Write a separate class file with a function to calculate the possible values for the 4th fields based on 1st 3 inputs. This function can return some where between 1-10 values. So I've decided to use drop-down for 4th field, and allow users to select the appropriate value.
Call the above function in onchange function of 3rd field and take and use the return values to populate the 4th field. I'm planning to get the return values in array field.(Does this need a post back?)
Please let me know how if there is better way to implement this.
Thanks.
You may want to consider doing this with Javascript. You could read and control the fields pretty easily with pure Javascript, or using a nice library like jQuery (my favorite). If you did it this way, no post-back would be required and the 4th field would update immediately. (Nice for your users)
You can also do it with ASP.NET for the most part. "onchange" in ASP.NET still requires Javascript as far as I know, it just does some of it for you. A post-back will definitely happen when you change something.
You need javascript or to set autopostback=true on your form elements.
From a user perspective the best thing is to use javascript to populate the field for display, BUT when the form is submitted use your backend function to validate it. This will make sure the user didn't change the value.
An easy way is to use jQuery for the UI (that way you don't have to worry about long winded javascript and deal with browser compatibility as it's already taken care of for you) and have it call to the server for the data. For the server, your easiest route is to return JSON for looping values.
Include your jQuery:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
Then add in a handle for the JavaScript:
<script type="text/javascript">
function autoPopulate() {
var value1 = $('#ddl1').val();
var value2 = $('#ddl2').val();
var value3 = $('#ddl3').val();
var url = 'path/to/your/file.aspx?value1=' + value1 + '&value2=' + value2 + '&value3=' + value3;
$.getJSON(url, function(data) {
data == null ? return false : data = eval(data);
var ddl = $('#ddl4')[0];
for (i = 0; i < data.length; i++) {
var option = new Option(data[i][0], data[i][1]);
if ($.browser.msie) {
ddl.add(option);
} else {
ddl.add(option, null);
}
}
}
}
</script>
(Yes, I know I used a native loop but I'm little lazy here today :) )
Now, for your server side code you'll want your code your page to return data in the format of:
[['value1','text1'],['value2','text2'],['value3','value3']]
so something like:
<script type="vb" runat="server">
Private Sub Page_Init()
// get your data
// loop through it and add in values
// ex.
Dim result As String = "[" //start multi-dimensional array
For Each Item As String In data
result += String.Format("['{0}','{1}'],", _value, _text)
Next
result = result.SubString(0, result.Length - 1) // removes trailing comma
result += "]" // closes off m-array
Response.Write(result)
Response.Flush()
End Sub
</script>