Renaming formelements in a particular range with jquery - javascript

I've multiple autogenerated forms on a page. They are named in a particular manner like:
form-0-weight, form-1-weight, form-2-weight etc.
<ul>
<li>
<input id="id_form-0-weight" type="text" name="form-0-weight">
<a class="deleteIngredient" href="">x</a>
</li>
<li>
....more forms
</li>
</ul>
The user can add and delete forms. If a form get's deleted, the remaining ones should be renamed to stay in order. e.g. "form-1-weight" gets deleted >> "form-2-weight" will be renamed to "form-1-weight".
The total number of forms is stored in a hidden field named TOTAL_FORMS.
I'm trying to achieve this with a simple for loop.
The problem is that all the forms after the deleted one get the same name.
e.g. If I delete form-2-weight, all the following forms get the name form-2-weight instead of 2, 3, 4 etc.
$(".deleteIngredient").click(function(e){
e.preventDefault();
var delete = $(this).closest('li');
name = delete.children('input').attr("name");
count = name.replace(prefix,'');
count = name.replace("-weight",'');
var formCount = parseInt($("#TOTAL_FORMS").val())-1;
delete.remove();
for (var i = parseInt(count); i<=formCount; i++){
var newName = "form-"+i+"-weight";
$("#id_form-"+(i+1)+"-weight").attr("name",newName);
}
});
I suppose it has something to do with how I select the elements inside the loop because when I use just the variable "i" instead of "newName" it works as expected.

The problem is you're not initializing i properly.
This happens because "count" doesn't contain a string that can be parsed into an integer under the conditions of parseInt, I suggest you look here:
w3Schools/parseInt
Note: If the first character cannot be converted to a number, parseInt() returns NaN.
When you assign a string to "count" you're actually inserting the string "form-i" into the variable.
What you should do is this:
count = name.replace(prefix,'');
count = count.replace("-weight",'');
You should also rename your "delete" variable to "form" or any other descriptive name, as delete is a reserved word in javascript and also an action so it doesn't really suit as a name for an object.
Don't forget to change the id attribute of the item so it'll fit the new name.
As a note, you should probably consider following through some tutorial on Javascript or jQuery, Tuts+ learn jQuery in 30 days is one i'd recommend.

My first impulse is just to solve this a different way.
Live Demo
var $ul = $('ul');
// Add a new ingredient to the end of the list
function addIngredient() {
var $currentIngredients = $ul.children();
var n = $currentIngredients.length;
var $ingredient = $('<li>', {
html: '<input type="text" /> x'
});
$ul.append($ingredient);
renameIngredientElements();
}
// Rename all ingredients according to their order
function renameIngredientElements() {
$ul.children().each(function (i, ingredient) {
var $ingredient = $(ingredient);
var $input = $ingredient.find('input');
var name = 'form-' + i + '-weight';
$input
.attr('id', 'id_' + name)
.attr('name', name);
});
}
// Delete an ingredient
function deleteIngredient($ingredient) {
$ingredient.remove();
renameIngredientElements();
}
// Bind events
$('.add-ingredient').on('click', addIngredient);
$ul.on('click', '.delete-ingredient', function (event) {
var $ingredient = $(event.currentTarget).closest('li');
deleteIngredient($ingredient);
event.preventDefault();
});
As to why your particular code is breaking, it looks like user2421955 just beat me to it.

Related

What am I missing in the jQuery .each() function?

I have this function that I am trying to figure out/fix and can't seem to pinpoint the issue / can't figure out a way to get it working.
Basically my CMS is spitting certain hrefs that I would like to:
Part 1) change the targeted href URL
Part 2) change the button's text
Right now I only have 2 instances of this type of button, so here's what is printing out in my console:
Part 1) for this part I get the correct urls without the characters i want to strip out.
Part 2) two instances of the button's text (See All) followed by the correct variable of btnParent for the first button and then the second button and finally one instance of "Products".
My issue is, I can't figure out how to:
Part 1) send back the stripped URL to its respective button's href as an each function.
Part 2) Have the each() function print out the new text as "See All + BLAH + Products" for each instance, and then append the new text to the respective button.
Here is the code:
function viewMoreBtn() {
var btnMain = $("li:contains('See All')");
var btnText = $("li:contains('See All')").text();
var btnParent = $("li:contains('See All')").parent('ul').prev('li').text();
// PART 1 - STRIP LINK URL OF -_-// CHARACTERS
$.each(btnMain, function(i, v) {
v = $(this).find('a').attr('href').replace('-_-//', '');
console.log(v);
});
// PART 2 - ADD LABEL TO HTML TEXT OF BTN
$.each(btnMain, function(index, value) {
value = (btnText + btnParent + 'Products');
$(btnMain).text(value);
console.log(value);
});
}
viewMoreBtn();
Thank you.
jQuery objects, as return by $(...) have a each method already on them. The element is passed as the this context. You could use that further with jQuery to act on the objects in an scoped context. Basically, you have the right code, just in the wrong scope.
Part 1
btnMain.each(function() {
var $li = $(this);
var $a = $li.find('a');
var desiredUrl = $a.attr('href').replace('-_-//', '');
$a.attr('href', desiredUrl);
});
Part 2
btnMain.each(function() {
var $li = $(this);
var btnText = $li.text();
varbtnParent = $li.parent('ul').prev('li').text();
value = (btnText + btnParent + 'Products');
console.log(value);
$li.find('a').text(value);
});
See #Zequ's answer for the iteration over the each() function in the returned btnMain.
This is how $.each( obj, function( key, value ) works: you iterate over btnMain, and for each iteration of $.each(), the function assigns the index of the iteration to i and the value of btnMain at that index to v.
$.each(btnMain, function(i, v) {
//v = $(this).find('a').attr('href').replace('-_-//', '');
console.log(i); // I am the index of $.each() iterator
console.log(v); // I am the node from the btnMain array
// I don't know if this is right without seeing your HTML, but it seems like what you want
v.find('a').attr('href').replace('-_-//', '');
});
The second $.each() follows the same pattern.
If I understood correctly, you're confusing your variables.
$.each is a function for each element of the array/object being passed. It gives you a index and the element, check the reference
In part 1, you're defining v as the string you want, you're not changing the element at all,you need something like this:
$.each(btnMain, function() {
// you're saying you got the correct URLs, so the only thing you need to do is to change the element afterwards
var element = $(this).find('a');
v = element.attr('href').replace('-_-//', '');
element.attr('href', v);
});`
Also you could use btnMain.each instead of $.each
In part 2, you are changing the value variable (it's actually the element you're iterating over), to the string you want, then you follow it by trying to change btnMain's text. This is wrong, from what I understood, btnMain is an array of two elements you can't change it's text. You should change the element's value (that you are calling value). It would be something like that
$.each(btnMain, function(index, element){
// I think this is the time you want to define the btnParent, relative to the element
var btnParent = element.parent('ul').prev('li').text();
var value = (btnText + btnParent + 'Products');
element.text(value);
}
I THINK this is what you need.
Also you could append both parts into one, since both are iterating over btnMain

Get only word part of an id/name

I have a div element with lots of descendent's elements, all with ids in the form "word1", for a simple example: id="moviment1" or id="type1".
I need to get only the written part of these ids (moviment or type), in order to concatenate their names with a increment of 1 (id="moviment2" or id="type2").
$clone.find('*').each(function() {
var id = $(this).prop('id');
var num = parseInt( $(this).prop("id").match(/\d+/g), 10 ) +1;
$(this).prop('id', id+num);
});
The way it is, I always get ids like id="moviment12". I already tried:
var id = $(this).prop('id').replace(/\d+/g, '');
or
var id = $(this).prop('id').match(/\w+/);
But I always get errors like "cannot read property 'replace'/'match' of undefined". So, what am I doing wrong? Any other ideas? Thank you very much!
Ideally you should use a template. Traversing and modifying parsed elements makes your code slow and hard to maintain.
If you want to increment the number part of the IDs by 1 you can use the replace method callback function:
$clone.find('[id]').prop('id', function(_, id) {
// Assuming `id` is `test_25_segment`
return id.replace(/(\d+)/, function(num) {
// |
// --- is 25
return +num + 1;
// |
// --- parses the matching string into integer
});
});
Here is a demo using the above snippet.
Easiest way, you could just add those values as data-attr:
<div id="type1" data-id="1" data-text="type"></div>
So you can easily get them separated just using .data('id') and .data('text').
You may select the elements by this way:
var all = [].filter.call(document.querySelectorAll('[id*=type]'), function(el) {
return (/\btype\d+\b/).test(el.id);
});
and then you can change the ids using methods like replace()
Try this...
var onlyAlphabets = id.split(/(\d)/)[0];

JavaScript Asp.net repeating controls

I am trying to do the folowing with Asp.net 3.5/IIS
A web form with a top level repeatable form. So basically a Order->Products->ProductsParts kinda of scenerio. Order is only one. Product is repeatable. Each product has repeatable products parts. The product and product part have a whole bunch of fields so I cannot use a grid.
So, I have add/remove buttons for Product and within each product add/remove buttons for each product part.
That is my requirement. I have been able to achieve add/remove after some research using jquery/js. How, do i capture this data on the server? Since javascript is adding and removing these controls they are not server side and I don't know how to assign name attributes correctly. I am trying following javascript but it ain't working:
function onAddProperty(btnObject){
var previous = btnObject.prev('div');
var propertyCount = jquery.data(document.body, 'propertyCount');
var newDiv = previous.clone(true).find("*[name]").andSelf().each(function () { $(this).attr("name").replace(($(this).attr("name").match(/\[[0-9]+\]/), cntr)); }); ;
propertyCount++;
jquery.data(document.body, 'propertyCount', propertyCount);
//keep only one unit and remove rest
var children = newDiv.find('#pnlUnits > #pnlUnitRepeater');
var unitCount = children.length;
var first = children.first();
for (i = 1; i < unitCount; i++) {
children[i].remove();
}
newDiv.id = "pnlPropertySlider_" + propertyCount;
newDiv.insertBefore(btnObject);
}
I need to assign name property as array so that I can read it in Request.Form
Fix for not updating ids not working:
var newDiv = previous.clone(true).find("input,select").each(function () {
$(this).attr({
'name': function () {
var name = $(this).attr('name');
if (!name) return '';
return name.replace(/property\[[0-9]+\]/, 'property' + propertyCount);
}
});
}).end().insertBefore(btnObject);
The issue looks like the following line:
$(this).attr("name").replace(($(this).attr("name").match(/\[[0-9]+\]/), cntr));
This statement doesn't do anything. Strings in JavaScript an immutable, and .replace only returns the string with something replaced.
You would then have to actually set the attr("name") to the new string that has the replaced value:
http://api.jquery.com/attr/
I can't help much more without seeing your HTML.

jQuery list of data

I have a series of editable lists which, on a press of a button should be transformed into some sort of data structure. When it has been turned into some sort of data I need to add duplicates together.
Example:
200g banana
100g apple
200g apple
Should be turned into a data list of some sort and should in the end look like this:
200g banana
300g apple
Here's my attempt:
//button click event
$(".calculate").bind("click", function(e)
{
//get the correct parent of the button
var parent = $(this).closest("#calc");
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
});
});
Basically I click the button and the above script finds all the relevant data.
How can I turn this into a multidimensional array or a list of objects I can search for duplicates in?
When I try to make a dynamic object it seems to fail and when I make a multidimensional array to search in I get blocked by inArray's inability to search through them.
Problem recap:
I am able to get the user data no problem. Turning it into a list and adding together duplicates is the problem.
I will suggest you to have a global object that will contain the summary, this will look like this:
$(".calculate").bind("click", function(e)
{
var fruits = {};
//get the correct parent of the button
var parent = $(this).closest("#calc");
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
// here is my code
if(fruits[opt2] == undefined) {
fruits[opt2] = opt1;
} else {
// assuming that opt1 is an integer
fruits[opt2] += opt1;
}
});
// use fruits variable here
});
Here's another variant, which also does some simple parsing in case you have 100g as input, versus 100. Also, the data structure gets reinitialized every time, so everything does not get doubled on every click.
$(".calculate").bind("click", function(e)
{
//get the correct parent of the button
var parent = $(this).closest("#calc");
var ingredients = {};
var extractWeight = function (input) {
// you can add other logic here
// to parse stuff like "1kg" or "3mg"
// this assumes that everything is
// in grams and returns just the numeric
// value
return parseInt(input.substring(0, input.length - 1));
}
//get relevant data
parent.find(".options").each(function(index, element)
{
var opt1 = $(this).children(".opt1").children("input").val(); //weight
var opt2 = $(this).children(".opt2").children("input").val(); //ingredient
// initialize to 0 if not already initialized
ingredients[opt2] = ingredients[opt2] ? ingredients[opt2] : 0;
ingredients[opt2] += extractWeight(opt1);
});
});​
Here are some tips:
{} is called an object literal and is used to create a new empty object
object members can be accessed dynamically through the [] notation (i.e. if x === "name" then o[x] === o.name)
variables are visible inside functions that are at the same level or deeper in the scope - like in my example I use ingredients in the each function.
arrays in JavaScript only support numeric keys, so you won't have stuff like PHP's "associative arrays". Objects fill this gap in JS.
Here is a jsFiddle that does what you're looking for :) http://jsfiddle.net/LD9TY/
It has two inputs, one for the item name and the other for the amount. When you click add, it checks an object to see if the item was already added. If so, it increments the amount for that item based on your input. If not, it adds that item with the amount you specified. It then goes and builds a ul with all the items in your "store".
Note that this is a quick and dirty example, so there is no type checking or validation going on :)

use variable part of ID with js

I have a list of 8 divs: #video1, #video2, ... with each the same javascript actions to run when clicked, but with other id's (for #video1: show #image1, #preview1, ...).
Instead of writing 8 times the same code but with other id's, can I do this more efficient? Is it possible to take the sixth caracter (the number) from each #videoX as a variable when clicked, and use
this in the code?
Inside your event handler, you can extract the number, e.g. with a regular expression [MDN]:
var id = element.id.match(/\d+$/)[0];
and then use it to create the IDs of the other elements:
var image_id = "image" + id,
preview_id = "preview" + id;
Another option would be to assign data- attributes to the elements and use them to store the numerical part of the ID.
Use a class name instead. This way it's independent of the IDs completely.
<div class="videoClick" id="...">...</div>
JS:
$('.videoClick').click(function() {
...
})
yes you can:
$("div[id*='video']").click(function() {
var numid = $(this).attr("id").replace("video", "");
alert(numid);
//...use your numid value
});
Check attribute contains selector.
Try this
var ids = [#video1, #video2, #video3, #video4, #video5, #video6, #video7, #video8];
$(ids.join(",")).click(function(){
var imageId = this.id.replace("video", "image");
var previewId = this.id.replace("video", "preview");
$("#"+imageId).show();
$("#"+previewId).show();
});

Categories

Resources