Got an odd problem with angular. I can get this to work with ng-click but I need to use onchange for other reasons.
My code seems to be working fine for other buttons that can be pressed to trigger the same code but when it comes to my upload button it decides not to update the ng-class even though scope.slideMenu is actually logged out as true.
I would also like to close the menu when the user has finished choosing an image/file if someone could help with that as well.
Heres the code:
HTML
<div class="app-slide-menu"
ng-class="{'app-menu-active': slideMenu}">
<form class="app-set-image-file-form">
<label class="app-file-input"
title="Upload Image">
<i class="icon-enter"></i>
<input type="file"
onchange="angular.element(this).scope().setImageFile(this)"
accept="image/*">
</label>
</form>
</div>
Now for the JS:
scope.setImageFile = function (element) {
var reader = new FileReader();
// Converts the image to a data URL.
reader.readAsDataURL(element.files[0]);
scope.slideMenuToggle();
/**
* When chosen image is selected it triggers the onload function to pass the image to the whiteboardService.
* #param event - Saves the event of the input field to get the images data URL.
*/
reader.onload = function (event) {
fabric.Image.fromURL(event.target.result, function (img) {
whiteboardService.uploadImageToCanvas(img);
});
// Resets the form that wraps round the file input to allow the
// user to add more than one of the same file.
// NOTE: This will break other inputs if you put them inside this form
$('.app-set-image-file-form').trigger('reset');
};
};
And the simple toggle JS:
scope.slideMenuToggle = function () {
scope.slideMenu = !scope.slideMenu;
};
Use ngChange. onChange is still the JavaScript onChange it was before using Angular. When you use onChange, Angular doesn't know things have changed.
This is one of the rare cases calling $scope.$apply() would be needed (when using onChange)
I think your best option would be to create an angular directive for a custom onchange, something like this:
app.directive('customOnChange', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var onChangeHandler = scope.$eval(attrs.customOnChange);
element.bind('change', onChangeHandler);
}
};
});
Then you can use it like this on your input:
<input type="file" custom-on-change="setImageFile" accept="image/*">
Finally in your controller, you can do this:
$scope.setImageFile = function (element) {
var reader = new FileReader();
// Converts the image to a data URL.
reader.readAsDataURL(element.files[0]);
scope.slideMenuToggle();
/**
* When chosen image is selected it triggers the onload function to pass the image to the whiteboardService.
* #param event - Saves the event of the input field to get the images data URL.
*/
reader.onload = function (event) {
fabric.Image.fromURL(event.target.result, function (img) {
whiteboardService.uploadImageToCanvas(img);
});
// Resets the form that wraps round the file input to allow the
// user to add more than one of the same file.
// NOTE: This will break other inputs if you put them inside this form
$('.app-set-image-file-form').trigger('reset');
};
};
Hope this can help.
Ok guys,
Sorry to say that your suggestions didn't help me but I managed to come to a solution that fixed my bug but I don't really understand why so please let me know if you can let me know.
I've basically added a class of 'js-input-file' to the input tag and then put this code below to trigger a scope reset.
var inputScope = angular.element('.js-input-file').scope();
inputScope.$apply();
This fixes my problem but I just can't get round my head why putting scope.$apply(); on its own doesn't fix the problem but the above does. Let me know your thoughts.
Related
So this was hard to search on, read on you'll see why.
This is my goal - detect if a form field has changed so i can enable the "Save" button. Ok, so this is super easy. Here's the rub. If the user then changes the input back to the ORIGINAL value when the form loaded I want to disable the save button again.
Quick example;
Page loads - input contains the value "red"
User changes it to "blue"
script detects change from original value, enable the save button
User then decides he liked "red" so changes the input back to "red"
script sees the change is same as original value, disable the save button
I know this may be a bit anal. But my clients don't get why the form still needs saved if the input is back to the beginning value.
Of course the whole thing then needs to work with many inputs.
I assume you have some onChange callback that you've registered on the `change' event for input fields. In that callback you'd want to be able to check if the new value has gone back to the original value. (If so, you'd want to disable the save button again). What is missing is some reference to the original value.
So when do you first know what the initial value is? I'm guessing you are, at some point, populating the input field with previously saved data. At that time, you had the information about what the "original" value was.
Now you want to have a specialized onChange callback for each of the input fields, where each of these callbacks would have access to the original value.
One way you could do that is using higher order functions. A function returning a new function. How would that work?
function createOnChangeCallback(originalValue) {
// returning a function!
return function(changeEvent) {
// this is the callback that will run for one
// of the input elements. Note that the "originalValue"
// is in scope
const newValue = changeEvent.target.value;
const hasGoneBackToOriginalValue = newValue == originalValue;
if(hasGoneBackToOriginalValue) {
// disable the submit button
}
}
};
Then, when you are populating a specific field, you would do something like
// get a reference to the input element
const $myInput = $('#myInput');
// give the input field it's first value ("populate")
$myInput.val(originalValue);
// create a specialized callback with access to the original value
$myInput.click( createOnChangeCallback(originalValue) )
A little more background. The app built using the .Net and MVC technology.
The primary view page where most action is happening has a table with a page
of job request records. Each record has an "Edit" link like this.
Edit
I use jquery to catch events at the document level because the table is loaded and rendered dynamically using the DataTables.net library.
Here's how I catch the "Click" event of the ".btnJobEdit" links (there will be one for each row of the jobs table).
And also the "Change" and "Submit" events of the "#frmJobEdit", this form does not exist until the above "Edit" link is clicked.
$(document)
.on('click', '.btnEditJob', event_click_btnEditJob)
.on('change', '#frmJobEdit', function () {
// .valid() is a jQuery validation library
$(this).find(':submit').attr('disabled', !$(this).valid());
$(this).find(':reset').attr('disabled', false);
})
.on('submit', '#frmJobEdit', event_submit_frmJobEdit)
Now we have the inital page loaded.
When a user clicks the "Edit" button for a record the function event_click_btnEditJob() runs.
Heres that simple function
function event_click_btnEditJob() {
event.preventDefault();
$.get($(this).attr('href'), function (response) {
// the response will be a full html form returned as a partial view
// by the _JobEdit method of the JobController
// I then set this into a BootStrap modal dialog and show the modal
$('#modal_JobEdit .modal-body')
.html(response)
.modal('show')
});
}
When the user clicks the "Save Changes" button the function event_submit_frmJobEdit handles the submit again thru ajax call to the JobController which again returns a partial view with the form and the newly changed data ready for another edit as well as now there is a message indication the success or fail of the submit action.
This existing process is, clean, stable and simple. Introducing this requirement of toggling the "Save" button based on user edits is undesirable from the development perspective. And at this time the customer representatives to this project have agreed. "Train the users!"
Try this:
$("#your-client-form").submit(function(e){
e.preventDefault(); //prevents for reloading the page.
});
Here is a full example of doing something similar to what you want to do.
// Retrieves the initial state of the form as a JSON object
// data-field-name determines the JSON field name
const getForm = () => {
return $("#my-form");
};
const getFormState = (form) => {
let originalFormState = {};
form.children(":input").each(function() {
const fieldName = $(this).data("fieldName");
const fieldValue = $(this).val();
if (fieldName) {
originalFormState[fieldName] = fieldValue;
}
});
return originalFormState;
};
const setSubmitButtonState = (form, isChanged) => {
form.children("#submit-form").prop("disabled", isChanged);
};
const checkAndHandleFormState = (form, originalFormState) => {
const currentFormState = getFormState(form);
let isChanged = false;
for (const field of Object.keys(originalFormState)) {
if (originalFormState[field] !== currentFormState[field]) {
isChanged = true;
break;
}
}
setSubmitButtonState(form, isChanged);
};
const addListenersToFormInputFields = (form, originalFormState) => {
form.children(":input").each(function() {
if ($(this).data("fieldName")) {
$(this).change(function() {
const fieldName = $(this).data("fieldName");
const fieldValue = $(this).val();
checkAndHandleFormState(getForm(), originalFormState);
});
}
});
};
$(document).ready(function() {
const form = getForm();
addListenersToFormInputFields(form, getFormState(form));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="my-form">
<input id="name" data-field-name="name" type="text" value="John" />
<input id="age" data-field-name="age" type="text" value="12" />
<input id="submit-form" type="submit" />
</form>
I'm trying to create a remove button to clear the value for a <input type="file">. The problem I have is that I don't know how can I make each button point to their respective <input>.
I'm using the JQuery plugin ezdz to upload and create previews of the images and I can easily add the buttons to each input with the $ezdz predefined var:
$ezdz.parent('div').find('div.arrow_box').html('<p>Quitar imagen</p>');
$ezdz.parent('div').find('div.arrow_box').removeClass('hide');
I need to create a function to reset the file input:
window.resetMethod =
function (e) {
$(this).parent('div').find('input').get(0).reset();
}
But the problem is in the button...
Quitar imagen
This is the html code:
<div class="first">
<!-- ezdz creates the following div and it'll be the container for the img -->
<div>Drop a file</div> <!-- This div is $ezdz -->
<input type="file" name="file[]" accept="image/jpeg" /><div class="arrow_box hide"></div>
</div>
Any idea to make this working?
EDIT: Sorry I didn't realized that function will reset the entire form. I need to reset the input above the button.
I'm getting the following error in console:
TypeError: $(...).parent(...).find(...).get(...) is undefined
this.form[...] in the onClickhandler will fail. I suppose that your console will display an error. At the moment of the call this corresponds to the anchor element (a), and that element has no form property.
So replace:
Quitar imagen
By:
Quitar imagen
The function resetMethod does not require you to pass the form object. As long as the element is placed in the intended form, you can pass it (as jQuery object) to the function, and it will find that form, and reset it.
There seems to be a problem too with the resetMethod function, because of the wrapping it does. I would suggest skipping that part, and just use an if, like this:
window.resetMethod = function (elem) {
var frm = elem.closest('form');
if (frm.length) {
frm.get(0).reset();
}
return false; // cancel default
}
Because of the return false, you can simplify the onclick part to:
Quitar imagen
You also do not need to use the window prefix.
Note that the function will also reset any other inputs you might have in the same form.
If you just want to reset the file-upload input, then use this variant of the function:
window.resetMethod = function (elem) {
frm = elem.closest('form');
console.log(frm);
if (frm.length) {
upload = frm.find('input[type=file]');
console.log(upload);
if (upload.length) {
// clear file-upload by
// re-injecting the html for it in the dom:
upload.replaceWith($("<p>").append(upload.clone()).html());
}
}
return false; // cancel default
}
I'm fairly new to Javascript, and am trying to get an 'on click enlarge' kind of effect, where clicking on the enlarged image reduces it again. The enlarging happens by replacing the thumbnail by the original image. I also want to get a slideshow using images from my database later on.
In order to do that, I made a test where I replace the id which indicates enlarging is possible by a class and I also use a global variable so that I can keep a track of the url I'm using. Not sure this is the best practice but I haven't found a better solution.
The first part works fine, my image gets changed no problem, values are also updated according to the 'alert' statement. However, the second part, the one with the class never triggers.
What am I doing wrong (apart from the very likely numerous bad practices) ?
If instead of changing the class I change the id directly (replacing .image_enlarged by #image_enlarged, etc.), it seems to call the first function, the one with the id, yet outputs the updated id, which is rather confusing.
var old_url = "";
$(function(){
$('#imageid').on('click', function ()
{
if($(this).attr('class')!='image_enlarged'){
old_url = $(this).attr('src');
var new_url = removeURLPart($(this).attr('src'));
$(this).attr('src',new_url); //image does enlarge
$(this).attr('class',"image_enlarged");
$(this).attr('id',"");
alert($(this).attr('class')); //returns updated class
}
});
$('.image_enlarged').on('click', function (){
alert(1); //never triggered
$(this).attr('src',old_url);
$(this).attr('class',"");
$(this).attr('id',"imageid");
});
});
function removeURLPart(e){
var tmp = e;
var tmp1 = tmp.replace('thumbnails/thumbnails_small/','');
var tmp2 = tmp1.replace('thumbnails/thumbnails_medium/','');
var tmp3 = tmp2.replace('thumbnails/thumbnails_large/','');
return tmp3;
}
As for the html, it's really simple :
<figure>
<img src = "http://localhost/Project/test/thumbnails/thumbnails_small/image.jpg" id="imageid" />
<figcaption>Test + Price thing</figcaption>
</figure>
<script>
document.write('<script src="js/jquery-1.11.1.min.js"><\/script>');
</script>
<script type="text/javascript" src="http://localhost/Project/js/onclickenlarge.js"></script>
From the API: http://api.jquery.com/on/
The .on() method attaches event handlers to the currently selected
set of elements in the jQuery object.
When you do $('.image_enlarged').on(...) there is no element with that class. Therefore, the function is not registered in any element.
If you want to do so, then you have to register the event after changing the class.
Here's an example based on your code: http://jsfiddle.net/8401mLf4/
But this registers the event multiple times (every time you click) and it would be wrong. So I would do something like:
$('#imageid').on('click', function () {
if (!$(this).hasClass('image_enlarged')) {
/* enlarge */
} else {
/* restore */
}
}
JSfiddle: http://jsfiddle.net/8401mLf4/2/
Try using:
addClass('image-enlarged')
instead of:
.attr('class',"image_enlarged");
the best way to do this would be to have a small-image class and a large image class that would contain the desired css for both and then use addClass() and removeClass depending on which you wanted to show.
I have a textbox in my page, what I want is to do some sort of "preview" using a modal, but i cannot display the value of textbox which I put the information i need. Can someone help me?
I use javascript in doing this, but my modal displays empty textbox.
$('#<%= txtDetails.ClientID %>').on('change', function () {
$('input[id$="txtKBDecription"]').text($(this).val());
});
$('#<%= txtIssue.ClientID %>').on('keyup', function () {
$('input[id$="txtKBSummary"]').text($(this).val());
});
$('#<%= area.ClientID %>').on('change', function () {
$('input[id$="txtKBResolution"]').text($(this).val());
});
Really need more specifics, but essentially you're going to grab the value form one and put it in the other whenver it changes.
this goes in your preview modal
<input type="text" id="preview" onchange="Copy();">
and this one goes in your final modal
<input type="text" id="final">
and code...
<script>
function Copy()
{
document.getElementById("final").value = document.getElementById("preview").value;
}
</script>
though it should really be something closer to
<script>
function Copy()
{
var previewValue = document.getElementById("preview").value;
if(previewValue != "" /* Or Other Validation */)
document.getElementById("final").value = previewValue;
}
</script>
you should also consider checking to make sure the elements exist if you are planning on having other people edit the page and/or to make it more robust.
how can i alert the user if there are any changes inside the object field
i''m trying to detect the changes on this div inside the object
if it's normal the code would be this:
<div id="HeaderNewMessageIcon" class="BrosixContactNewMessage" style="display:none;">
</div>
but if there are changes it will look like this:
<div id="HeaderNewMessageIcon" class="BrosixContactNewMessage" style="display: block; ">
</div>
i want to alert the user if there are changes inside the object, either via alert or using an image.
is there any way for me to achieve this?
and another thing, i have no access to the code inside the object, i can only view it but not edit it.
I believe there must be some JavaScript code which changing your html you can call your method from there. Other way you can use setInterval.
You can use jQuery plugin Mutation Events plugin for jQuery . see thread
var a = document.getElementsByClassName('HeaderNewMessageIcon')[0];
var oldTitle = a.title;
setInterval(function(){
if(a.title !== oldTitle){
alert("Title change");
oldTitle = a.title;
}
},100);
jsfiddle
You have to detect the changes when throught user interaction such as click, mouseover, mousedown, etc... then you can attach a function to see if its attributes or anything inside it changes.
//detect inputs change
$('#HeaderNewMessageIcon').find(':input').change(function(){ alert(...)});
//detect attributes change
$('#HeaderNewMessageIcon').click(function(){
detectChange(this);
});
As for detectChange to work, you must save the attributes when page just loaded
var attrs = $('#HeaderNewMessageIcon').get(0).attributes;
function detectChange(obj){
//pseudo-code, you need to find a function on the web to commpare 2 objetcs
if (obj.attributes === attrs){
alert(...);
}
//the same comparison for the children elements $(obj).children()
}