KnockoutJS: issues validating multiple rows with dropdownlists - javascript

So I'm trying validate the two dropdowns that I have on button click. When the dropdowns first initiate, they are initiated with a value of undefined because nothing has been selected. This is what I want. But when I click the add button to validate the dropdowns before I add another row, the validation never occurs.
JS
ko.validation.init({
insertMessages: false,
errorMessageClass: "ErrorLine"
});
ko.validation.rules['bothUndefined'] = {
getValue: function (o) {
return (typeof o === 'function' ? o() : o);
},
validator: function (val, params) {
var self = this;
var anyOne = ko.utils.arrayForEach(params, function (param) {
if (typeof param === "function") {
return typeof param() === "undefined";
} else {
return (typeof param === "undefined");
}
});
return (typeof anyOne !== "undefined");
},
message: 'Please select one change.'
};
ko.validation.registerExtenders();
function BookPossessionTransferVM() {
var self = this;
.
.
.
self.PossessionChanges = ko.observableArray([]);
self.PossessionChangesErrors = ko.validation.group(self.PossessionChanges(), { deep: true, live: true });
self.PossessionChanges.push(new PossessionChangeVM(self.PossessionChanges().length +1))
.
.
.
self.addPossessionChange = function () {
if (self.PossessionChanges().length < 1) {
self.PossessionChanges.push(new PossessionChangesVM(self.PossessionChanges().length + 1,
self.AllFrom()));
} else {
self.PossessionChangesErrors.showAllMessages();
}
}
}
function PossessionChangeVM(possessionChangeId) {
var self = this;
self.possessionChangeId = ko.observable(possessionChangeId);
self.SelectedFrom = ko.validatedObservable();
self.SelectedTo = ko.validatedObservable();
self.IsValidRow = ko.pureComputed(function() {
return typeof self.SelectedFrom !== "undefined" && typeof self.SelectedTo !== "undefined";
}).extend({
bothUndefined: {
params: [self.SelectedFrom, self.SelectedTo]
}
});
self.ChangeType = ko.pureComputed(function() {
if (self.SelectedFrom() !== undefined && self.SelectedTo() !== undefined) {
return 'Update';
} else if (self.SelectedFrom() === undefined && self.SelectedTo() === undefined) {
return '';
} else if (self.SelectedFrom() === undefined) {
return 'Add';
} else if (self.SelectedTo() === undefined) {
return 'Remove';
} else { return ''; }
});
}
After I click the add button, I am expecting PossessionChangesErrors to have one issue since it should load with undefined options. But I am getting nothing. Thanks again for helping!
EDIT:
Now I have the validation working, but I am not able to clear the validation to create a new row once one of the drop downs have been selected. Here is the fiddle: https://jsfiddle.net/p6x1nqm5/18/

Turns out I just didn't have the correct logic for the validation. Here is the new fiddle with the changes. https://jsfiddle.net/zw80kh2n/2/
self.IsValidRow became
self.IsValidRow = ko.pureComputed(function() {
return (!(self.SelectedFrom() === undefined && self.SelectedTo() === undefined))
}).extend({ bothUndefined: {}
});
and the validation became
ko.validation.rules['bothUndefined'] = {
getValue: function (o) {
return (typeof o === 'function' ? o() : o);
},
validator: function (val) {
return val;
},
message: 'Please select one change.'
};
ko.validation.registerExtenders();

Related

Javascript to get ComboBox values works in IE11 but not in Edge

I have a set of javascript functions that are working correctly in IE11 but not in Edge and I am not certain what I need to amend to sort them out. I am calling them using the Id of the select box
function getComboValue (combo)
{
if (typeof (combo) == "string")
combo = getComboById (combo);
// If no combo can be found return undefined
if (!combo)
return;
if (isDHTMLCombo (combo))
{
var sVal = combo.getActualValue ();
if ((sVal != null) && (sVal != 'null'))
return sVal;
}
else
{
var selIdx = combo.selectedIndex;
if (selIdx > -1)
{
var selValue = combo.options[selIdx].value;
return selValue;
}
}
return '';
}
function getComboById (id)
{
var combo = document.getElementById (id);
if (combo && combo.tagName == "INPUT")
{
// DHTML combo is returned if the id is of a DHTML combo box.
var par = combo.parentNode;
if (par)
{
// Get the parent object
combo = par.combo;
}
}
return combo;
}
function isDHTMLCombo (combo)
{
if (typeof (combo) == "string")
combo = getComboById (combo);
if (!combo)
return false;
if (combo.DOMelem_input != undefined)
{
// Seems to be DHTML
return true;
}
if (combo.getAttribute ("igIsDHTML") != null)
{
// Seems to be DHTML
return true;
}
return false;
}
How can I adapt these so that will return the correct information in Edge - getComboValue is returning "" at the moment

Error while executing async get of Outlook item property

Why am I getting Could not complete the operation due to error 80043200 sometimes while getting Outlook item property (for example, Subject) in email Compose mode?
Call stack:
Error
at n.prototype.execute (appsforoffice.microsoft.com/lib/1.1/hosted/outlook-win32-16.02.js:11:86635)
at u.DDA.OutlookAppOm.prototype.callOutlookDispatcher (appsforoffice.microsoft.com/lib/1.1/hosted/outlook-win32-16.02.js:11:150300)
at u.DDA.OutlookAppOm.prototype.invokeHostMethod (appsforoffice.microsoft.com/lib/1.1/hosted/outlook-win32-16.02.js:11:150169)
at u.DDA.OutlookAppOm.prototype._standardInvokeHostMethod$i$0 (appsforoffice.microsoft.com/lib/1.1/hosted/outlook-win32-16.02.js:11:148443)
at r.ComposeSubject.prototype.getAsync (appsforoffice.microsoft.com/lib/1.1/hosted/outlook-win32-16.02.js:11:190544)
We use following code for getting a field from an Outlook item:
sfMailApp.OfficeManager.prototype._reloadField = function (name, defaultValue) {
var deferred = $.Deferred();
defaultValue = defaultValue || '';
var item = this.getItem();
if (item.isFake) {
deferred.resolve('');
return deferred.promise();
}
var field = item[name];
if (field && this._isComposeMode && field.getAsync) {
var _this = this;
try {
field.getAsync(function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
_this[name] = _this._processOfficeData(result.value) || defaultValue;
deferred.resolve(_this[name]);
}
else {
_this._onError(result);
deferred.reject();
}
});
} catch (e) {
deferred.reject();
}
} else {
this[name] = this._processOfficeData(field) || defaultValue;
deferred.resolve(this[name]);
}
return deferred.promise();}
Code for getItem function:
sfMailApp.OfficeManager.prototype.getItem = function () {
var item = Office && Office.context && Office.context.mailbox && Office.context.mailbox.item ? Office.context.mailbox.item : null;
if (!item) {
item = {
isFake: true
}
}
return item;}
One more error we're getting sometimes while trying to get user identity token:
{"name":"AccessRestricted","message":"Internal protocol error: -2147467259'.","code":9017 }
How should we handle that?

How can I create empty JSON keys (nested or not) using a string?

I currently have this code built in JS, but it's really, really ugly.
Is there any better way to approach it?
The way it works basically is pushing a string like app.chat.test to be the key, and value like teststr.
I test the lengths to see if the "parent" key is there, otherwise we build it.
function constructJson(jsonKey, jsonValue){
//REWRITE!!!!!!!!
let jsonObj = langFile;
let jsonKeyArr = jsonKey.split('.')
if (jsonKeyArr.length === 1) {
if (valToAdd === undefined) {
if (jsonObj[jsonKey] === undefined) {
jsonObj[jsonKey] = {}
}
} else {
if (jsonObj[jsonKey] === undefined) {
jsonObj[jsonKey] = valToAdd
}
}
} else if (jsonKeyArr.length === 2) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = jsonValue
}
} else if (jsonKeyArr.length === 3) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = jsonValue
}
} else if (jsonKeyArr.length === 4) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] = jsonValue
}
} else if (jsonKeyArr.length === 5) {
if (jsonObj[jsonKeyArr[0]] === undefined) {
jsonObj[jsonKeyArr[0]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]] = {}
}
if (jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]][jsonKeyArr[4]] === undefined) {
jsonObj[jsonKeyArr[0]][jsonKeyArr[1]][jsonKeyArr[2]][jsonKeyArr[3]][jsonKeyArr[4]] = jsonValue
}
} else if (jsonKeyArr.length > 5) {
return console.log("Length over 5 not supported yet!")
}
return jsonObj;
}
Regards.
OF course it's possible, a simple loop will perfeclty do the job.
function constructJson(jsonKey, jsonValue){
//REWRITE!!!!!!!!
langFile = {a:{}, foo:{}};// remove this for your own code
var jsonObj = langFile;
var jsonKeyArr = jsonKey.split('.');
var currentValue = jsonObj;
for(var i = 0; i < jsonKeyArr.length;i++){
if(currentValue[jsonKeyArr[i]]===undefined){
currentValue[jsonKeyArr[i]] = {};
}
if(i < jsonKeyArr.length-1){
currentValue = currentValue[jsonKeyArr[i]];
}else{
currentValue[jsonKeyArr[i]] = jsonValue;
}
}
return jsonObj;
}
alert(JSON.stringify(constructJson("a.b.cd.ef", "toto")));
I just assigning to a temporary variable each sublevel. When i'm on the last i'm assigning the value.
Yes you can, using the javascript reduce function on the array created from the splitted string.
function namespaceCreateExceptLast(representationOfElementToCreate, baseNamespace) {
var tokens;
if (typeof representationOfElementToCreate !== 'string')
throw new Error('Expecting string as first parameter');
if (baseNamespace === undefined)
baseNamespace = window;
tokens = representationOfElementToCreate.split('.');
// Remove the last element (which will contain the value)
tokens.pop();
// Use reduce to create part by part your new object
return tokens.reduce(function (prev, property) {
if (typeof prev !== 'object') {
throw Error('One property is already defined but not an object, namespace creation has failed', property);
return undefined;
} else {
if (!prev[property])
prev[property] = {};
return prev[property];
}
}, baseNamespace);
};
Then you can have:
function constructJson(jsonKey, jsonValue){
let jsonObj = langFile;
var lastItem = namespaceCreateExceptLast(jsonKey, jsonObj);
var lastKey = jsonKey.substring(jsonKey.lastIndexOf('.') + 1);
lastItem[lastKey] = jsonValue;
}
I have added some comments and exceptions to help you understand how it's done, but it's mainly based on the reduce function which you can easily get help for (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce).

Game Maker JS Extension

Hi I'm looking to add javascript functions to game maker, but the format of them is like this:
companyname.initialize({
soundMuteCallback: muteSound, // optional
soundUnmuteCallback: unmuteSound // optional
});
And in the file they look like this
this.initialize = function(params) {
companyname.getSharedEventCenter().postEvent(SharedEventKeys.API_INITIALIZE);
_isInitialized = true;
if (typeof params !== "undefined") {
var muteSoundCallback = ("soundMuteCallback" in params && typeof params["soundMuteCallback"] === "function") ? params["soundMuteCallback"] : undefined;
var unmuteSoundCallback = ("soundUnmuteCallback" in params && typeof params["soundUnmuteCallback"] === "function") ? params["soundUnmuteCallback"] : undefined;
_adsManager.setSoundCallbacks(function() {
typeof muteSoundCallback === "function" && muteSoundCallback();
[].forEach.call(document.getElementsByTagName("audio"), function(element){
element.muted = true;
});
}, function() {
typeof unmuteSoundCallback === "function" && unmuteSoundCallback();
[].forEach.call(document.getElementsByTagName("audio"), function(element){
element.muted = false;
});
});
}
_tryShowAd();
};
Does anyone have any idea how to do this in game maker? I don't know what information to put in the extension function properties.
Thanks,
Mitchell.
I would recommend creating a new function that Game Maker can understand and then use that to create your object and the constructor you are showing here.
company.initialize = function(params) {
companyname.getSharedEventCenter().postEvent(SharedEventKeys.API_INITIALIZE);
_isInitialized = true;
if (typeof params !== "undefined") {
var muteSoundCallback = ("soundMuteCallback" in params && typeof params["soundMuteCallback"] === "function") ? params["soundMuteCallback"] : undefined;
var unmuteSoundCallback = ("soundUnmuteCallback" in params && typeof params["soundUnmuteCallback"] === "function") ? params["soundUnmuteCallback"] : undefined;
_adsManager.setSoundCallbacks(function() {
typeof muteSoundCallback === "function" && muteSoundCallback();
[].forEach.call(document.getElementsByTagName("audio"), function(element){
element.muted = true;
});
}, function() {
typeof unmuteSoundCallback === "function" && unmuteSoundCallback();
[].forEach.call(document.getElementsByTagName("audio"), function(element){
element.muted = false;
});
});
}
_tryShowAd();
};
function createMuteCallback() {
muteCallback = function () {
// Code to handle the callback
}
return muteCallback;
}
function createUnmuteCallback() {
unmuteCallback = function () {
// Code to handle the callback
}
return unmuteCallback;
}
function createCompany (mute, unmute) {
if (mute == 1) {
soundMuteCallback.createMuteCallback();
}
if (unmute == 1) {
soundUnmuteCallback.createUnmuteCallback();
}
company.initialize(soundMuteCallback, soundUnmuteCallback);
}
So all of that goes in the same .js file. Create a new extension in Game Maker. Add the .js file to that extension. Add a function named createCompany with two optional parameters.
Then when you call createCompany(1, 1); in your Game Maker code, the .js file will run and will initialize the company object with the two callback functions.
Hope this helps.

Knockotjs Validation. Passing function gives undefined, because of property order inside VM

validation works fine if validation properties are placed after "HasError" property in VM.
In the case that the property placed before HasError I will get "parameters.hasError" as undefined. I think it's because the property "HasError" is not defined to that time.
Is there any solution without changing the order of the properties inside VM to make it work.
Thanks!
self._BusTypeDefault = function(param) {
var ret = param.BusType;
if(typeof(ret)==='undefined') {
ret = '';
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.BusType = ko.observable(self._BusTypeDefault(init)).extend({maxLength: {message: $Resources.PCIBUSError(), maxFieldLength: 255,hasError: self.HasError }});
self._HasErrorDefault = function(param) {
var ret = param.HasError;
if(typeof(ret)==='undefined') {
ret = false;
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.HasError = ko.observable(self._HasErrorDefault(init)).extend({errorAggregation: {}});
ko.extenders.maxLength = function (target, parameters) {
//add some sub-observables to our observable
target.hasMaxLengthError = ko.observable();
target.validationMessageMaxError = ko.observable();
//define a function to do validation
function validate(newValue) {
var preValue = target.hasMaxLengthError();
if (newValue.length >= parameters.maxFieldLength) {
target.hasMaxLengthError(true);
target.validationMessageMaxError(parameters.message || "This field is required");
}
else {
target.hasMaxLengthError(false);
target.validationMessageMaxError("");
}
if (parameters.hasError != null && target.hasMaxLengthError() !== preValue && typeof preValue !== 'undefined') {
parameters.hasError(target.hasMaxLengthError());
}
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
You can use a function to delay the interpretation of hasError:
this.myObservable = ko.observable(1).extend({ myExtender : { hasError: function () { return self.hasError } } });
Then in the extender you'll need to call the function to actually get the observable behind:
ko.extenders.myExtender = function (target, params) {
function validate(newValue) {
alert("New Value: " + newValue + " ; Has Error: " + params.hasError()());
}
target.subscribe(validate);
}
See this example: http://jsfiddle.net/7ywLN/

Categories