I have certain form inputs that are only displayed depending on other input's value. I am trying to control when inputs fadeOut() and fadeIn() with, I think, a promise() but it seems the call-stack still runs everything synchronously - maybe I am not configuring the promise correctly?
Here is some pseudo code. The catch here is one of my inputs, input3 needs to be removed, then re-added to correct some JQuery validation and JQuery automcomplete rules, so I am trying to create the function to do that, every time that function is called to fadeIn().
function display_input1() {
return new Promise(function (resolve) {
resolve(hide_input3())
var new_element = `...`
$(new_element)
.hide()
.insertAfter(
$("#input0")
)
$("new_element").fadeIn("fast")
})
}
function hide_input1() {
$("#input1")
.fadeOut("fast", function () {
$("#input1").remove()
})
}
function display_input2(data) {
var new_element = `...`
$(new_element)
.hide()
.insertAfter(
$("#input0")
)
$("new_element").fadeIn("fast")
}
function hide_input2() {
$("#input2")
.fadeOut("fast", function () {
$("#input2").remove()
})
}
function display_input3(search_type) {
return new Promise(function (resolve) {
resolve(hide_input1(), hide_input3())
if (search_type == "some_varible") {
var new_element = `...`
} else if (search_type == "some_variable") {
var new_element - `...`
}
$(new_element)
.hide()
.insertAfter(
$("#input2")
)
$("new_element").fadeIn("fast")
})
}
function hide_input3() {
if ($("#input3").length) {
$("#input3")
.fadeOut("fast", function () {
$("#input3").remove()
})
}
}
$(document).on("change", "#input0", function (e) {
var option = $(this).find("option:selected")
var data = AJAX response
if (data["some_attr"]) {
display_input2(data)
hide_input1()
} else {
hide_input2()
if (data["some_attr"]) {
if (!$("#input1").length) {
display_input1()
}
} else {
hide_input1()
}
}
})
$(document).on("change", "input2", function (e) {
var option = this
if (option.value === "input1") {
display_input1()
} else if (["input2", "input3"].includes(option.value)) {
if (option.value === "input2") {
var search_type = "some_varible"
} else {
var search_type = "some_varibale"
}
display_input3(search_type)
}
})
$(document).on("click", "button.cancel", function (e) {
hide_input1()
hide_input2()
hide_input3()
$(form).validate().resetForm()
})
UPDATE
Based on comments, I've reflected my code block for better understanding. I've consolidated functions in hopes to make the code more malleable.
function display_form_input(input_type, search_type = null, data = null) {
if (input_type == "input_var1") {
var new_element = `...`
$(new_element)
.hide()
.insertAfter(
$("#input1")
)
$("#input1").fadeIn("fast")
} else if (input_type == "input_var2") {
var new_element = `...`
$(new_element)
.hide()
.insertAfter(
$("#input0")
)
$("#input2").fadeIn("fast")
} else if (input_type == "input_var3") {
if (search_type == "some_var1") {
var new_element = `...`
} else if (search_type == "some_var2") {
var new_element = `...`
}
$(new_element)
.hide()
.insertAfter(
$("#input3")
)
$("#input3").fadeIn("fast")
}
}
function hide_form_input(input_type) {
return new Promise(function (resolve) {
if (input_type == "input_var1") {
$("#input1")
.fadeOut("fast", function () {
$("#input1").remove()
resolve()
})
} else if (input_type == "input_var2") {
$("#input2")
.fadeOut("fast", function () {
$("#input2").remove()
resolve()
})
} else if (input_type == "input_var3") {
$("#input3")
.fadeOut("fast", function () {
$("#input3").remove()
resolve()
})
}
})
}
$(document).on("change", "#input0", function (e) {
var option = $(this).find("option:selected")
$.ajax({
...
success: function (data) {
if (data["key1"]) {
hide_form_input("input_var1")
display_form_input(
"input_var2",
null,
(data = data["key1"]),
)
} else {
hide_form_input("input_var2")
if (data["key2"] && !$("#input1").length) {
display_form_input("input_var1")
}
}
},
})
})
$(document).on("change", "#input2", function (e) {
var option = this
if (option.value === "value1") {
var search_form = hide_form_input("input_var3")
search_form
.promise()
.done(display_form_input("input_var1"))
} else if (["value2", "value3"].includes(option.value)) {
if (option.value === "value2") {
var search_type = "value22"
} else {
var search_type = "value33"
}
hide_form_input("input_var1")
var search_form = hide_form_input("input_var3")
search_form
.promise()
.done(display_form_input("input_var3", search_type))
}
})
$(document).on("click", "button.cancel", function (e) {
var items = ["input_var1", "input_var2", "input_var3"]
items.forEach(function (item) {
hide_form_input(item)
})
$(form).validate().resetForm()
})
You are creating a promise in display_input1, but where you are calling display_input1 you are not capturing the promise or ever resolving it. So your promise isn't doing anything
if (option.value === "input1") {
var p = display_input1();
//here you can call p.then(function(resolve, reject) { ... } or
var res = await p; (if you mark the function that await is in async)
Thanks to #Bergi and #ControlAltDel for walking me through understanding .promise(). I now have a working example I was trying to achieve! Thank you guys so much for your patience.
Here is my working example in pseudo code. I made some improvements too on a function based #Bergi comments (always strive for DRY code)
function display_form_input(input_type, search_type = null, data = null) {
/*
create_form_input: Display the form input for a given input_type.
Args:
input_type (str): The type of input to create.
Optional Args:
search_type (str): The type of results expected from this search.
Returns:
None
*/
if (input_type == "input_var1") {
var new_element = `...`
$(new_element)
.hide()
.insertAfter(
$("#input0").closest("div.row").children(),
)
$("#input1").parents(":eq(1)").fadeIn("fast")
} else if (input_type == "input_var2") {
var new_element = `...`
$(new_element)
.hide()
.insertAfter($("#input0").closest("div.row"))
$("#input2").parents(":eq(2)").fadeIn("fast")
} else if (input_type == "input_var3") {
if (search_type == "some_var1") {
var new_element = `...`
} else if (search_type == "some_var2") {
var new_element = `...`
}
$(new_element)
.hide()
.insertAfter($("#input2").closest("div.row").children())
$("#input3").parents(":eq(1)").fadeIn("fast")
}
}
function hide_form_input(input_type) {
/*
hide_form_input: Hide the form input for a given input_type.
Args:
input_type (str): The type of input to hide.
Returns:
JQuery Promise.
*/
if (input_type == "input_var1") {
var form_element = $("#input1").parents(":eq(1)")
} else if (input_type == "input_var2") {
var form_element = $("#input2").parents(":eq(2)")
} else if (input_type == "input_var3") {
var form_element = $("#input3").parents(":eq(1)")
}
return form_element
.fadeOut("fast")
.promise()
.then(function () {
form_element.remove()
})
}
$(document).on("change", "#input0", function (e) {
/*
Event Handler: Trigger what form field to display when a the input0
changes.
*/
var option = $(this).find("option:selected")
$.ajax({
...
success: function (data) {
if (data["key1"]) {
hide_form_input("input_var1").then(function () {
if (!$("#input2").length) {
display_form_input(
"input_var2",
null,
data["key1"],
)
}
})
} else {
hide_form_input("input_var2")
if (data["key2"] && !$("#input1").length) {
display_form_input("input_var1")
} else if (!data["key2"]) {
hide_form_input("input_var2")
}
}
},
})
})
$(document).on("change", "#input2", function (e) {
/*
Event Handler: Trigger what form field to display when a the input2
changes.
*/
var option = this
if (option.value === "value1") {
hide_form_input("input_var3").then(function () {
display_form_input("input_var1")
})
} else if (["value2", "value3"].includes(option.value)) {
if (option.value === "value2") {
var search_type = "some_var1"
} else {
var search_type = "some_var2"
}
hide_form_input("input_var1")
hide_form_input("input_var3").then(function () {
display_form_input("input_var3", search_type)
})
}
})
$(document).on("click", "button.cancel", function (e) {
/*
Event Handler: Make form back to default state.
*/
var button = this
var form = $($(button).closest("form"))
var items = ["input_var1", "input_var2", "input_var3"]
items.forEach(function (item) {
hide_form_input(item)
})
$(form).validate().resetForm()
})
// openMNav.isDropDown = false;
var openMNav = function () {
if (!this.isDropDown) {
this.isDropDown = true;
console.log(1);
} else {
this.isDropDown = false;
console.log(0);
}
My question is how to do something like this: var openMNav.isDropDown = false;.
I want to set openMNav -> isDropDown outside the function to false
You mean to set on the function itself, you should just use openMNav in the function not this.
function openMNav () {
if (!openMNav.isDropDown) {
console.log('do open');
openMNav.isDropDown = true;
}
else {
console.log('already opened');
openMNav.isDropDown = false;
}
}
openMNav();
openMNav();
I tried to make parts of code read-only in Ace editor.
I have tried by using code given in JsFiddle
$(function() {
var editor = ace.edit("editor1")
, session = editor.getSession()
, Range = require("ace/range").Range
, range = new Range(1, 4, 1, 10)
, markerId = session.addMarker(range, "readonly-highlight");
session.setMode("ace/mode/javascript");
editor.keyBinding.addKeyboardHandler({
handleKeyboard : function(data, hash, keyString, keyCode, event) {
if (hash === -1 || (keyCode <= 40 && keyCode >= 37)) return false;
if (intersects(range)) {
return {command:"null", passEvent:false};
}
}
});
before(editor, 'onPaste', preventReadonly);
before(editor, 'onCut', preventReadonly);
range.start = session.doc.createAnchor(range.start);
range.end = session.doc.createAnchor(range.end);
range.end.$insertRight = true;
function before(obj, method, wrapper) {
var orig = obj[method];
obj[method] = function() {
var args = Array.prototype.slice.call(arguments);
return wrapper.call(this, function(){
return orig.apply(obj, args);
}, args);
}
return obj[method];
}
function intersects(range) {
return editor.getSelectionRange().intersects(range);
}
function preventReadonly(next, args) {
if (intersects(range)) return;
next();
}
});
I got a problem when I keep pressing backspace it went into the read-only part and there was no editable part left.
How can I make multiple chunks of code read-only and avoid last character from read-only getting deleted.
Also, how to achieve the whole thing dynamically where I have markers in text specifying editable portions ?
Check the below code that allows multiple chunk of lines read-only with Enter at end of range to prevent non reversible delete and drag/drop handled.
function set_readonly(editor,readonly_ranges) {
var session = editor.getSession()
, Range = require("ace/range").Range;
ranges = [];
function before(obj, method, wrapper) {
var orig = obj[method];
obj[method] = function() {
var args = Array.prototype.slice.call(arguments);
return wrapper.call(this, function(){
return orig.apply(obj, args);
}, args);
}
return obj[method];
}
function intersects(range) {
return editor.getSelectionRange().intersects(range);
}
function intersectsRange(newRange) {
for (i=0;i<ranges.length;i++)
if(newRange.intersects(ranges[i]))
return true;
return false;
}
function preventReadonly(next, args) {
for(i=0;i<ranges.length;i++){if (intersects(ranges[i])) return;}
next();
}
function onEnd(position){
var row = position["row"],column=position["column"];
for (i=0;i<ranges.length;i++)
if(ranges[i].end["row"] == row && ranges[i].end["column"]==column)
return true;
return false;
}
function outSideRange(position){
var row = position["row"],column=position["column"];
for (i=0;i<ranges.length;i++){
if(ranges[i].start["row"]< row && ranges[i].end["row"]>row)
return false;
if(ranges[i].start["row"]==row && ranges[i].start["column"]<column){
if(ranges[i].end["row"] != row || ranges[i].end["column"]>column)
return false;
}
else if(ranges[i].end["row"] == row&&ranges[i].end["column"]>column){
return false;
}
}
return true;
}
for(i=0;i<readonly_ranges.length;i++){
ranges.push(new Range(...readonly_ranges[i]));
}
ranges.forEach(function(range){session.addMarker(range, "readonly-highlight");});
session.setMode("ace/mode/javascript");
editor.keyBinding.addKeyboardHandler({
handleKeyboard : function(data, hash, keyString, keyCode, event) {
if (Math.abs(keyCode) == 13 && onEnd(editor.getCursorPosition())){
return false;
}
if (hash === -1 || (keyCode <= 40 && keyCode >= 37)) return false;
for(i=0;i<ranges.length;i++){
if (intersects(ranges[i])) {
return {command:"null", passEvent:false};
}
}
}
});
before(editor, 'onPaste', preventReadonly);
before(editor, 'onCut', preventReadonly);
for(i=0;i<ranges.length;i++){
ranges[i].start = session.doc.createAnchor(ranges[i].start);
ranges[i].end = session.doc.createAnchor(ranges[i].end);
ranges[i].end.$insertRight = true;
}
var old$tryReplace = editor.$tryReplace;
editor.$tryReplace = function(range, replacement) {
return intersectsRange(range)?null:old$tryReplace.apply(this, arguments);
}
var session = editor.getSession();
var oldInsert = session.insert;
session.insert = function(position, text) {
return oldInsert.apply(this, [position, outSideRange(position)?text:""]);
}
var oldRemove = session.remove;
session.remove = function(range) {
return intersectsRange(range)?false:oldRemove.apply(this, arguments);
}
var oldMoveText = session.moveText;
session.moveText = function(fromRange, toPosition, copy) {
if (intersectsRange(fromRange) || !outSideRange(toPosition)) return fromRange;
return oldMoveText.apply(this, arguments);
}
}
function refresheditor(id,content,readonly) {
var temp_id=id+'_temp';
document.getElementById(id).innerHTML="<div id='"+temp_id+"'></div>";
document.getElementById(temp_id).innerHTML=content;
var editor = ace.edit(temp_id);
set_readonly(editor,readonly);
}
function get_readonly_by_editable_tag(id,content){
var text= content.split("\n");
var starts=[0],ends=[];
text.forEach(function(line,index){
if((line.indexOf("<editable>") !== -1))ends.push(index);
if((line.indexOf("</editable>") !== -1))starts.push(index+1);
});
ends.push(text.length);
var readonly_ranges=[];
for(i=0;i<starts.length;i++){
readonly_ranges.push([starts[i],0,ends[i],0])
}
refresheditor(id,content,readonly_ranges);
}
var content=document.getElementById("code").innerHTML;
function readonly_lines(id,content,line_numbers){
var readonly_ranges=[];
all_lines= line_numbers.sort();
for(i=0;i<line_numbers.length;i++){
readonly_ranges.push([line_numbers[i]-1,0,line_numbers[i],0]);
}
refresheditor(id,content,readonly_ranges);
}
get_readonly_by_editable_tag("myeditor",content)
//readonly_lines("myeditor",content,[5,7,9]);
.ace_editor {
width:100%;
height:300px;
}
.readonly-highlight{
background-color: red;
opacity: 0.2;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ace.c9.io/build/src/ace.js"></script>
<link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/normalize.css">
<link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/result-light.css">
<button onclick="get_readonly_by_editable_tag('myeditor',content)">Readonly by tags</button>
<button onclick="readonly_lines('myeditor',content,[3,7])">Readonly lines 3 and 7 </button>
<div id="myeditor" ></div>
<div id="code" style="display:none;">//<editable>
//</editable>
function refresheditor() {
//<editable>
document.getElementById("myeditor").innerHTML="<div id='editor'></div>";
document.getElementById("editor").innerHTML=document.getElementById("code").innerHTML;
//</editable>
var editor = ace.edit("editor")
, session = editor.getSession()
, Range = require("ace/range").Range;
ranges = [];
var text= document.getElementById("code").innerHTML.split("\n");
var starts=[0],ends=[];
text.forEach(function(line,index){
if((line.indexOf("<editable>") !== -1))ends.push(index);
if((line.indexOf("</editable>") !== -1))starts.push(index+1);
});
ends.push(text.length);
for(i=0;i<starts.length;i++){
ranges.push(new Range(starts[i], 0,ends[i] ,0));
}
ranges.forEach(function(range){session.addMarker(range, "readonly-highlight");});
session.setMode("ace/mode/javascript");
//<editable>
editor.keyBinding.addKeyboardHandler({
handleKeyboard : function(data, hash, keyString, keyCode, event) {
var pos=editor.getCursorPosition();
if (Math.abs(keyCode) == 13){
for (i=0;i<ranges.length;i++){
if((ranges[i].end["row"]==pos["row"])&&(ranges[i].end["column"]==pos["column"])){ return false;}
}
}
if (hash === -1 || (keyCode <= 40 && keyCode >= 37)) return false;
for(i=0;i<ranges.length;i++){
if (intersects(ranges[i])) {
return {command:"null", passEvent:false};
}
}
}
});
//</editable>
before(editor, 'onPaste', preventReadonly);
before(editor, 'onCut', preventReadonly);
for(i=0;i<ranges.length;i++){
ranges[i].start = session.doc.createAnchor(ranges[i].start);
ranges[i].end = session.doc.createAnchor(ranges[i].end);
ranges[i].end.$insertRight = true;
}
function before(obj, method, wrapper) {
var orig = obj[method];
obj[method] = function() {
var args = Array.prototype.slice.call(arguments);
return wrapper.call(this, function(){
return orig.apply(obj, args);
}, args);
}
return obj[method];
}
function intersects(range) {
return editor.getSelectionRange().intersects(range);
}
function preventReadonly(next, args) {
for(i=0;i<ranges.length;i++){if (intersects(ranges[i])) return;}
next();
}
}
refresheditor();
</div>
This code snippet will prevent the user from editing the first or last line of the editor:
editor.commands.on("exec", function(e) {
var rowCol = editor.selection.getCursor();
if ((rowCol.row == 0) || ((rowCol.row + 1) == editor.session.getLength())) {
e.preventDefault();
e.stopPropagation();
}
});
https://jsfiddle.net/tripflex/y0huvc1b/
Source:
https://groups.google.com/forum/#!topic/ace-discuss/yffGsSG7GSA
sorry: this code does not handle drag/drop
I added this last proposal last week: Ace Editor: Lock or Readonly Code Segment
Validation gets removed from SSN textbox after calling event.preventDefault(); in $("form").submit(function (event)
Validation gets fired and than removed
function CheckSSN() {
var sender = $("#SSN");
var value = $(sender).val();
var errorSpan = $(sender).siblings("span[data-valmsg-for='SSN']");
var message = "SSN is required.";
var validAliasPatt = /^[0-9]{9}$/;
if (!value || value == "" || value == null) {
enableValidationUI(sender, errorSpan, message);
return false;
}
else if (!validAliasPatt.test(value)) {
message = "SSN should be a 9 digit number.";
enableValidationUI(sender, errorSpan, message);
return false;
}
else {
disableValidationUI(sender, errorSpan);
return true;
}
}
----------
("form").submit(function (event) {
var submit = true;
if (!CheckSSN()) {
event.preventDefault();
var submit = false;
}
if (submit) {
$("form").submit();
}
else {
event.preventDefault();
return;
}
});
The issue was with using ("form").submit(function (event) which ended up creating endless loop. After changing to ('button').click fixed this issue.
How do I access the jquery object when validateName is called?
In my code below, errorDate is not a JQuery object.
Correction to variable name
$(document).ready(function() {
var errorDate = $("#errorDate");
errorDate.blur(validateName);
function validateName() {
if (errorDate.val().length == "") {
errorDate.addClass("error");
return false;
}
else {
errorDate.removeClass("error");
return true;
}
}
});
Just use $(this) instead of name.
You can
$(document).ready(function() {
var errorDate = $("#errorDate");
errorDate.blur(validateName);
function validateName() {
var $this = $(this);
if ($this.val().length == "") {
$this.addClass("error");
return false;
}
else {
$this.removeClass("error");
return true;
}
}
});
Or use errorDate instead of name since it is a closure variable
$(document).ready(function() {
var errorDate = $("#errorDate");
errorDate.blur(validateName);
function validateName() {
if (errorDate.val().length == "") {
errorDate.addClass("error");
return false;
}
else {
errorDate.removeClass("error");
return true;
}
}
});
Try this,
$(document).ready(function() {
var errorDate = $("#errorDate");
errorDate.blur(validateName);
function validateName() {
// no need to get length you can use $(this).val()==""
if ($(this).val() == "") {
$(this).addClass("error");
return false;
}
else {
$(this).removeClass("error");
return true;
}
}
});
Fiddle http://jsfiddle.net/jtWFX/