Sorting Dynamic Data with Isotope - javascript

I am trying to use Isotope.js to sort data by type. There seem to be a few ways to do this however they all require that you know the sort variables before hand.
One of the best examples of what I'm talking about is found in this question.
In the example they are trying to sort by class for example group all elements with class .milk like so:
milk: function( $elem ) {
var isMilk = $elem.hasClass('milk');
return (!isMilk?' ':'');
},
A jsfiddle is provided here: http://jsfiddle.net/Yvk9q/9/
My problem:
I am pulling the categories (classes or data-type) from a user generated database. For this reason I cannot simply add all the sorting variables to the code before hand.
I played with the fiddle and got a semi working sort here: http://jsfiddle.net/BandonRandon/erfXH/1/ by using data-category instead of class. However,this just sorts all data alphabetically not by actual category.
Some possible solutions:
Use JSON to return an array of all categories and then use this to loop through classes
Use inline javascript and run a PHP loop inside a <script> tag
Write an external PHP file with a javascript header
What I'm looking for
The simplest best approach here, being if it's one of the solutions above or something different. This doesn't seem like it should need to be this complicated. So I may be over complicating this.
EDIT:
I now have a json array of my data but I can't figure out how to pass the data into the isotope settings when i try something like this
var $container = $('.sort-container');
var opts = {
itemSelector: '.member-item',
layoutMode: 'straightDown',
getSortData : {
$.getJSON( 'member-cat-json.php', function(data) {
$.each(data, function(i, item) {
var slug = data[i].slug;
slug : function( $elem ) {
var is+slug = $elem.hasClass(slug);
return (!is+slug?' ':'');
}
}
});
});
}
}
var $container = $('.sort-container');
$container.isotope(opts);
It fails because I can't use a loop inside of the plugin settings. Not sure what can be done about this though.
EDIT 2:
I found this question which seems about what I'm trying to do but unfortunately the most recent jsfiddle fails with isotope
Here is a sample of my JSON output:
{term_id:9, name:Milk, slug:milk, term_group:0, term_taxonomy_id:17...}
{term_id:9, name:Eggs, slug:eggs, term_group:0, term_taxonomy_id:17...}
I am using the slug as the class name and in my loop.

I'm not sure I entirely understand your question, but I'll state my assumptions and work from there:
You have data in a format as described above:
{term_id:9, name:Milk, slug:milk, term_group:0, term_taxonomy_id:17...}
You want to sort on the slug names, even though we do not know what the slugs will be named ahead of time.
Assuming these two things, the fiddle you've linked to is close, but has a problem due to closures which I have fixed.
As expected, your situation is similar to the one listed, except that you need to obtain the JSON data first, as you have.
var $container = $('.sort-container'),
createSortFunction = function(slug) {
return function($elem) {
return $elem.hasClass(slug) ? ' ' : '';
};
},
getSortData = function(data) {
var sortMethods = {};
for (var index in data) {
var slug = data[index].slug;
// immediately create the function to avoid
// closure problems
sortMethods[slug] = createSortFunction(slug);
}
return sortMethods;
}
$.getJSON('member-cat-json.php', function (data) {
// I'm wrapping the isotop creation inside the `getJSON`
// call, just to ensure that we have `data`
$container.isotope({
itemSelector: '.member-item',
layoutMode: 'straightDown',
getSortData: getSortData(data);
});
});

Related

Parse JSON data, and then pass all its values to another function

I'm building this website: http://collections.design
The way it works is by reading all tools data from a JSON, using jQuery (I don't know much javascript). Then, you can click on an item and a side panels opens with further information. But there's a lot of repeated code, so I'm trying to optimise it a bit.
First I parse the JSON:
// The data source
var data_source = "../data/tools/tools.json";
// Parsing the JSON
$.getJSON(data_source, function(data) {
$.each(data, function(key,val) {
// And I'm storing all of its values in variables, to make them easier to read:
var name = val.availability.name;
var linux = val.os.linux;
// Then I'm using all that to render each item on screen
…
});
});
Each of the items has a button that calls another function to create and open the side panel. The side panel reuses that item's data from the JSON. This function to create the side panel is using the name variable as parameter, but then inside is parsing the JSON again to get the rest of the values it needs.
My question is:
How can I "encapsulate" all variables when I do the JSON parsing, then pass it as a parameter to the other function; and finally, individually read each of those values in the other function?
I tried working with arrays. But didn't manage it to work, also keeping in mind that I'm trying to simplify things, not repeat myself, and keep short names…
Maybe I'm asking too much, but any pointers or links to doc will be appreciated.
I see two ways of doing this.
1) Save the JSON data outside the scope so you can reuse it and pass the index of the data you want.
Something like this
// The data source
var data_source = "../data/tools/tools.json";
var all_data;
// Parsing the JSON
$.getJSON(data_source, function(data) {
all_data = data;
$.each(data, function(key,val) {
$('.button').on('click', function() { callToOtherFunction(key) })
});
});
function callToOtherFunction(key) {
console.log(all_data[key]);
}
2) As Sam Axe said, pass the data directly to the function
// The data source
var data_source = "../data/tools/tools.json";
// Parsing the JSON
$.getJSON(data_source, function(data) {
$.each(data, function(key,val) {
$('.button').on('click', function() { callToOtherFunction(key) })
});
});
function callToOtherFunction(val) {
console.log(val);
}
Here's a working fiddle.
The data is already "encapsulated" in the data object. Pass that object to the function that you want to use the data in.
You could always construct a new object - but what's the point - it's already in the data object.

Appending new results to an open jQuery autocomplete menu

I have an app in which I have multiple search sources. Previously, the users had to choose in what source to search in before searching. If they did not choose, the app would default to one of the options.
However, now they want to search in all the sources at the same time. This is fine enough, but the problem is that when one of the searches returns, it overwrites the previous search result. Pretty much expected behavior. What I basically want is to append the new results to the already open autocomplete menu, instead of overwriting the old results. Naturally, the autocomplete menu would have to empty when it closes.
I guess that this is possible to do, but what approach is the best? I could just have an array I guess, which I append results to and then overwrite _renderMenu to use this array instead of the items one that is passed to the function. Then empty said array at the close event.
Is this the best way to go though? Or is there a more elegant solution?
Some code:
Ok, so searchAction is called by jquery autocomplete eventually. In collection.search I do the ajax call, here the URL is created based in the this parameter, then respondWhithData is called and maps the search result to a proper format (ie value and label for the autocomplete menu). After reponse is called from respondWithData, jquery automagically renders the resultsmenu. Thus, I probably have to overwrite the reponse event function as well as the _renderMenu and possibly _renderItem, yes?
searchAction: function(searchTerm, collection, response){
var self = this;
$.when(collection.search(searchTerm, this)).then(function(data) {
self.respondWithData(data, response);
});
},
respondWithData : function(data, response) {
if (data.length > 0) {
var responseVal = _.map(data, this.mapData);
this.checkResponseCount(responseVal);
response(responseVal);
}
else {
response(this.emptyResult());
}
},
To be clear, the problem is not the multiple search itself, but rendering the asynchronos results. I want to render the first results that come back, and then appends the rest as soon as they are returned from the server.
Edit 2:
Just tried to edit ui.content in the autocompleteresponse event, but any edit does not take once it renders for some reason...
Edit 3: Ah, ui.content can only be modified directly, not changed. If I push every single change instead of concating two arrays ui.content shows what I want.
It works I guess, but its not perfect.
I can figure how looks your scenario but I'm guessing:
You should have like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").html(data)
}
});
}
etc
Instead of overwritting the #myResultsDiv you need to Append the results like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").append(data)
}
});
}
Edit: You can also do something like this:
var resultsArray = [];
var searchDone = 0;
var totalSearchs = 5; //assuming 5 searches
function search1() {
function search1() {
$.ajax({ ...
success: function(data) {
//APPEND data to resultsArray
searchDone++;
if(searchDone==totalSearch) //syncronize the 5 searchs before render
renderSearchs(resultsArray);
}
});
}

jquery ui - call methods with same name on different widgets

Suppose I have created several widgets (mywidget1, mywidget2, ...) and that all have a method with the same name (doSomething).
To invoke the method I can use:
$("#elem").widget1("doSomething");
but this way I need to know the name of the widget (in the example widget1).
If I have an array with multiple instances of the various widgets, how can I invoke on each one the method "doSomething" without knowing the name of the widget?
You can't. Two options for you:
Store them in separate arrays (one array for widget1, another for widget2), or
Store objects in the arrays containing the information about which widget relates to that entry
Here's an example of #2
var list = [
{widget: "widget1", element: $("#elem")},
{widget: "widget2", element: $(".some-selector")},
{widget: "widget2", element: $("#another")},
{widget: "widget1", element: $("div .target")}
];
$.each(list, function(i, entry) {
entry.element[entry.widget]("doSomething");
});
In theory I suppose you could do something like the following:
var widgets = [widget1, widget2, widget3]; // etc, will assume they are defined already
// Access each Widget, now widget points to each of the widgets
// So we dont need to know the actual name like widget1, widget2 etc
widgets.forEach(function (widget) {
$('#elem').widget('doSomething'); // Call the doSomething method of this widget
$('#elem').widget.call(null, 'doSomething'); // Try this if the above fails
$('#elem').call(widget, 'doSomething'); // Or maybe this :)
}
Anyway try the above, of the top of my head im not sure which will work. I think what you are trying to do might be a bit difficult to implement, so sorry if it doesnt work. Hopefully it will :)
you can have an object of functions and get the key value dynamically to use the particular function
widgets = {
widget1 : function(value){return value;},
widget2 : function(value){return value+1},
widget3 : function(value){return value+2}
}; //you can have your list of functions say widgets1,2,3....
for(var k in widgets ){
console.log(widgets[k](1));
} //to get the function name you can use the key name in the object
Example on https://gist.github.com/vishnu667/44063d64f2d1210c26c9
you can also call the function if you know the key
widgets['widget2'](10); //to directly call the function if you know the key
to Dynamically add more widgets use
function add_widget(name,widgetFunction){ //function to add widget
widgets[name]=widgetFunction;
}
add_widget("widget4",function(value){return value+10;}); //adds a new widget to the widgets list
I found this solution:
before:
var tmp = $("#elem").widget1();
myarray[i] = tmp;
now:
var tmp = $("#elem").widget1();
myarray[i] = tmp.data(ui-widget1);
in this way it is possible to directly call the method:
myarray[x].doSomething();
what do you think?
can be an efficient solution?
thanks to all

Filter json data in controller, the right way

(there are other questions but neither helped me out)
Hi, I would like to know if this is the right way to filter the results I get from service where I'm displaying only one result (like a detail).
I know I could use ng-repeat and filter it in the view and that is the cleanest, but I want to have more control over because I will re-use some of the data in the controller for other operations.
Right now I'm doing this:
$scope.savedEvents = Event.getPayedEvents(); //gets a list from service
//Goes through entire list and checks for a match
angular.forEach($scope.savedEvents, function(event) {
if (event.IdEvent == $stateParams.eventId) {
$scope.showEvent = event;
}
});
//now if there is a match I can use $scope.showEvent.eventName etc
Not sure if this would be easier using $filter to return just one event that has correct IdEvent. Or if someone has better solution, please let me know.
thanks
I don't see any problems with what you have, but you could inject the $filter service and do this one liner:
$scope.showEvent = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId });
EDIT: Here is an easy way to resolve the result to a single value from the returned array:
var showEvents = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId });
$scope.showEvent = showEvents && showEvents.length ? showEvents[0] : null;
In CoffeeScript it is a little more concise:
$scope.showEvent = $filter('filter')($scope.savedEvents, { IdEvent: $stateParams.eventId })?[0]

Advice needed... Javascript OOP/namespacing

right now i am at a point where i feel that i need to improve my javascript skills because i already see that what i want to realize will get quite complex. I've iterrated over the same fragment of code now 4 times and i am still not sure if it's the best way.
The task:
A user of a webpage can add different forms to a webpage which i call modules. Each form provides different user inputs and needs to be handled differently. Forms/Modules of the same type can be added to the list of forms as the user likes.
My current solution:
To make the code more readable and seperate functions i use namespaced objects. The first object holds general tasks and refers to the individual forms via a map which holds several arrays where each contains the id of a form and the reference to the object which holds all the functions which need to be performed especially for that kind of form.
The structure looks more or less similar to this:
var module_handler = {
_map : [], /* Map {reference_to_obj, id} */
init: function(){
var module = example_module; /* Predefined for this example */
this.create(module);
},
create: function(module) {
//Store reference to obj id in map
this._map.push([module,id = this.createID()]);
module.create(id);
},
createID: function(id) {
//Recursive function to find an available id
},
remove: function(id) {
//Remove from map
var idx = this._map.indexOf(id);
if(idx!=-1) this._map.splice(idx, 1);
//Remove from DOM
$('#'+id+'').remove();
}
}
var example_module = {
create: function(id) {
//Insert html
$('#'+id+' > .module_edit_inner').replaceWith("<some html>");
}
}
Now comes my question ;-)
Is the idea with the map needed?
I mean: Isn't there something more elegant like:
var moduleXYZ = new example_module(id)
which copies the object and refers only to that form.... Something more logical and making speed improvements?? The main issue is that right now i need to traverse the DOM each time if i call for example "example_module.create() or later on any other function. With this structure i cant refer to the form like with something like "this"???
Do you see any improvements at this point??? This would help me very much!!! Really i am just scared to go the wrong way now looking at all the stuff i will put on top of this ;-)
Thank You!
I think you're looking for prototype:
=========
function exampleModule(id)
{
this.id = id;
}
exampleModule.prototype.create = function()
{
}
=========
var module1 = new exampleModule(123);
module1.create();
var module2 = new exampleModule(456);
module2.create();

Categories