With Angular, I'm trying to implement a way to change a value with an 'Edit' button click in such a way that when this button is clicked, an input is displayed over the text, and when the 'Save' button is clicked, the input's opacity becomes 0, and the model's value is applied.
I've created a jsfiddle to make my issue a bit more visual. JSFIDDLE DEMO
The issue is the following: I want to select the text to make it obvious for the user that it can be changed now, after the 'Edit' button is clicked. I do it this way:
var input = angular.element(document.querySelector('input'))[0];
input.focus();
input.select();
The only problem is that the input.select() only works on second attempt. You can see it in the demo. I have no rational explanation to this whatsoever. I need to mention that this app that I'm writing is for Electron, it means that it will only launch in Chromium, so I don't need cross-browser support for this.
When the 'Edit' button is clicked for the first time, no selection happens:
But when I click 'Save' and then 'Edit' again, everything works as expected:
Any thought would be much appreciated!
Use $timeout , it will trigger digest cycle
var app = angular.module('app', []);
app.controller('mainController', function($timeout,$scope) {
var vm = this;
vm.address = '127.0.0.1';
vm.name = 'anabelbreakfasts';
vm.editing = {
address: false
};
vm.temp = {
address: null
};
vm.changeClick = function(element) {
vm.editing[element] = !vm.editing[element];
if (vm.editing[element]) {
vm.temp[element] = vm[element];
var input = angular.element(document.querySelector('div.row.' + element + ' input'))[0];
$timeout(function(){
input.focus();
input.select();
});
} else {
vm[element] = vm.temp[element];
}
};
});
Fiddle
Use setTimeout:
setTimeout(function(){
input.select();
}, 0)
Also, input.focus() is kind of redundant
Related
I have one form validator example in java script. In this case, Is there any way to check whether the form is dirty or not?
My platform is JavaScript
Please find corresponding sample below, and suggest any solution.
sample link
code snipet:
i have used like:
if (name.value != name.defaultValue) {
alert("#name has changed");
}
you have tags as kendo-ui-grid and kendo-validator so I suppose you are using kendo framework.
if you want to see if the form is dirty you should check the viewModel in kendo way sample.
basically I've created a viewModel which is impements the ObservableObject interface and has a two way binding with the form's container.
Every time you change something in the form the change event is fired in the viewModel which set a variable (dirty) as true.
var dirty = false;
var viewModel = new kendo.data.ObservableObject({
fullname: "test"
});
viewModel.bind("change", function () {
alert("changed");
dirty = true;
});
kendo.bind($("#tickets"), viewModel);
Add all the fields you need to "watch" in the ObservableObject
and set on their markup the property data-bind="value:fieldName"
You could use JQuery something like this...
var _isDirty = false;
$("input[type='text']").change(function(){
_isDirty = true;
});
You need a global boolean variable to remember when you edit anything in your form.
This variable should initially be false, then when the user edits an input, it changes to true. When you submit the form it changes back to false. Now you can check any time you want, whether the dirty variable is true or false.
Example code:
var dirty = false;
var inputs = document.querySelectorAll('input');
for (var i = 0;i < inputs.length)
{
var input = inputs[i];
input .addEventListener('input', function()
{
dirty = true;
});
}
var form = document.forms[0];
form.addEventListener('submit, function()
{
dirty = false;
}
I have created a form using angular js with around 7 input elements. When I click on submit, I want the form to get scrolled up to the first blank field which is required. But now it is not correctly pointing to the field left blank. Any solution to resolve this ?
Check the error here.
before submitting the form, you can check whether the form is valid or not and use .focus() to focus on that element.
$scope.onSubmit = function(yourForm) {
if (!yourForm.$valid) {
angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').focus();
return false;
}
};
method #2 - You can also use $anchorScroll service
see the documentation here
$scope.onSubmit = function(yourForm) {
if (!yourForm.$valid) {
var id = angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').data('id');
$location.hash(id);
$anchorScroll();
return false;
}
};
We are using Backgrid and have discovered that to begin editing a "boolean" (checkbox) cell in Backgrid, you must click twice: the first click is ignored and does not toggle the state of the checkbox. Ideally we would get to the root of what is causing this behavior (e.g. is preventDefault being called) and solve it there, but I at first I tried a different approach with the following extension of BooleanCell's enterEditMode method which seemed like a logical place since it was upon entering edit mode that the checkbox click was being ignored.
Problem is my attempt also toggles the state of the previously edited checkbox. Here is the code.
var BooleanCell = Backgrid.BooleanCell.extend({
/*
* see https://github.com/wyuenho/backgrid/issues/557
*/
enterEditMode: function () {
Backgrid.BooleanCell.prototype.enterEditMode.apply(this, arguments);
var checkbox = this.$('input');
checkbox.prop('checked', !checkbox.prop('checked'));
}
});
The following seems to work:
var BooleanCell = Backgrid.BooleanCell.extend({
editor: Backgrid.BooleanCellEditor.extend({
render: function () {
var model = this.model;
var columnName = this.column.get("name");
var val = this.formatter.fromRaw(model.get(columnName), model);
/*
* Toggle checked property since a click is what triggered enterEditMode
*/
this.$el.prop("checked", !val);
model.set(columnName, !val);
return this;
}
})
});
This is because the render method gets called by Backgrid.BooleanCell's enterEditMode method on click, and said method destroys and re-creates the checkbox as follows but in so doing loses the checked state (after the click) of the original "non-edit-mode" checkbox
this.$el.empty();
this.$el.append(this.currentEditor.$el);
this.currentEditor.render();
A simpler approach:
var OneClickBooleanCell = Backgrid.BooleanCell.extend({
events: {
'change input': function(e) {
this.model.set(this.column.get('name'), e.target.checked);
},
},
});
This bypasses the CellEditor mechanism entirely and just reacts to the input event on the checkbox by updating the model.
I am using the jQuery plugin radiosToSlider (http://rubentd.com/radios-to-slider/) and need to make sure that all radio button groups are checked and to give an alert when they are
I can do this if they are just radio buttons by checking the length but because the plugin changes the input buttons I am having difficulties
My fiddle is
http://jsfiddle.net/yFaAj/270/
$(document).ready(function () {
$(".radios").radiosToSlider();
});
$(":radio").change(function () {
var names = {};
$(':radio').each(function () {
names[$(this).attr('name')] = true;
});
var count = 0;
$.each(names, function () {
count++;
});
if ($(':radio:checked').length === count) {
alert("all answered");
}
}).change();
thanks
The problem isn't that the plugin changes your structure (although it does add some ins elements, which I don't agree with), it's that the plugin doesn't fire a change event for the converted radio controls, and setting the checked property interactively doesn't appear to do so either.
Since the plugin author doesn't publish an API for this use case, it's hard to know whether this is by design or oversight, but the source code definitely doesn't fire the event when the slider is clicked:
this.bearer.find('.slider-level').click( function(){
var radioId = $(this).attr('data-radio');
slider.bearer.find('#' + radioId).prop('checked', true);
slider.setSlider();
});
Your options, as I see them:
Contact the API author and ask for a bug fix, or the intended way to support this case
Downside: Time: dependent on the author to respond
Attach your "check" function to the click event of the .slider-level class, as the API does.
Downside: Brittle: future versions of the plugin may attach the behavior to different selectors
Attach your function to the click event of your radio group, and catch click events on the bubble
Downside: Inefficient: It will check for every click in the radio control
Here's a sample implementation of option 3. DEMO.
$(document).ready(function () {
$(".radios").radiosToSlider();
});
var makeIsRadioGroupChecked = function(selector) {
var $radioGroup = $(selector);
return function isRadioGroupChecked() {
return $radioGroup.find(':checked').length > 0;
};
};
var isOptionsChecked = makeIsRadioGroupChecked('#optionsRadioGroup');
var isSizeChecked = makeIsRadioGroupChecked('#sizeRadioGroup');
var areAllGroupsChecked = function() {
return isOptionsChecked() && isSizeChecked();
};
var alertIfAllGroupsChecked = function() {
if (areAllGroupsChecked()) {
alert("all answered");
}
};
$('.radios').on('click', alertIfAllGroupsChecked);
I am developing web app, I have such a requirement that whenever user click on text inside span i need convert it into input field and on blur i need to convert it back to span again. So i am using following script in one of my jsp page.
Java Script:
<script type="text/javascript">
function covertSpan(id){
$('#'+id).click(function() {
var input = $("<input>", { val: $(this).text(),
type: "text" });
$(this).replaceWith(input);
input.select();
});
$('input').live('blur', function () {
var span=$("<span>", {text:$(this).val()});
$(this).replaceWith(span);
});
}
JSP Code:
<span id="loadNumId" onmouseover="javascript:covertSpan(this.id);">5566</span>
Now my problem is, everything works fine only for the first time. I mean whenever i click on the text inside span for the first time it converts into input field and again onblur it coverts back from input field to normal text. But if try once again to do so it won't work. Whats wrong with above script?
Would be good to change your dom structure to something like this (note that the span and the input are side by side and within a shared parent .inputSwitch
<div class="inputSwitch">
First Name: <span>John</span><input />
</div>
<div class="inputSwitch">
Last Name: <span>Doe</span><input />
</div>
Then we can do our JS like this, it will support selecting all on focus and tabbing to get to the next/previous span/input: http://jsfiddle.net/x33gz6z9/
var $inputSwitches = $(".inputSwitch"),
$inputs = $inputSwitches.find("input"),
$spans = $inputSwitches.find("span");
$spans.on("click", function() {
var $this = $(this);
$this.hide().siblings("input").show().focus().select();
}).each( function() {
var $this = $(this);
$this.text($this.siblings("input").val());
});
$inputs.on("blur", function() {
var $this = $(this);
$this.hide().siblings("span").text($this.val()).show();
}).on('keydown', function(e) {
if (e.which == 9) {
e.preventDefault();
if (e.shiftKey) {
$(this).blur().parent().prevAll($inputSwitches).first().find($spans).click();
} else {
$(this).blur().parent().nextAll($inputSwitches).first().find($spans).click();
}
}
}).hide();
I understand you think that element replacement is a nice thing, however, I would use a prompt to get the text. Why? It is a lot easier and actually a bit prettier for the user as well. If you are curious on how to do it, I show you.
html:
<span class='editable'>foobar</span>
js:
$(function()
{
$('span.editable').click(function()
{
var span = $(this);
var text = span.text();
var new_text = prompt("Change value", text);
if (new_text != null)
span.text(new_text);
});
});
http://jsfiddle.net/qJxhV/1/
First, you need to change your click handler to use live() as well. You should take note, though, that live() has been deprecated for quite a while now. You should be using on() in both cases instead.
Secondly, when you replace the input with the span, you don't give the element an id. Therefore, the element no longer matches the selector for your click handler.
Personally, I would take a different (and simpler) approach completely. I would have both the span and in the input in my markup side by side. One would be hidden while the other is shown. This would give you less chance to make mistakes when trying to recreate DOM elements and improve performance since you won't constantly be adding/removing elements from the DOM.
A more generic version of smerny's excellent answer with id's can be made by slightly altering two lines:
$input.attr("ID", "loadNum"); becomes $input.attr("ID", $(this).attr("ID")); -- this way, it simply takes the current id, and keeps it, whatever it is.
Similarly,
$span.attr("ID", "loadNum"); becomes $span.attr("ID", $(this).attr("ID"));
This simply allows the functions to be applied to any div. With two similar lines added, both id and class work fine. See example.
I have done little change in code, By using this input type cant be blank, it will back to its real value.
var switchToInput = function () {
var $input = $("<input>", {
val: $(this).text(),
type: "text",
rel : jQuery(this).text(),
});
$input.addClass("loadNum");
$(this).replaceWith($input);
$input.on("blur", switchToSpan);
$input.select();
};
var switchToSpan = function () {
if(jQuery(this).val()){
var $text = jQuery(this).val();
} else {
var $text = jQuery(this).attr('rel');
}
var $span = $("<span>", {
text: $text,
});
$span.addClass("loadNum");
$(this).replaceWith($span);
$span.on("click", switchToInput);
}
$(".loadNum").on("click", switchToInput);
jsFiddle:- https://jsfiddle.net/svsp3wqL/