Array not updating when using JQuery - javascript

I'm trying to splice an array in order to delete an object from my array. I'm using angular-formly to display the forms and AngularJs and JQuery in order to handle the data.
The JQuery
$(document).on("click", ".delete-me", function () {
var id = $(this).parent().find('input, select, textarea').attr('id');
var splitid = id.split("_")[3];
angular.element(document.getElementById('View2Ctrl')).scope().removeField(splitid);
$(this).closest('.formly-field').remove();
});
The reason for the split and is that formly wraps an id like formly_1_input_textField-1_0.
The Angular Function
$scope.removeField = function (id) {
for(var i=0;i<$scope.formFields.length;i++){
if($scope.formFields[i].key == id){
$scope.formFields.splice(id, 1);
console.log($scope.formFields.length);
$scope.currentId = i;
}
}
};
The console log is displaying the actual length of the array however I have {{formFields.length}} on the template and it does not update, as well as {{formFields}} still showing the data in the array. I suspect that JQuery updating the DOM isn't being watched by Angular and have tried calling $scope.$apply() manually to no avail.

You have a couple of logic errors in your function:
When you remove an item from the array, you increase i anyway, which means you'll miss out the next item if it's a match.
You're using id instead of i in the splice
That second one is probably the culprit, but once you'd fixed that, the first would have bitten you (unless the id values are unique, as the name implies; in which case, you should probably terminate the loop when you find it).
Updates:
$scope.removeField = function (id) {
var i=0;
while (i<$scope.formFields.length){ // Use a while loop
if($scope.formFields[i].key == id){
$scope.formFields.splice(i, 1); // Use i, not id
console.log($scope.formFields.length);
$scope.currentId = i;
// DON'T increment i here, you've removed the element
// Or if IDs are unique, just terminate the loop
} else {
++i;
}
}
};

Related

breaking out of an underscore each

I am trying to find a model within a collection with an attribute equal to html select option value.
<div id="hospital-details">
<select name="hospitalnames">
<option><%- model.get('name') %></option>
</select>
</div>
whenever hospital name is changed, jquery change callback is triggered to find locationModel with selected option value as attribute value as shown below,
$('select[name="hospitalnames"]').change(function() {
var name = $(this).val();
locationListCollection.each(function(locationModel) {
if ($.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
return false; // control is returned to underscore.min.js
}
});
});
console.log(that.locationModel); // this is not being displayed at all
After the locationModel with an attribute is found, I am unable to come out the loop. Any help ? At this moment I have looked into
this but without success.
You're using the wrong method if you're searching for the first match. Collections have lots of Underscore methods mixed in, in particular they have find mixed in:
find _.find(list, iterator, [context])
Looks through each value in the list, returning the first one that passes a truth test (iterator), or undefined if no value passes the test.
Something like this:
var name = $.trim($(this).val());
that.locationModel = locationListCollection.find(function(locationModel) {
return $.trim(locationModel.get('name')) == name;
});
and if the names in your model are pre-trimmed and nice and clean, then you could use findWhere:
findWhere collection.findWhere(attributes)
Just like where, but directly returns only the first model in the collection that matches the passed attributes.
like this:
var name = $.trim($(this).val());
that.locationModel = locationListCollection.findWhere({ name: name });
BTW, this:
console.log(locationModel);
won't give you anything because locationModel and that.locationModel are different things.
You can always go oldschool.
$('select[name="hospitalnames"]').change(function() {
var name = $(this).val();
for (var i = 0; i < locationListCollection.length; ++i) {
var locationModel = locationListCollection.models[i];
if ($.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
break;
}
}
});
Try this,
var name = $(this).val();
var flag=true;
locationListCollection.each(function(locationModel) {
if (flag && $.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
flag=false;
//return false;// to break the $.each loop
}
});
The short is no.
If you take a look at underscore's source you'll see that they use a breaker object to quickly stop a .each() but that is only available internally.
I would not recommend this but you could always modify the source to expose this breaker object (see baseline setup in the annotated source
http://underscorejs.org/docs/underscore.html). Then you would just return this object instead of returning false. But you would probably need to remove the native forEach call to keep the behaviour consistent. So it's not worth it!
_.each(function(arr) {
if(condition) {
return _.breaker; // Assuming you changed the source.
}
});
Since you are searching for a single item instead of .each() use:
var locationModel = _.find(arr, function(item) {
return $.trim(locationModel.get('name')) == $.trim(name);
));

Build a switch based on array

I want to create a Javascript switch based on an array I'm creating from a query string. I'm not sure how to proceed.
Let's say I have an array like this :
var myArray = ("#general","#controlpanel","#database");
I want to create this...
switch(target){
case "#general":
$("#general").show();
$("#controlpanel, #database").hide();
break;
case "#controlpanel":
$("#controlpanel").show();
$("#general, #database").hide();
break;
case "#database":
$("#database").show();
$("#general, #controlpanel").hide();
break;
}
myArray could contain any amount of elements so I want the switch to be created dynamically based on length of the array. The default case would always be the first option.
The array is created from a location.href with a regex to extract only what I need.
Thanks alot!
#Michael has the correct general answer, but here's a far simpler way to accomplish the same goal:
// Once, at startup
var $items = $("#general,#controlpanel,#database");
// When it's time to show a target
$items.hide(); // Hide 'em all, even the one to show
$(target).show(); // OK, now show just that one
If you really only have an array of selectors then you can create a jQuery collection of them via:
var items = ["#general","#controlpanel","#database"];
var $items = $(items.join(','));
Oh, and "Thanks, Alot!" :)
I think you want an object. Just define keys with the names of your elements to match, and functions as the values. e.g.
var switchObj = {
"#general": function () {
$("#general").show();
$("#controlpanel, #database").hide();
},
"#controlpanel": function () {
$("#controlpanel").show();
$("#general, #database").hide();
},
"#database": function () {
$("#database").show();
$("#general, #controlpanel").hide();
}
}
Then you can just call the one you want with
switchObj[target]();
Granted: this solution is better if you need to do explicitly different things with each element, and unlike the other answers it focused on what the explicit subject of the question was, rather than what the OP was trying to accomplish with said data structure.
Rather than a switch, you need two statements: first, to show the selected target, and second to hide all others.
// Array as a jQuery object instead of a regular array of strings
var myArray = $("#general,#controlpanel,#database");
$(target).show();
// Loop over jQuery list and unless the id of the current
// list node matches the value of target, hide it.
myArray.each(function() {
// Test if the current node's doesn't matche #target
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
});
In fact, the first statement can be incorporated into the loop.
var myArray = $("#general,#controlpanel,#database");
myArray.each(function() {
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
else {
$(this).show();
}
});
Perhaps you're looking for something like this? Populate myArray with the elements you're using.
var myArray = ["#general","#controlpanel","#database"];
var clone = myArray.slice(0); // Clone the array
var test;
if ((test = clone.indexOf(target)) !== -1) {
$(target).show();
clone.splice(test,1); // Remove the one we've picked up
$(clone.join(',')).hide(); // Hide the remaining array elements
}
here you dont need to explicitly list all the cases, just let the array define them. make sure though, that target exists in the array, otherwise you'll need an if statement.
var target = "#controlpanel";
var items = ["#general","#controlpanel","#database"];
items.splice($.inArray(target, items), 1);
$(target).show();
$(items.join(",")).hide();
items.push(target);

select an array of elements and use them

Using this syntax:
var position = array($('#ipadmenu > section').attr('data-order'));
I cannot get my code to work. I have never used arrays before so im kind of lost on how to use them. (especially in jquery).
How would I make an array of all section elements and associate the value of data-order to that list. Example:
first section - data-order:1
second section - data-order:2
etc and then use that info afterwards.
Thank you!
Since .attr just gets one attribute -- the first one found by the jQuery selector -- you need to build your array element by element. One way to do that is .each (you can also use .data to extract data attributes):
var position = new Array;
$('#ipadmenu > section').each(function() {
position.push($(this).data('order'));
});
alert(position[0]); // alerts "1"
This will be an indexed array, not an associative array. To build one of those (which in JavaScript is technically an object, not any kind of array) just change the inner part of your .each loop:
var position = {};
$('#ipadmenu > section').each(function(i) {
position["section"+i] = $(this).data('order');
});
The resulting object position can now be accessed like:
alert(position['section1']); // alerts "1"
A different approach involves using jQuery.map, but since that only works on arrays, not jQuery objects, you need to use jQuery.makeArray to convert your selection into a true array first:
var position = $.map($.makeArray($('#ipadmenu > section')), function() {
return $(this).data('order');
} ); // position is now an indexed array
This approach is technically shorter than using .each, but I find it less clear.
Javascript:
var orders = [];
$('#ipadmenu > section').each(function() {
orders.push($(this).data('order'))
});
HTML:
<div id="ipadmenu">
<section data-order="1">1</section>
<section data-order="2">2</section>
</div>
You will want to do something like this:
// Get the elements and put them in an array
var position = $('#ipadmenu section').toArray();
console.log(position);
// Loop through the array
for (var i = 0; i < position.length; i++){
// Display the attribute value for each one
console.log("Section " + i + ": " + $(position[i]).attr('data-order'));
}
Working example here: http://jsfiddle.net/U6n8E/3/

check dynamic array against static array

I need some javascript/jquery for this. I have two arrays, one static array that is hard coded and another dynamic array that is user driven. For example the values in each array represent a div. Each div is represented in the static array. I want to be able to show only the divs that are present in the dynamic array. So if the dynamic array changes, it shows the divs present in the dynamic array and hides the divs not present. I also want to be able to run a function connected to each div, ie box1 has a function that needs called if it is displayed.
var static_list = new Array("box1","box2","box3");
var dynamic_list = new Array("box1","box3");
I'm assuming that dynamic_list is a subset of static_list.
$.each(static_list, function(index, value) {
$('#' + value).hide();
});
$.each(dynamic_list, function(index, value) {
$('#' + value).show();
});
$.each() iterates through a collection calling the callback function for each element. The first $.each() hides all elements with IDs found in the static_list. The second $.each() shows all elements with IDs found in the dynamic_list. Running both in that order hides all div elements in static_list except for those found in the dynamic_list.
You could use a simple comparator function:
// Return true if value is in array
function isIn(value, array) {
var i = array.length;
while (i--) {
if (array[i] == value {
return true;
}
}
return false;
}
then you can see if a value is in an array using:
if (isIn(value, array)) {
// do something with/to value
}
You can achieve your result by using $.each(), $.inArray(), and .toggle()
$.each(static_list, function(index, value){
var showHide = $.inArray(value, dynamic_list) >= 0;
$("#" + this).toggle(showHide);
});
Code example on jsfiddle.

Sorting Divs in jQuery by Custom Sort Order

I'm trying to re-sort the child elements of the tag input by comparing
their category attribute to the category order in the Javascript
variable category_sort_order. Then I need to remove divs whose category attribute
does not appear in category_sort_order.
The expected result should be:
any
product1
product2
download
The code:
<div id="input">
<div category="download">download</div>
<div category="video">video1</div>
<div category="video">video2</div>
<div category="product">product1</div>
<div category="any">any</div>
<div category="product">product2</div>
</div>
<script type="text/javascript">
var category_sort_order = ['any', 'product', 'download'];
</script>
I really don't even know where to begin with this task but if you could please provide any assistance whatsoever I would be extremely grateful.
I wrote a jQuery plugin to do this kind of thing that can be easily adapted for your use case.
The original plugin is here
Here's a revamp for you question
(function($) {
$.fn.reOrder = function(array) {
return this.each(function() {
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('div[category="' + array[i] + '"]');
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
and use like so
var category_sort_order = ['any', 'product', 'download'];
$('#input').reOrder(category_sort_order);
This happens to get the right order for the products this time as product1 appears before product2 in the original list, but it could be changed easily to sort categories first before putting into the array and appending to the DOM. Also, if using this for a number of elements, it could be improved by appending all elements in the array in one go instead of iterating over the array and appending one at a time. This would probably be a good case for DocumentFragments.
Just note,
Since there is jQuery 1.3.2 sorting is simple without any plugin like:
$('#input div').sort(CustomSort).appendTo('#input');
function CustomSort( a ,b ){
//your custom sort function returning -1 or 1
//where a , b are $('#input div') elements
}
This will sort all div that are childs of element with id="input" .
Here is how to do it. I used this SO question as a reference.
I tested this code and it works properly for your example:
$(document).ready(function() {
var categories = new Array();
var content = new Array();
//Get Divs
$('#input > [category]').each(function(i) {
//Add to local array
categories[i] = $(this).attr('category');
content[i] = $(this).html();
});
$('#input').empty();
//Sort Divs
var category_sort_order = ['any', 'product', 'download'];
for(i = 0; i < category_sort_order.length; i++) {
//Grab all divs in this category and add them back to the form
for(j = 0; j < categories.length; j++) {
if(categories[j] == category_sort_order[i]) {
$('#input').append('<div category="' +
category_sort_order[i] + '">'
+ content[j] + '</div>');
}
};
}
});
How it works
First of all, this code requires the JQuery library. If you're not currently using it, I highly recommend it.
The code starts by getting all the child divs of the input div that contain a category attribute. Then it saves their html content and their category to two separate arrays (but in the same location.
Next it clears out all the divs under the input div.
Finally, it goes through your categories in the order you specify in the array and appends the matching child divs in the correct order.
The For loop section
#eyelidlessness does a good job of explaining for loops, but I'll also take a whack at it. in the context of this code.
The first line:
for(i = 0; i < category_sort_order.length; i++) {
Means that the code which follows (everything within the curly brackets { code }) will be repeated a number of times. Though the format looks archaic (and sorta is) it says:
Create a number variable called i and set it equal to zero
If that variable is less than the number of items in the category_sort_order array, then do whats in the brackets
When the brackets finish, add one to the variable i (i++ means add one)
Then it repeats step two and three until i is finally bigger than the number of categories in that array.
A.K.A whatever is in the brackets will be run once for every category.
Moving on... for each category, another loop is called. This one:
for(j = 0; j < categories.length; j++) {
loops through all of the categories of the divs that we just deleted from the screen.
Within this loop, the if statement checks if any of the divs from the screen match the current category. If so, they are appending, if not the loop continues searching till it goes through every div.
Appending (or prepending) the DOM nodes again will actually sort them in the order you want.
Using jQuery, you just have to select them in the order you want and append (or prepend) them to their container again.
$(['any', 'product', 'video'])
.map(function(index, category)
{
return $('[category='+category+']');
})
.prependTo('#input');
Sorry, missed that you wanted to remove nodes not in your category list. Here is the corrected version:
// Create a jQuery from our array of category names,
// it won't be usable in the DOM but still some
// jQuery methods can be used
var divs = $(['any', 'product', 'video'])
// Replace each category name in our array by the
// actual DOM nodes selected using the attribute selector
// syntax of jQuery.
.map(function(index, category)
{
// Here we need to do .get() to return an array of DOM nodes
return $('[category='+category+']').get();
});
// Remove everything in #input and replace them by our DOM nodes.
$('#input').empty().append(divs);
// The trick here is that DOM nodes are selected
// in the order we want them in the end.
// So when we append them again to the document,
// they will be appended in the order we want.
I thought this was a really interesting problem, here is an easy, but not incredibly performant sorting solution that I came up with.
You can view the test page on jsbin here: http://jsbin.com/ocuta
function compare(x, y, context){
if($.inArray(x, context) > $.inArray(y, context)) return 1;
}
function dom_sort(selector, order_list) {
$items = $(selector);
var dirty = false;
for(var i = 0; i < ($items.length - 1); i++) {
if (compare($items.eq(i).attr('category'), $items.eq(i+1).attr('category'), order_list)) {
dirty = true;
$items.eq(i).before($items.eq(i+1).remove());
}
}
if (dirty) setTimeout(function(){ dom_sort(selector, order_list); }, 0);
};
dom_sort('#input div[category]', category_sort_order);
Note that the setTimeout might not be necessary, but it just feels safer. Your call.
You could probably clean up some performance by storing a reference to the parent and just getting children each time, instead of re-running the selector. I was going for simplicity though. You have to call the selector each time, because the order changes in a sort, and I'm not storing a reference to the parent anywhere.
It's seems fairly direct to use the sort method for this one:
var category_sort_order = ['any', 'product', 'download'];
// select your categories
$('#input > div')
// filter the selection down to wanted items
.filter(function(){
// get the categories index in the sort order list ("weight")
var w = $.inArray( $(this).attr('category'), category_sort_order );
// in the sort order list?
if ( w > -1 ) {
// this item should be sorted, we'll store it's sorting index, and keep it
$( this ).data( 'sortindex', w );
return true;
}
else {
// remove the item from the DOM and the selection
$( this ).remove();
return false;
}
})
// sort the remainder of the items
.sort(function(a, b){
// use the previously defined values to compare who goes first
return $( a ).data( 'sortindex' ) -
$( b ).data( 'sortindex' );
})
// reappend the selection into it's parent node to "apply" it
.appendTo( '#input' );
If you happen to be using an old version of jQuery (1.2) that doesn't have the sort method, you can add it with this:
jQuery.fn.sort = Array.prototype.sort;

Categories

Resources