Xrm.Navigation.openForm set lookup field automatically from the previous form - javascript

I added a new button inside my "Parcours" form that takes me to another form "Question" with a Xrm.Navigation.openForm
The "Question" form has a lookup field from "Parcours" that I want to populate set automatically from the previous form like this for example :
This is my code for my button, I want to detect the ID of the parcours automatically and set it in the new "Question" form
function NavigateQuestion(){
var entityFormOptions = {};
entityFormOptions["entityName"] = "zs_question";
var formParameters = {};
// Set lookup column
formParameters["zs_parcours"] = ""; // I want to put the ID here from the previous form.
formParameters["zs_parcoursname"] = ""; // Name of the parcours.
formParameters["zs_parcourstype"] = "zs_parcours"; // Table name.
Xrm.Navigation.openForm(entityFormOptions, formParameters).then(
function (success) {
console.log(success);
console.log(formParameters);
},
function (error) {
console.log(error);
});
}

You should be able to get the values and assign from the current record form itself.
Make sure to pass the formContext for latest client API methods (I put the syntax in commented code section).
// Set lookup column
formParameters["zs_parcours"] = Xrm.Page.data.entity.getId(); // or by using form context -- formContext.data.entity.getId();
formParameters["zs_parcoursname"] = Xrm.Page.getAttribute("zs_name").getValue(); // or by using form context -- formContext.getAttribute("zs_name").getValue();
formParameters["zs_parcourstype"] = "zs_parcours";

Related

How can I pass a server-side variable to HTML and have the HTML page return an auto-filled form?

I am building an HTML page in Google Apps Script with CRUD functionality. The user can currently add data, edit data, and search data (I am not adding a delete feature). I would like the user to receive the form url link with an ID that when they go BACK to that link, it auto-fills the form with the previously added data.
In my HTML file, I have the following button defined:
document.getElementById("sbtn").addEventListener("click",getTID);
Once a user has entered data, it gets sent to a Google Sheet. The user HAS to enter a unique ID that they've already been provided. Using this ID, they can enter it, hit search, and it runs getTID():
function getTID() { //TID CODE
var transID = document.getElementById("tid").value;
if (transID.length === 36) {
google.script.run.withSuccessHandler(updateAllData).getID(transID);
} else {
alert("Transaction ID is not long enough.\nPlease copy the Transaction ID EXACTLY!\n\nFor Example: https:/workwebsiteconcealedforprivacy/w?txid=36275284-2ed6-4868-97b2-16bc1fde1a08\n\nThe Transaction ID is: 36275284-2ed6-4868-97b2-16bc1fde1a08")
}
}
This takes the ID they gave, references the spreadsheet and then returns values it found by index. Now, I have in my server-side GS file, the following in doGet:
var urlValue = '';
function doGet(e) {
// Test Code
var ss = SpreadsheetApp.openById(id);
var ws = ss.getSheetByName("Options");
var list = ws.getRange(1, 1, ws.getRange("A1").getDataRegion().getLastRow(), 1).getValues();
var htmlListArray = list.map(function (r) { return '<option>' + r[0] + '</option>'; }).join('');
var title = "Please Work";
var vals = JSON.stringify(e);
if ('v' in e.parameter){
urlValue = String(e.parameter['v']);
//return HtmlService.createHtmlOutput(urlValue);
}
return render("page",{list: htmlListArray, title});
and the following:
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function render(file, argsObject) {
var tmp = HtmlService.createTemplateFromFile(file);
if (argsObject) {
var keys = Object.keys(argsObject);
keys.forEach(function (key) {
tmp[key] = argsObject[key]
});
}
return tmp.evaluate();
}
If I uncomment the return HtmlService.createHtmlOutput(urlValue); line, I can see that IF an ID is in the URL, it returns the correct parameter.
My problem is that I cannot get the HTML to read the urlValue variable and autorun getTID() when the user enters the url with the correct parameter. The correct functionality is that IF the parameter is found, it auto populates the HTML form. If it doesn't, it returns the blank form.
There is an error on
return render("page",{list: htmlListArray, title});
On {list: htmlListArray, title} the name of the second property is missing.
To "read the urlValue variable" there are two options:
pass the parameters from the URL using the event object of the doGet function. For this you have two options, create the HtmlService.HtmlOutput object from an html string generated using "vanilla" JavaScript or create it from a HtmlService.HtmlTemplate object.
get the parameters from the URL directly on the client-side code using google.script.url.getLocation .
If you go for the first option, then you should pass someway the urlValue to the render function. In the question code urlValue is a global variable, so you might add the following before the render's return statement.
tmp.urlValue = urlValue;
Then you have to add a scriptlet on the html file to handle this value and "autorun" getTID. Scriptlets is a feature of Templated HTML.

form.getRecord() is undefind. Extjs

In my application I use the Ext.form.Panel component. In the panel set the selected entry from the tree using loadRecord(record)
...
showDataFields: function(view, record, item, index, event) {
var panel = view.up('tabpanel');
var formfield = panel.down('form');
formfield.loadRecord(record);
},
....
I get an entry to send data to the store
...
saveUserData: function(button, e) {
var values;
var panel = button.up('actionform'),
form = panel.down('form');
record = form.getRecord();
console.log(record)// value undefined. Why ?
},
...
How to get a record?
Link to my fiddle app the form is in the file app/view/ActionFormTree.js
The showDataFields and saveUserData methods are in app/controller/MainController.js
thank
As you are getting your form component using button.up('actionform') and then after again you are using panel.down('form'). So in this case you getting inner form of form component. See below screenshot for better understanding (see selected dom element)
So you don't need to use down selector because you already getting form component using button.up('actionform').
Code Snippet
saveUserData: function(button, e) {
var values, form = button.up('actionform'),
record = form.getRecord();
console.log(record)
},
You can check here with working fiddle.
If I understand your design correctly, what I think is the problem is how you reference your form.
saveUserData: function(button, e) {
var values;
var panel = button.up('tabpanel'),
form = panel.down('form');
// Or, because button is on your form:
// var form = button.up('form');
record = form.getRecord();
console.log(record)// value undefined. Why ?
//store = this.getUserTreeStoreStore('UserTreeStore');
//values = form.getValues();
//id = form.getRecord().get('id');
},
better is to assign the itemId property to your form panel and use getValues() function
var formpanel = Ext.ComponentQuery.query('form[itemId=your_form_itemId_here]')[0];
var form = formpanel.getForm();//this gets all the fields inside the form
//no to fetch the record or form field values
var record = form.getValues();
this is Convenient function for fetching the current values of each field in the form. ...

How do I submit an array from the client to a server (ExpressJs) using AJAX?

I'm working on a web application in which a user can drag and drop div elements, whose content is generated from a database, into their preferred order. I want the user to be able to, when they are finished arranging the divs, submit their list (in order) to the server and store the new order in a table. I'm using AJAX to do this, however I'm not sure if that is necessary/the proper way to do this, since I don't need to asynchronously update the page (I just need to submit the data). I've tried a variety of methods to get my array to the server in a usable format (it needs to be iterable and allow for element locations to be compared). I have tried using JSON.stringify/parse, creating a custom object, simply submitting the array on its own, and so on. Here is my code, the most relevant bits are towards the bottom by the makeRouteArray function. Ideally to accomplish this I would like to use just JavaScript (no jQuery). Finally, please excuse my messy code, I'm learning.
// get two groups of elements, those that are draggable and those that are drop targets
let draggable = document.querySelectorAll('[draggable]');
let targets = document.querySelectorAll('[data-drop-target]');
// div immediately surrounding bus routes
var busList = document.getElementById("bus-list");
const button = document.getElementById("button");
// store the id of the draggable element when drag starts
function handleDragStart(e) {
e.dataTransfer.setData("text", this.id); // sets 'text' value to equal the id of this
this.classList.add("drag-start"); // class for styling the element being dragged, sets opacity
}
function handleDragEnd(e) {
e.target.classList.remove('drag-start');
}
function handleDragEnterLeave(e) {
// should provide visual feedback to user?
}
// handles dragover event (moving your source div over the target div element)
// If drop event occurs, the function retrieves the draggable element’s id from the DataTransfer object
function handleOverDrop(e) {
e.preventDefault();
var draggedId = e.dataTransfer.getData("text"); // retrieves drag data (DOMString) for specified type
var draggedEl = document.getElementById(draggedId);
draggedEl.parentNode.insertBefore(draggedEl, this); // inserts element being dragged into list
var draggedArray = Array.from(draggedEl.parentNode.children); // creates new array which updates current location of each route
e.target.classList.remove('drag-start'); // sets opacity back to 1
// if (e.type === "drop") {
// // when dropped, update localstorage
savePage(draggedArray);
// }
}
// get each full bus-route div in #bus-list with p content as single arr item each
// called when item is dropped
function savePage(dArray) {
// honestly i can't remember what this does precisely
// but i can't seem to add to localstorage in the way i want without it
var arr = Array.prototype.map.call(dArray, function(elem) {
return elem.outerHTML;
});
localStorage.newList = JSON.stringify(arr); // have to stringify the array in order to add it to localstorage
}
// ideally it should just update the order of the bus routes to reflect local storage
// and add classes/ids to the divs etc. (hence using outerHTML)
function makePage() {
// getting the item from localstorage
var getData = localStorage.getItem("newList");
// parsing it back into an array
var parsedData = JSON.parse(getData);
// string to hold contents of array so they can be display via innerHTML
var fullList = "";
if (localStorage.getItem("newList") === null) {
return; // maybe this is wrong but if there's nothing in local storage, don't do anything
} else {
for (let i = 0; i < parsedData.length; i++) {
fullList = fullList + parsedData[i];
}
busList.innerHTML = fullList;
// reassigning targets after calling function in order to re-register event handlers
draggable = document.querySelectorAll('[draggable]');
targets = document.querySelectorAll('[data-drop-target]');
}
}
// probably better way to do this
for (let i = 0; i < draggable.length; i++) {
draggable[i].addEventListener("dragstart", handleDragStart);
draggable[i].addEventListener("dragend", handleDragEnd);
}
// drop target elements
for (let i = 0; i < targets.length; i++) {
targets[i].addEventListener("dragover", handleOverDrop);
targets[i].addEventListener("drop", handleOverDrop);
targets[i].addEventListener("dragenter", handleDragEnterLeave);
targets[i].addEventListener("dragleave", handleDragEnterLeave);
}
// rolling average: new_average_score = old_average_score * (total_users-1)/total_users + user_route_rank/total_users
// user id, column per route (score)
// session id
// submit button to save changes to db
// when submit is clicked
// get current results from either local storage or from currently ordered + displayed list
// data will be in array format
// do i need AJAX to be able to submit something other than form data to a server?
// is submitting data in this format (taken from page's HTML ordering) even possible?
// i'd prefer to not use jQuery
// how do i submit an array to the server (express) and then parse (?) it to make it useable?
// so far i have tried JSON.stringify/parse, making a custom object, just submitting array, etc.
// ultimately what i need is a data type that i can loop over and compare positions of stored elements
var makeRouteArray = function() {
var currentOrderArr = Array.from(busList.children);
var idData = currentOrderArr.map(function(el) {
return Number(el.id.slice(2));
});
return idData; // not sure if having two return statements like this is okay
};
button.addEventListener("click", function(e) {
var request = new XMLHttpRequest();
request.open('POST', '/submit', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.send(data);
});
makePage();
The makeRouteArray function is taking ids from an HTML ul and stripping them of the first two characters because I only want to use the numbers (the ids are structured as #r-num). An array of these numbers is what I would like to submit to the server and if possible, once on the server, return the JSON object to an array.
Thanks!

Local storage saving multiples of the same items

I'm quite new to using storage settings in HTML/JavaScript. I'm building a hybrid app which is a not taking app on mobile using Phonegap. I want the user to type in a note name, then the note itself, and be able to save both by placing them into a jquery mobile list and putting them back on the home screen. My problem is that I can only save one note at a time. If I try to save another one, it just overwrites the previous one. How would I go about fixing it? Also, when I try refresh the browser the note disappears. Is this normal?
Please and thank you.
Here is the saving function I used:
function storeData() {
var i;
for (i=0; i<999; i++) {
var fname = document.getElementById('fname').value;
var wtf = document.getElementById('wtf').value;
localStorage.setItem('fname', fname);
localStorage.setItem('wtf', wtf);
}
var newEl = "<li><a href='#' id='savedNote'onclick='loadData'></a></li>"
document.getElementById("savedNote").innerHTML = localStorage.getItem("fname");
//try to create a new list element in main menu for this item being stored in
// and add an onclick load function for that
};
function loadData() {
var x;
for (x=0; x<999; x++) {
document.getElementById("fname").innerHTML = localStorage.getItem('fname', fnamei);
document.getElementById("wtf").innerHTML = localStorage.getItem('wtf', wtfi);
}
};
I'm not sure why you're using a loop for your functions. The storeData function looks 999 times for the value of #fname and #wtf, and save this 999x times in localStorage.fname and localStorage.wtf
This makes absolut no sense. Same Problem with your loadData function.
A nice way to save more then one string to the localStorage, is to create a javascript object, stringify it and then save it to the localStorage.
You only need to load the data from the localStorage, if you (re)load the page. But you need to save it to the localStorage, every time something changed, to be sure that the data in the localStorage is always up to date.
For display and manipulation on the page, you use the javascript object. in my example "myData". If you change something, you update your javascript object and then save it to the localStorage.
a side note. to be sure that the user don't overwrite something with a
identical name, you should use unique ids. like i did with the timestamp.
var postID = new Date().getTime();
Here a little example to show you a possible way. It's hard to code something functionally without your html code.
// Creating a object for all Data
var myData = {};
// Fill the Object with data if there is something at the LocalStorage
if (localStorage.myData){
loadDataFromLocalStorage();
}
function createNewPost(){
// Create a ID for the Post
var postID = new Date().getTime();
// Create a Object inside the main object, for the new Post
myData[postID] = {};
// Fill the Object with the data
myData[postID].fname = document.getElementById('fname').value;
myData[postID].wtf = document.getElementById('wtf').value;
// Save it to the LocalStorage
saveDataToLocalStorage();
// Display the Listitem. with the right postID
}
function loadPost (postID){
var singlePost = myData[postID];
// Display it
}
// A Helper Function that turns the myData Object into a String and save it to the Localstorage
function saveDataToLocalStorage(){
localStorage.myData = JSON.stringify(myData);
}
// A Helper Function that turns the string from the LocalStorage into a javascript object
function loadDataFromLocalStorage(){
myData = JSON.parse(localStorage.myData);
}
// Creating a object for all Data
var myData = {};
// Fill the Object with data if there is something at the LocalStorage
if (localStorage.myData){
loadDataFromLocalStorage();
}
function createNewPost(){
// Create a ID for the Post
var postID = new Date().getTime();
// Create a Object inside the main object, for the new Post
myData[postID] = {};
// Fill the Object with the data
myData[postID].fname = document.getElementById('fname').value;
myData[postID].wtf = document.getElementById('wtf').value;
// Save it to the LocalStorage
saveDataToLocalStorage();
// Display the Listitem. with the right postID
}
function loadPost (postID){
var singlePost = myData[postID];
// Display it
}
// A Helper Function that turns the myData Object into a String and save it to the Localstorage
function saveDataToLocalStorage(){
localStorage.myData = JSON.stringify(myData);
}
// A Helper Function that turns the string from the LocalStorage into a javascript object
function loadDataFromLocalStorage(){
myData = JSON.parse(localStorage.myData);
}
Store an array.
var arrayX = [];
arrayX.push(valueY);
localStorage.setItem('localSaveArray', arrayX);

getting responseID of latest form submission in google apps

I have a google form. Every time it is submitted, the answers go into a google spreadsheet. I am trying to write an app script that triggers every time the form is submitted, and adds an "edit" link in the column to the right of the data from the form. The link itself is easy to generate, google has a method called getEditResponseURL(). (https://developers.google.com/apps-script/reference/forms/form-response)
But everytime I run it, I am getting the error "TypeError: Cannot call method "getResponses" of null."
Here is my code:
function addeditlink(e) {
// Get the active sheet
var sheet = SpreadsheetApp.getActiveSheet();
// Get the active row
var row = sheet.getActiveCell().getRowIndex();
//get the form
var form = FormApp.getActiveForm();
//get latest form response
var responses = form.getResponses();
var lastResponse = responses[responses.length - 1];
//get edit URL
var editurl = lastResponse.getEditResponseUrl();
//build link
var editlink = "Edit";
//place edit link in column R (index 18)
if (sheet.getRange(row, 18).getValue() == "") {
sheet.getRange(row, 18).setValue(editlink);
}
}
Any help? Thanks!
The solution is to remove:
var form = FormApp.getActiveForm(); //this is WRONG
and replace with:
var form = FormApp.openByID(' ID here ')
There is no "active form", because this script is being run in sheets, not forms.
I think you can only call FormApp.getActiveForm() from a script attached to a form, whereas your script is contained in a GSheet. I couldn't find a way to easily gets forms that used this sheet as its destination so what I've did was get all of the forms and then looked at the destination id of each and checked if it is the same as this spreadsheet. Once you've got your Form object you can get the responses. Feels a bit long winded would love to know if anyone knows a quicker way.
There are also a few exceptions that FormApp throws that you have to cope with.
Here's the function I use:
/**
* Find the first form that is linked to a specific spreadsheet
*
* #param {string} spreadsheet id
* #return {object} Form or null
*/
function getFormByDestinationId_(spreadsheetId) {
var formFiles = DriveApp.getFilesByType('application/vnd.google-apps.form');
var form;
var formFile;
var formId;
var destinationId;
while (formFiles.hasNext()) {
formFile = formFiles.next();
formId = formFile.getId();
// Throws an error if ID invalid
try {
form = FormApp.openById(formId);
} catch (error) {
if (error.name === "Exception") {
// Just ignore it
} else {
throw error;
}
}
// Form.getDestinationId() throws an error if there is no destination id
try {
destinationId = form.getDestinationId();
} catch (error) {
if (error.name === "Exception") {
// Just ignore it
} else {
throw error;
}
}
if (destinationId !== spreadsheetId) {
continue;
}
return form;
}
return null;
} // getFormByDestinationId_()
The only line using: getResponses() method is this one:
var responses = form.getResponses();
Your error:
Cannot call method "getResponses" of null
Means that form is null. If form is null, then this line:
//get the form
var form = FormApp.getActiveForm();
is not working. So, why isn't it working? There is nothing wrong with the code, so it must be a different problem. If there was an active form, that code would return a form type. This means that there is no form bound to the script. getActiveForm()
Returns the form to which the script is container-bound.
Your script is not "container-bound" to the form. Your script is bound to the spreadsheet.
The documentation states:
To interact with forms to which the script is not container-bound, use openById(id) or openByUrl(url) instead.
You can bind your script to the form by opening the script editor from the edit page of the form. But, there's no need to do that if you want to keep your script bound to the spreadsheet.
The line var form = FormApp.getActiveForm(); isn't going to work in your spreadsheet script.
The problem with using the Event Object e with an installable trigger, is that it looks like you can't get the response URL.
google_sheets_events
This means that you need to use openById(id) or openByUrl(url) inside the script bound to the spreadsheet, or move all your script to the form.
Here is how to get the edit url from script in the spreadsheet:
// Open a form by ID.
var form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');
Now the problem is, that you can only get the Edit Response URL: getEditResponseUrl() through the "FormResponse" class. So you need the Form Responses.
var formResponses = form.getResponses();
But that's all the responses, you need the last one.
var lastResponseIndex = formResponses.length - 1;
var lastResponse = formResponses[lastResponseIndex];
var editURL = lastResponse.getEditResponseUrl();
or:
function getEditURLofLastResponse() {
// Open a form by ID.
var form = FormApp.openById('Your Form ID');
var formResponses = form.getResponses();
//get last respnse
var lastResponseIndex = formResponses.length - 1;
var lastResponse = formResponses[lastResponseIndex];
var editURL = lastResponse.getEditResponseUrl();
Logger.log(editURL);
}
Just an observation:
You are using an e argument: function addeditlink(e) {. But I don't see it being used in your code. That makes me wonder if you are using an "installable" trigger, as opposed to a "simple" trigger.
It's possible to get the values that were just submitted with e.values or e.namedValues. But you can't get the Edit URL with the Event Object.

Categories