Reactively counting the number of form inputs completed in Meteor.js - forms

I've been struggling with this implementation so I figure I'd give the good ol' community here at Stack Overflow a shot at solving my problem.
So i got a standard personal information form. The form id is "personal-form".
I have a helper method called {{numFormsCompleted}} from the template.
Here is my implementation of the method --
numFormsCompleted : function(){
var count = 0;
var form = document.getElementById("quote-form");
console.log(form);
if(form == null)
return 0;
else{
for(i = 0; i < form.length; i++){
value = form.elements[i].value;
if(value != "" || value != null)
count++;
}
console.log(count);
return count;
}
}
The form is showing up as null. Anyone know why?

Your template may or may not have been rendered when your code runs, therefore instead of providing it as a helper of your template, you can run your function at your template's rendered callback, and then assign the return value to a reactive session variable.
Template.myFormTemplate.rendered = function() {
Session.setDefault("formElementCount", 0);
var count = 0;
var form = this.find("#quote-form");
console.log(form);
if(!!form){
for(i = 0; i < form.length; i++){
value = form.elements[i].value;
if(value != "" || value != null)
count++;
}
console.log(count);
Session.set("formElementCount", count);
}
}
and then as your helper in your template
Template.myFormTemplate.numFormsCompleted = function() {
var count = Session.get("formElementCount");
return count;
}
This way, whenever your form template gets rendered, it will reactively give you the element count in your helper and as an added bonus, you can use it from within other templates as well.

Related

Can we perform "Not null or return" in flutter with null safety feature?

I'm trying to migrate null safety feature into my Flutter project because it's good for my future of project. Since I already have experience of developing Android app via Kotlin, I already got used to this null safety feature. But it seems I can't perform one thing which can be done in Kotlin.
user?.name ?? "Unknown"
I noticed that in Dart, I can set default value with this code line.
Now I'm trying to perform this
fun performOrReturn(users: Map<String, User>, id: String) {
val user = users[id] ?: return //This line
println("User info gotten : ${user.name}")
}
In Kotlin, I can perform this thing. I can make method return itself if specific variable is null, so Kotlin can handle null safety flexibly.
So here is what I'm trying to do, there is table in my code. I want to check if there is data existing in specified column and row in this field.
Map<int, List<MyData>> table = Map();
...
int _findDataExisting(int row, int col) {
List<MyData> data = table[row]; //This is causing problem because table[row] can be null
//Alternative solution is
//List<MyData> data = table[row] ?? List.empty();
//
//if(data.isEmpty) {
// return -1;
//}
//
for(int i = 0; i < data.length; i++) {
if(data[i].column == col) {
return i;
}
}
return -1;
}
I want to make it return -1 rather than initializing default value because it's not good for handling memory, and code becomes inefficient.
List<MyData> data = map[row] ?? return -1;
I tried line below just like Kotlin, but Dart seems that it can't handle this.
Is there any equivalent thing in Dart? Making it return method if specific variable is null.
Don't be fancy.
int _findDataExisting(int row, int col) {
if (table[row] != null) {
List<MyData> data = table[row]!;
for(int i = 0; i < data.length; i++) {
if(data[i].column == col) {
return i;
}
}
}
return -1;
}
You could initially declare data as nullable and let the null check automatically promote it to a non-nullable type, avoiding the need to use null assertions (!) later:
int _findDataExisting(int row, int col) {
List<MyData>? data = table[row];
if (data == null) return -1;
for (int i = 0; i < data.length; i++) {
if (data[i].column == col) {
return i;
}
}
return -1;
}

Find and Replace Header/Footer text Office JS

I'm having trouble with office js and processing a list of items with lookup codes and replacement values for the header and footer. I've got the body working just not the header/footer. I'm getting this error:0x800a139e - JavaScript runtime error: The property 'items' is not available. Before reading the property's value, call the load method on the containing object and call "context.sync()" on the associated request context. As you can see I do call load and sync before trying to access the results.
function mergeHeader(documentFieldKeys) {
if (documentFieldKeys.length > 0)
Word.run(function(context) {
var key = documentFieldKeys.shift();
var mySections = context.document.sections;
context.load(mySections, 'body/style');
return context.sync().then(function() {
for (var i = 0; i < mySections.items.length; i++ ) {
findAndReplace(key, context, mySections.items[i].getHeader("primary"));
}
return context.sync().then(function() {
return mergeHeader(documentFieldKeys);
})
.then(context.sync);
});
});
}
function findAndReplace(key, context, body) {
var results = body.search(key.Code, { matchWholeWord: false, matchCase: false });
context.load(results);
return context.sync().then(function() {
if (results.items.length > 0 && key.Value === "") {
missingFields.push(key.Description);
} else {
for (var i = 0; i < results.items.length; i++) {
results.items[i].insertText(key.Value, "replace");
}
}
})
.then(context.sync);
}
Any help would be appreciated.
Add
context.load(mySections, 'items');
or
mySections.load('items');

How to check if text is found in column in Protractor

I'm trying to assert that a name is displayed in a column of a table. I've written an inResults function that will iterate through a column's text to see if a name exists. Here's what I'm trying:
Page object:
this.names = element.all(by.repeater('row in rows').column('{{row}}'));
this.inResults = function(nameString) {
var foundit = '';
this.names.each(function(name) {
name.getText().then(function(it) {
console.log(it); // each name IS printed...
if(it == nameString) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
});
});
return foundit; // returns '' but should be true?
};
Spec expect:
expect(friendPage.inResults('Jo')).toBeTruthy();
Both console statements print as expected... but my expect fails as foundit's value is still ''. I've tried this a number of ways and none are working. What am I missing?
I've devised what I think is a better/cleaner way to solve this. It's less complex and doesn't require locator/css code in the method.
friend.page.js
// locator
this.friendName = function(text) { return element.all(by.cssContainingText('td.ng-binding', text)) };
// method
this.inResults = function(name) {
return this.friendName(name).then(function(found) {
return found.length > 0;
});
};
friend.spec.js
expect(friendPage.inResults('Jo')).toBeTruthy();
I've added this to my protractor_example project on GitHub...
I would recommend you to use filter: http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.filter
this.inResults = function(nameString) {
return this.names.filter(function(name) {
return name.getText().then(function(text) {
return text === nameString;
});
}).then(function(filteredElements) {
// Only the elements that passed the filter will be here. This is an array.
return filteredElements.length > 0;
});
});
// This will be a promise that resolves to a boolean.
expect(friendPage.inResults('Jo')).toBe(true);
Use map to do this.This will return a deferred that will resolve with the values in an array, so if you have this:
this.mappedVals =element.all(by.repeater('row in rows').column('{{row}}')).map(function (elm) {
return elm.getText();
});
It will resolve like this:
this.inResults = function(nameString) {
var foundit = '';
mappedVals.then(function (textArr) {
// textArr will be an actual JS array of the text from each node in your repeater
for(var i=0; i<textArr.length; i++){
if(it == textArr[i]) {
console.log('it\'s TRUE!!!!'); // this gets printed...
foundit = true;
}
}
return foundit;
});
}
And Use that in Spec file like,
friendPage.inResults('Jo').then(function(findIt){
expect(findIt).toBeTruthy();
});

Google Spreadsheet - How to avoid sending email duplicates?

I am having an issue with a script. I used the following script from Google Developers Website in order to do a simple merge mail. See https://developers.google.com/apps-script/articles/mail_merge
I modified a bit the script so to prevent email duplicates. However, even if the script seems to work as it marks 'EMAIL_SENT' in each row every time an email is sent. It does not pay attention if the mail as already been marked and still send the mail.
I believe there is an error at line 16 "var emailSent = rowData[6];"
I would really appreciate if someone could help me. Whoever you are thanks in advance.
Here is the modified script :
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheets()[0];
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 7);
var templateSheet = ss.getSheets()[1];
var emailTemplate = templateSheet.getRange("A2").getValue();
var objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var Resume = DriveApp.getFilesByName('Resume.pdf') var Portfolio = DriveApp.getFilesByName('Portfolio.pdf') var rowData = objects[i];
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "Architectural Internship";
var emailSent = rowData[6];
if (emailSent != EMAIL_SENT) {
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {
attachments: [Resume.next(), Portfolio.next()]
});
dataSheet.getRange(2 + i, 7).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
function fillInTemplateFromObject(template, data) {
var email = template;
var templateVars = template.match(/\${\"[^\"]+\"}/g);
for (var i = 0; i < templateVars.length; ++i) {
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
}
return email;
}
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue;
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty. // Arguments: // - cellData: string function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise. function isAlnum(char) { return char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || isDigit(char); }
// Returns true if the character char is a digit, false otherwise. function isDigit(char) { return char >= '0' && char <= '9'; }
Your code is really hard to read and the functions that return 2 or more objects make it even harder...you are using variable names that are also a bit confusing.... but that is probably a personal pov :-)
Anyway, I think I've found the issue: when you write var rowData = objects[i];
This "object" is actually the result of the getRowData function but if you look at this function, you'll see that it returns 2 objects, the first one being itself the result of another function (getObjects) ...
You are checking the value is the 6th element of the array which is actually an object and compare it to a string. The equality will never be true.
I didn't go further in the analyse since I found it really confusing ( as I already said) but at least you have a first element to check .
I would suggest you rewrite this code in a more simple way and use more appropriate variable names to help you while debugging.
I would recommend logging both values before executing to make sure they are the same. I would also guess that the email_sent and EMAIL_SENT are different data types. Can also try forcing the value to string for comparison.
To clarify:
logger.Log(emailSent);
logger.Log(EMAIL_SENT);
if (emailSent.toString() != EMAIL_SENT.toString())
{...
Error is in this line of code -
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
It's considering only 2 columns in the range. Changed 2 to 3 and it worked fine.

execCommand without a selection? (set font, size etc)

I can get my custom WYSIWYG editor to apply styling to selected text no problem:
pnlDocumentEditor_IFrame.document.execCommand(cmd, ui, opt)
.. but what I need to be able to do is allow the user to set a font, or font size, or bold etc so that text typed AFTER this command is issued will have that style applied.
Is this possible? All execCommands I've tried seem to only work on selected text.
Appling execCommand on certain element, WITHOUT selecting it with mouse, can be done with this function:
jsFiddle example
function execCommandOnElement(el, commandName, value) {
if (typeof value == "undefined") {
value = null;
}
if (typeof window.getSelection != "undefined") {
// Non-IE case
var sel = window.getSelection();
// Save the current selection
var savedRanges = [];
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
savedRanges[i] = sel.getRangeAt(i).cloneRange();
}
// Temporarily enable designMode so that
// document.execCommand() will work
document.designMode = "on";
// Select the element's content
sel = window.getSelection();
var range = document.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
// Execute the command
document.execCommand(commandName, false, value);
// Disable designMode
document.designMode = "off";
// Restore the previous selection
sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedRanges.length; i < len; ++i) {
sel.addRange(savedRanges[i]);
}
} else if (typeof document.body.createTextRange != "undefined") {
// IE case
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.execCommand(commandName, false, value);
}
}
And example usage:
var testDiv = document.getElementById("test");
execCommandOnElement(testDiv, "Bold");
execCommandOnElement(testDiv, "ForeColor", "red");
Source: set execcommand just for a div