I am trying to see if a value in the "apply" sublist for customer payment data has changed and do some action based on it.
My SuiteScript is as follows:
define(['N/record', 'N/https'],
function(record,https)
{
function afterSubmit(context)
{
var oldRec = context.oldRecord;
log.debug({title: 'oldRec ', details: oldRec });
// This log shows that the JSON has an
// attribute called sublists which contains "apply" which has all the applied payments
// eg: {"id":"1234", "type":"customerpayment", "fields":{all the fields},
// "sublists": {"apply" : {"line 1"...}}}
var oldRecSublists = oldRec.sublists;
log.debug({title: 'oldRecApply ', details: oldRecSublists });
// This returns empty or null though there is data
What am I doing wrong here?
Basically what I am trying to achieve is compare the context.oldRecord.sublists.apply and context.newRecord.sublists.apply to find if the amt has changed or not.
Is there a better way to do this is SuiteScript 2.0?
Thanks in advance!
Part of what is going on there is that it looks like you are trying to spelunk the NS data structure by what you see in the print statement. You are not using the NS api at all.
When you send the NS object to the log function I believe it goes through a custom JSON.stringify process so if you want to just inspect values you can do:
var oldRecObj = JSON.parse(JSON.stringify(oldRec));
now oldRecObj can be inspected as though it were a simple object. But you won't be able to manipulate it at all.
You should be using the NS schema browser
and referring to the help docs for operations on N/record
A snippet I often use for dealing with sublists is:
function iter(rec, listName, cb){
var lim = rec.getLineCount({sublistId:listName});
var i = 0;
var getV = function (fld){
return rec.getSublistValue({sublistId:listName, fieldId:fld, line:i});
};
var setV = function(fld, val){
rec.setSublistValue({sublistId:listName, fieldId:fld, line:i, value:val});
};
for(; i< lim; i++){
cb(i, getV, setV);
}
}
and then
iter(oldRec, 'apply', function(idx, getV, setV){
var oldApplied = getV('applied');
});
Related
I'm using Parse.com as my backend and after Query how can I fill an array with all the data inside the Parse object? how can I avoid re-mapping? example:
$scope.addContList = contacts.map(function(obj) { // re-map!!!!
return {name: obj.get("name")}; // mapping object using obj.get()
});
I'm mapping my Parse object's properties one by one: name: obj.get("name"), etc. is there a better way?
$scope.addContList = [];
var ActivityContact = Parse.Object.extend("ActivityContact2");
var query = new Parse.Query(ActivityContact);
query.equalTo("activityId", $scope.objId);
query.find({
success: function(contacts) {
console.log("Successfully retrieved " + contacts.length + " contact.");
$scope.$apply(function() {
/*$scope.addContList = contacts.map(function(obj) {
return {name: obj.get("name")}; // mapping object using obj.get()
});*/
for (var i = 0; i < contacts.length; i++) {
$scope.addContList.push(contacts.ALL_PROPERTIES); // contacts.ALL_PROPERTIES does not exist, I'm looking a way to do that and avoid mapping?
}
});
console.log("--->>>"+JSON.stringify($scope.addContList, null, 4));
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
Should I use Underscore library, is that the only way to go?
I have seen some ppl using PFQuery but I don't know what is that, is PFQuery better for this?
Thanks!
The other answers are correct, but I think it's unnecessary to launch a digest cycle every time you add an item from contacts to $scope.addContList. Something like this should be sufficient:
query.find({
success: function (contacts) {
$scope.apply(function () {
// 1) shallow-copy the list of contacts...
// (this is essentially what you are trying to do now)
$scope.addContList = contacts.slice();
// or 2) just assign the reference directly
$scope.addContList = contacts;
// or 3) transform the Parse.Object instances into
// plain JavaScript objects
$scope.addContList = contacts.map(function (c) {
return c.toJSON();
});
});
},
error: function (object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
Options 1) and 2) will correspond to a template similar to
<div ng-repeat="cont in addContList">{{ cont.get('name') }}</div>
while option 3) can be used like
<div ng-repeat="cont in addContList">{{ cont.name }}</div>
If you change
$scope.addContList = contacts[i];
to:
$scope.addContList.push(contacts[i]);
you should be good to go. Your previous code was re-assigning addContList to be each element in the contacts array, instead of adding the element to it. So at the end of your for loop, $scope.addContList would just be the last contact in your contacts array.
Change:
$scope.addContList = contacts[i];
to
$scope.addContList.push(contacts[i]);
I am working on an app to store data offline. My problem is when I try to retrieve the data from local storage for update/edit, it keeps calling only the id of the first item, and not calling the id of the data in view.
Please what am I doing wrong?
Here is my code for loading employees:
// load cases from localStorage
var employees;
if (localStorage.getItem('employees')) {
employees = JSON.parse(localStorage.getItem('employees'));
} else {
// If no cases, create and save them
employees = [];
// offling storing of our cases
localStorage.setItem('employees', JSON.stringify(employees));
}
// show case listing in list view page
var showEmployees = function () {
//erase existing content
$('#employee_list').html('');
//insert each employee
for (var i = 0; i<employees.length; i++) {
addEmployees(employees[i]);
}
};
Here is my code to add an employee to list view:
//add an eliment to list view
var addEmployees = function (empData) {
//HTML content of one list element
var listElementHTML = '<li><a class="employee_list" ui-btn ui-btn-e ui-btn-icon-right ui-icon-carat-r" data-transition="fade" data-split-icon="delete" href="#item'+empData.id+'">' + empData.employeename + '<br> ' + empData.dateofbirth + '</br></a></li>';
//appending the HTML code to list view
$('#employee_list').append(listElementHTML);
};
Here is my code for Edit function:
//User input to edit form
$('#edit_employee_page').on('click' , function () {
var editEmployee = JSON.stringify({
id: employees.length+1,
employeeno: $('#employeeno').val(),
employeename:$('#employeename').val(),
stateoforigine:$('#stateoforigine').val(),
employeephone: $('#employeephone').val(),
dateofbirth:$('#dateofbirth').val()
});
//Alter the slected data
localStorage.setItem("employees", JSON.stringify(employees));
return true;
});
for (var i in employees) {
var id = JSON.parse(localStorage.getItem(employees[i]));
}
Here is my code for the Edit button:
//register Edit button
$('.edit_button').live('click', function (e) {
alert('I was Cliked!');
e.stopPropagation();
$.each(employees, function(a, b) {
//if(b.id == employees[i]){
$('#id').val(b.id);
$('#employeeno').val(b.employeeno);
$('#employeename').val(b.employeename);
$("#stateoforigine").val(i.stateoforigine);
$('#employeephone').val(b.employeephone);
$('#dateofbirth').val(b.dateofbirth);
$("#id").attr("readonly","readonly");
$('#employeeno').focus();
$.mobile.changePage('#edit_employee_page');
return false;
//}
});
});
Here is my local Storage:
[
{"id":1,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1965"},
{"id":2,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1966"},
{"id":3,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1966"},
{"id":4,
"employeeno":"DAST/003/2003",
"employeename":"Gold Base",
"stateoforigine":"",
"employeephone":"",
"dateofbirth":"12/03/1986"}
]
Thanks for helping me out
The way you are storing your employees into localStorage is correct, but the way you are getting them out is incorrect. You stored your employees by stating:
localStorage.setItem("employees", JSON.stringify(employees));
So, in order to retrieve them, you must use:
var employees = JSON.parse(localStorage.getItem("employees"));
You see, you stored the data as a string with a key of "employees"; therefore, you can only retrieve it by that key. Since all data stored in localStorage is saved as a string, you must use JSON.parse() to convert the data back into an object - an array in this case. Then you can iterate over your employees.
Update:
You should be running this code as soon as the page is rendered (see below). I'm not sure how you're doing that - if you're using an IIFE or jQuery's document.ready() function. I don't think it's necessary to store an empty array into localStorage if none were loaded initially, so, I took your else clause out.
var employees = [];
if (localStorage.getItem('employees') !== null) {
employees = JSON.parse(localStorage.getItem('employees'));
}
Debug this line-by-line when it runs and make positive your employees variable contains data. If it doesn't contain data, well then, there's nothing to edit.
If, however, there is data, then execute your showEmployees() function. Oddly, I'm not seeing in your code where you actually call this. Is it bound to a button or action in your UI? Also, what is that for loop doing after your $('#edit_employee_page') click event function? It's trying to read data from localStorage improperly and it does nothing.
I think if you simply stepped through your code one line at a time using breakpoints and desk-checking your inputs/outputs you'd find out where you're going wrong.
It also appears that there's a disconnect in your code. May be you left out some lines; you define a string editEmployee but out of the blues you store JSON.stringify(employees) whereas employees is not defined in your code:
$('#edit_employee_page').on('click' , function(){
var editEmployee = JSON.stringify({
id: employees.length+1,
//........
});
//Alter the slected data
localStorage.setItem("employees", JSON.stringify(employees));
return true;
});
I had a similar task to do . I did it this way.
I passed the dynamic Id to be passed as an id attribute
id="'+empData.id+'"
and then inside the
$('.edit_button').live('click', function (e) {
alert('I was Cliked!');
var empId=$(this).attr('id');
rest of the code is same.
I am trying to implement the autocomplete with data from a database.
The data looks like
[
{"id":"1",name:"Jon"},
{"id":"2",name:"Carl"},
{"id":"3",name:"Jon"},
]
There is no example using such data even though this would be more common than selecting a string from a bunch of strings. Having the user select "Jon" doesn't mean anything unless you know the ID.
I was trying to add code to the following page and listen to an event when the user selects an item but first of all it's not documented in code and in the api what event that is and second none of the events seem to be triggered. Hopefully the event object would pass the object that was selected by the user (not the string) or the index of the selected item so I don't have to use access the private variable to get the object that was chosen.
ac1.dispose();
var DataItem = function(id,value){
this.id=id;
this.value=value;
};
DataItem.prototype.toString=function(){
return this.value;
};
DataItem.prototype.valueOf=function(){
return this.value;
};
var tcMovies = [
new DataItem(1,"one"),
new DataItem(2,"onetwo")
];
var ac1 = goog.ui.ac.createSimpleAutoComplete(
tcMovies, document.getElementById('txtInput1'), false);
goog.events.listen(ac1,goog.ui.ac.AutoComplete.EventType.SELECT,
function(e){
//never gets called
console.log(e);
//hoping to get an index so I can do something like
// ac1.getMatcher().rows_[selectedindex].id
// probably should not use rows_ but there is no get
// method for that either
}
);
Fiddling around a bit and looking through the source code I did find what hopefully is the way to implement it. The following comment in autocomplete.js would suggest it is the correct way:
/**
* Field value was updated. A row field is included and is non-null when a
* row has been selected. The value of the row typically includes fields:
* contactData and formattedValue as well as a toString function (though none
* of these fields are guaranteed to exist). The row field may be used to
* return custom-type row data.
*/
UPDATE: 'update',
Open the following page, press F12 and run the following code in the console.
//remove existing autocomplete
ac1.dispose();
//define data, need toString
// to display the values
var DataItem = function(id,value){
this.id=id;
this.value=value;
};
DataItem.prototype.toString=function(){
return this.value;
};
// create data
var tcMovies = [
new DataItem(1,"abbbbbbbb"),
new DataItem(2,"aabbbbbbb"),
new DataItem(3,"aaabbbbbb"),
new DataItem(4,"aaaabbbbb"),
new DataItem(5,"aaaaabbbb"),
new DataItem(6,"aaaaaabbb"),
];
// use easy method to create and attach the autocomplete
var ac1 = goog.ui.ac.createSimpleAutoComplete(
tcMovies, document.getElementById('txtInput1'), false);
//listen to UPDATE
goog.events.listen(ac1,goog.ui.ac.AutoComplete.EventType.UPDATE,
function(e){
//e.row should be the selected value
console.log("user selected",e.row);
}
);
I have a Single Page Application that is working pretty well so far but I have run into an issue I am unable to figure out. I am using breeze to populate a list of projects to be displayed in a table. There is way more info than what I actually need so I am doing a projection on the data. I want to add a knockout computed onto the entity. So to accomplish this I registered and entity constructor like so...
metadataStore.registerEntityTypeCtor(entityNames.project, function () { this.isPartial = false; }, initializeProject);
The initializeProject function uses some of the values in the project to determine what the values should be for the computed. For example if the Project.Type == "P" then the rowClass should = "Red".
The problem I am having is that all the properties of Project are null except for the ProjNum which happens to be the key. I believe the issue is because I am doing the projection because I have registered other initializers for other types and they work just fine. Is there a way to make this work?
EDIT: I thought I would just add a little more detail for clarification. The values of all the properties are set to knockout observables, when I interrogate the properties using the javascript debugger in Chrome the _latestValue of any of the properties is null. The only property that is set is the ProjNum which is also the entity key.
EDIT2: Here is the client side code that does the projection
var getProjectPartials = function (projectObservable, username, forceRemote) {
var p1 = new breeze.Predicate("ProjManager", "==", username);
var p2 = new breeze.Predicate("ApprovalStatus", "!=", "X");
var p3 = new breeze.Predicate("ApprovalStatus", "!=", "C");
var select = 'ProjNum,Title,Type,ApprovalStatus,CurrentStep,StartDate,ProjTargetDate,CurTargDate';
var isQaUser = cookies.getCookie("IsQaUser");
if (isQaUser == "True") {
p1 = new breeze.Predicate("QAManager", "==", username);
select = select + ',QAManager';
} else {
select = select + ',ProjManager';
}
var query = entityQuery
.from('Projects')
.where(p1.and(p2).and(p3))
.select(select);
if (!forceRemote) {
var p = getLocal(query);
if (p.length > 1) {
projectObservable(p);
return Q.resolve();
}
}
return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var list = partialMapper.mapDtosToEntities(
manager,
data.results,
model.entityNames.project,
'ProjNum'
);
if (projectObservable) {
projectObservable(list);
}
log('Retrieved projects using breeze', data, true);
}
};
and the code for the partialMapper.mapDtosToEntities function.
var defaultExtension = { isPartial: true };
function mapDtosToEntities(manager,dtos,entityName,keyName,extendWith) {
return dtos.map(dtoToEntityMapper);
function dtoToEntityMapper(dto) {
var keyValue = dto[keyName];
var entity = manager.getEntityByKey(entityName, keyValue);
if (!entity) {
extendWith = $.extend({}, extendWith || defaultExtension);
extendWith[keyName] = keyValue;
entity = manager.createEntity(entityName, extendWith);
}
mapToEntity(entity, dto);
entity.entityAspect.setUnchanged();
return entity;
}
function mapToEntity(entity, dto) {
for (var prop in dto) {
if (dto.hasOwnProperty(prop)) {
entity[prop](dto[prop]);
}
}
return entity;
}
}
EDIT3: Looks like it was my mistake. I found the error when I looked closer at initializeProject. Below is what the function looked like before i fixed it.
function initializeProject(project) {
project.rowClass = ko.computed(function() {
if (project.Type == "R") {
return "project-list-item info";
} else if (project.Type == "P") {
return "project-list-item error";
}
return "project-list-item";
});
}
the issue was with project.Type I should have used project.Type() since it is an observable. It is a silly mistake that I have made too many times since starting this project.
EDIT4: Inside initializeProject some parts are working and others aren't. When I try to access project.ProjTargetDate() I get null, same with project.StartDate(). Because of the Null value I get an error thrown from the moment library as I am working with these dates to determine when a project is late. I tried removing the select from the client query and the call to the partial entity mapper and when I did that everything worked fine.
You seem to be getting closer. I think a few more guard clauses in your initializeProject method would help and, when working with Knockout, one is constantly battling the issue of parentheses.
Btw, I highly recommend the Knockout Context Debugger plugin for Chrome for diagnosing binding problems.
Try toType()
You're working very hard with your DTO mapping, following along with John's code from his course. Since then there's a new way to get projection data into an entity: add toType(...) to the end of the query like this:
var query = entityQuery
.from('Projects')
.where(p1.and(p2).and(p3))
.select(select)
.toType('Project'); // cast to Project
It won't solve everything but you may be able to do away with the dto mapping.
Consider DTOs on the server
I should have pointed this out first. If you're always cutting this data down to size, why not define the client-facing model to suit your client. Create DTO classes of the right shape(s) and project into them on the server before sending data over the wire.
You can also build metadata to match those DTOs so that Project on the client has exactly the properties it should have there ... and no more.
I'm writing about this now. Should have a page on it in a week or so.
Our application currently shares messages between the Java and Javascript side. They are stored as resource bundles in the class path, and we have a custom controller that returns all the messages as Json. The client side code look like this:
// This calls the controller to get all the messages
var messages = MessageBundle();
var text = messages.get('my.message', 1);
This is great because we can mock "messages" in our unit tests.
I want to start using JAWR for this, since we already use it for other things. The problem is JAWR generates the following Javascript object:
var text = messages.my.message(1);
This means the code cannot be unit tested anymore unless the unit tests also define a global "messages" variable with the right nested objects. Is there a way around this? Any idea how to extend JAWR to make this unit-testable?
Currently my work around is:
function messages() {
var args = Array.prototype.slice.call(arguments);
var messageId = args.shift();
var messageFunc = window.messages;
messageId.split('.').forEach(function(part) {
messageFunc = messageFunc[part];
});
return messageFunc(args);
}
// Same syntax as the old one, but uses the JAWR object behind the scenes
// This function is easy to mock for a unit test
var text = messages('my.message', 1);
Thanks for any ideas!
Maybe next samples can help you.
1)
function messagesTester(funcPath,id) {
var args=funcPath.split('.'),root=window.messages;
for(var i=0;i<args.length;i++)root=root[args[i]];
return root(id);
// or if more that one parameter for *func*, then, for example:
// return root.apply(null,Array.prototype.slice(arguments,1));
}
var text = messagesTester('my.message',1);
2)
function messagesTester(funcPath) {
var args=funcPath.split('.'),root=window.messages;
for(var i=0;i<args.length;i++)root=root[args[i]];
return root;
}
// var text = messagesTester('my.message')( /*arguments list*/ );
var text = messagesTester('my.message')(1);